From 933517a3d9de89e7060dfe336f1d56adad5aaad8 Mon Sep 17 00:00:00 2001 From: samikshya-chand_data Date: Tue, 21 Apr 2026 11:48:24 +0530 Subject: [PATCH 1/2] fix: address review feedback on telemetry infrastructure (#325 follow-up) - Route DatabricksTelemetryExporter and FeatureFlagCache HTTP through shared connection stack (connectionProvider.getRetryPolicy().invokeWithRetry) matching CloudFetchResultHandler pattern; remove bespoke retry loops - Fix HALF_OPEN concurrent-probe race in CircuitBreaker with atomic tryAdmit check-and-set - Fix MetricsAggregator.close() flush race: async close with closing flag suppresses intermediate batch flushes; single awaited flush drains remaining metrics - Simplify telemetryUtils to minimal SSRF-safe URL builder; remove unused helpers - Clean up types, exports, and test coverage across telemetry modules Co-authored-by: samikshya-chand_data --- coverage/lcov-report/base.css | 224 + coverage/lcov-report/block-navigation.js | 87 + coverage/lcov-report/favicon.png | Bin 0 -> 445 bytes coverage/lcov-report/index.html | 296 + coverage/lcov-report/lib/DBSQLClient.ts.html | 1189 ++++ coverage/lcov-report/lib/DBSQLLogger.ts.html | 190 + .../lcov-report/lib/DBSQLOperation.ts.html | 1537 +++++ .../lcov-report/lib/DBSQLParameter.ts.html | 388 ++ coverage/lcov-report/lib/DBSQLSession.ts.html | 1972 +++++++ .../DatabricksOAuth/AuthorizationCode.ts.html | 658 +++ .../auth/DatabricksOAuth/OAuthManager.ts.html | 1036 ++++ .../DatabricksOAuth/OAuthPersistence.ts.html | 142 + .../auth/DatabricksOAuth/OAuthScope.ts.html | 118 + .../auth/DatabricksOAuth/OAuthToken.ts.html | 226 + .../auth/DatabricksOAuth/index.html | 191 + .../auth/DatabricksOAuth/index.ts.html | 250 + .../auth/PlainHttpAuthentication.ts.html | 187 + .../lib/connection/auth/index.html | 116 + .../tokenProvider/CachedTokenProvider.ts.html | 379 ++ .../ExternalTokenProvider.ts.html | 241 + .../tokenProvider/FederationProvider.ts.html | 889 +++ .../tokenProvider/StaticTokenProvider.ts.html | 214 + .../auth/tokenProvider/Token.ts.html | 556 ++ .../TokenProviderAuthenticator.ts.html | 250 + .../connection/auth/tokenProvider/index.html | 221 + .../auth/tokenProvider/index.ts.html | 109 + .../auth/tokenProvider/utils.ts.html | 322 + .../connections/HttpConnection.ts.html | 460 ++ .../connections/HttpRetryPolicy.ts.html | 391 ++ .../connections/NullRetryPolicy.ts.html | 124 + .../connections/ThriftHttpConnection.ts.html | 766 +++ .../lib/connection/connections/index.html | 161 + .../lib/contracts/IDBSQLLogger.ts.html | 130 + coverage/lcov-report/lib/contracts/index.html | 116 + .../lcov-report/lib/dto/InfoValue.ts.html | 184 + coverage/lcov-report/lib/dto/Status.ts.html | 214 + coverage/lcov-report/lib/dto/index.html | 131 + .../lib/errors/AuthenticationError.ts.html | 94 + .../lib/errors/HiveDriverError.ts.html | 88 + .../lib/errors/OperationStateError.ts.html | 178 + .../lib/errors/ParameterError.ts.html | 88 + .../lcov-report/lib/errors/RetryError.ts.html | 148 + .../lib/errors/StagingError.ts.html | 88 + .../lib/errors/StatusError.ts.html | 148 + coverage/lcov-report/lib/errors/index.html | 206 + .../lib/hive/Commands/BaseCommand.ts.html | 295 + .../CancelDelegationTokenCommand.ts.html | 124 + .../Commands/CancelOperationCommand.ts.html | 124 + .../Commands/CloseOperationCommand.ts.html | 124 + .../hive/Commands/CloseSessionCommand.ts.html | 124 + .../Commands/ExecuteStatementCommand.ts.html | 124 + .../hive/Commands/FetchResultsCommand.ts.html | 133 + .../hive/Commands/GetCatalogsCommand.ts.html | 124 + .../hive/Commands/GetColumnsCommand.ts.html | 124 + .../Commands/GetCrossReferenceCommand.ts.html | 124 + .../GetDelegationTokenCommand.ts.html | 124 + .../hive/Commands/GetFunctionsCommand.ts.html | 124 + .../lib/hive/Commands/GetInfoCommand.ts.html | 124 + .../GetOperationStatusCommand.ts.html | 124 + .../Commands/GetPrimaryKeysCommand.ts.html | 124 + .../GetResultSetMetadataCommand.ts.html | 124 + .../hive/Commands/GetSchemasCommand.ts.html | 124 + .../Commands/GetTableTypesCommand.ts.html | 124 + .../hive/Commands/GetTablesCommand.ts.html | 124 + .../hive/Commands/GetTypeInfoCommand.ts.html | 124 + .../hive/Commands/OpenSessionCommand.ts.html | 151 + .../RenewDelegationTokenCommand.ts.html | 124 + .../lcov-report/lib/hive/Commands/index.html | 431 ++ .../lcov-report/lib/hive/HiveDriver.ts.html | 637 ++ coverage/lcov-report/lib/hive/index.html | 116 + coverage/lcov-report/lib/index.html | 206 + coverage/lcov-report/lib/polyfills.ts.html | 241 + .../lib/result/ArrowResultConverter.ts.html | 754 +++ .../lib/result/ArrowResultHandler.ts.html | 301 + .../result/CloudFetchResultHandler.ts.html | 466 ++ .../lib/result/JsonResultHandler.ts.html | 328 ++ .../lib/result/ResultSlicer.ts.html | 313 + .../lib/result/RowSetProvider.ts.html | 412 ++ coverage/lcov-report/lib/result/index.html | 206 + coverage/lcov-report/lib/result/utils.ts.html | 574 ++ .../lib/telemetry/CircuitBreaker.ts.html | 793 +++ .../DatabricksTelemetryExporter.ts.html | 1171 ++++ .../lib/telemetry/ExceptionClassifier.ts.html | 388 ++ .../lib/telemetry/FeatureFlagCache.ts.html | 688 +++ .../lib/telemetry/MetricsAggregator.ts.html | 1261 ++++ .../telemetry/TelemetryEventEmitter.ts.html | 655 +++ coverage/lcov-report/lib/telemetry/index.html | 206 + .../lcov-report/lib/telemetry/types.ts.html | 916 +++ .../lib/utils/CloseableCollection.ts.html | 181 + .../lib/utils/OperationIterator.ts.html | 340 ++ .../lib/utils/buildUserAgentString.ts.html | 178 + .../lib/utils/definedOrError.ts.html | 103 + .../lib/utils/formatProgress.ts.html | 166 + coverage/lcov-report/lib/utils/index.html | 221 + coverage/lcov-report/lib/utils/index.ts.html | 106 + coverage/lcov-report/lib/utils/lz4.ts.html | 211 + .../lib/utils/protocolVersion.ts.html | 370 ++ coverage/lcov-report/lib/version.ts.html | 88 + coverage/lcov-report/prettify.css | 1 + coverage/lcov-report/prettify.js | 2 + coverage/lcov-report/sort-arrow-sprite.png | Bin 0 -> 138 bytes coverage/lcov-report/sorter.js | 196 + coverage/lcov.info | 5240 +++++++++++++++++ lib/DBSQLClient.ts | 26 +- lib/contracts/IClientContext.ts | 14 +- lib/index.ts | 5 - lib/telemetry/CircuitBreaker.ts | 152 +- lib/telemetry/DatabricksTelemetryExporter.ts | 303 +- lib/telemetry/ExceptionClassifier.ts | 10 +- lib/telemetry/FeatureFlagCache.ts | 152 +- lib/telemetry/MetricsAggregator.ts | 265 +- lib/telemetry/TelemetryEventEmitter.ts | 2 - lib/telemetry/telemetryUtils.ts | 214 +- lib/telemetry/types.ts | 43 +- tests/unit/.stubs/CircuitBreakerStub.ts | 4 +- tests/unit/.stubs/ClientContextStub.ts | 5 + tests/unit/telemetry/CircuitBreaker.test.ts | 61 - .../DatabricksTelemetryExporter.test.ts | 309 +- .../telemetry/ExceptionClassifier.test.ts | 20 +- .../unit/telemetry/MetricsAggregator.test.ts | 41 - tests/unit/telemetry/telemetryUtils.test.ts | 276 - 121 files changed, 37566 insertions(+), 1440 deletions(-) create mode 100644 coverage/lcov-report/base.css create mode 100644 coverage/lcov-report/block-navigation.js create mode 100644 coverage/lcov-report/favicon.png create mode 100644 coverage/lcov-report/index.html create mode 100644 coverage/lcov-report/lib/DBSQLClient.ts.html create mode 100644 coverage/lcov-report/lib/DBSQLLogger.ts.html create mode 100644 coverage/lcov-report/lib/DBSQLOperation.ts.html create mode 100644 coverage/lcov-report/lib/DBSQLParameter.ts.html create mode 100644 coverage/lcov-report/lib/DBSQLSession.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html create mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/index.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/index.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html create mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html create mode 100644 coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html create mode 100644 coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html create mode 100644 coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html create mode 100644 coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html create mode 100644 coverage/lcov-report/lib/connection/connections/index.html create mode 100644 coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html create mode 100644 coverage/lcov-report/lib/contracts/index.html create mode 100644 coverage/lcov-report/lib/dto/InfoValue.ts.html create mode 100644 coverage/lcov-report/lib/dto/Status.ts.html create mode 100644 coverage/lcov-report/lib/dto/index.html create mode 100644 coverage/lcov-report/lib/errors/AuthenticationError.ts.html create mode 100644 coverage/lcov-report/lib/errors/HiveDriverError.ts.html create mode 100644 coverage/lcov-report/lib/errors/OperationStateError.ts.html create mode 100644 coverage/lcov-report/lib/errors/ParameterError.ts.html create mode 100644 coverage/lcov-report/lib/errors/RetryError.ts.html create mode 100644 coverage/lcov-report/lib/errors/StagingError.ts.html create mode 100644 coverage/lcov-report/lib/errors/StatusError.ts.html create mode 100644 coverage/lcov-report/lib/errors/index.html create mode 100644 coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html create mode 100644 coverage/lcov-report/lib/hive/Commands/index.html create mode 100644 coverage/lcov-report/lib/hive/HiveDriver.ts.html create mode 100644 coverage/lcov-report/lib/hive/index.html create mode 100644 coverage/lcov-report/lib/index.html create mode 100644 coverage/lcov-report/lib/polyfills.ts.html create mode 100644 coverage/lcov-report/lib/result/ArrowResultConverter.ts.html create mode 100644 coverage/lcov-report/lib/result/ArrowResultHandler.ts.html create mode 100644 coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html create mode 100644 coverage/lcov-report/lib/result/JsonResultHandler.ts.html create mode 100644 coverage/lcov-report/lib/result/ResultSlicer.ts.html create mode 100644 coverage/lcov-report/lib/result/RowSetProvider.ts.html create mode 100644 coverage/lcov-report/lib/result/index.html create mode 100644 coverage/lcov-report/lib/result/utils.ts.html create mode 100644 coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html create mode 100644 coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html create mode 100644 coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html create mode 100644 coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html create mode 100644 coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html create mode 100644 coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html create mode 100644 coverage/lcov-report/lib/telemetry/index.html create mode 100644 coverage/lcov-report/lib/telemetry/types.ts.html create mode 100644 coverage/lcov-report/lib/utils/CloseableCollection.ts.html create mode 100644 coverage/lcov-report/lib/utils/OperationIterator.ts.html create mode 100644 coverage/lcov-report/lib/utils/buildUserAgentString.ts.html create mode 100644 coverage/lcov-report/lib/utils/definedOrError.ts.html create mode 100644 coverage/lcov-report/lib/utils/formatProgress.ts.html create mode 100644 coverage/lcov-report/lib/utils/index.html create mode 100644 coverage/lcov-report/lib/utils/index.ts.html create mode 100644 coverage/lcov-report/lib/utils/lz4.ts.html create mode 100644 coverage/lcov-report/lib/utils/protocolVersion.ts.html create mode 100644 coverage/lcov-report/lib/version.ts.html create mode 100644 coverage/lcov-report/prettify.css create mode 100644 coverage/lcov-report/prettify.js create mode 100644 coverage/lcov-report/sort-arrow-sprite.png create mode 100644 coverage/lcov-report/sorter.js create mode 100644 coverage/lcov.info delete mode 100644 tests/unit/telemetry/telemetryUtils.test.ts diff --git a/coverage/lcov-report/base.css b/coverage/lcov-report/base.css new file mode 100644 index 00000000..f418035b --- /dev/null +++ b/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/coverage/lcov-report/block-navigation.js b/coverage/lcov-report/block-navigation.js new file mode 100644 index 00000000..cc121302 --- /dev/null +++ b/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/lcov-report/favicon.png b/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1525b811a167671e9de1fa78aab9f5c0b61cef7 GIT binary patch literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 29.27% + Statements + 661/2258 +
+ + +
+ 12.04% + Branches + 170/1411 +
+ + +
+ 12.66% + Functions + 56/442 +
+ + +
+ 28.88% + Lines + 640/2216 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
lib +
+
14.44%78/5400.5%2/3972.43%2/8214.44%78/540
lib/connection/auth +
+
16.66%1/60%0/200%0/216.66%1/6
lib/connection/auth/DatabricksOAuth +
+
16.01%37/2313.73%4/1073.44%2/5816.44%37/225
lib/connection/auth/tokenProvider +
+
19.9%41/2060%0/1440%0/5016.32%32/196
lib/connection/connections +
+
11.84%18/1520%0/970%0/4212.08%18/149
lib/contracts +
+
100%5/5100%2/2100%1/1100%5/5
lib/dto +
+
13.79%4/290%0/210%0/913.79%4/29
lib/errors +
+
64.51%20/3122.22%4/1840%2/564.51%20/31
lib/hive +
+
25.58%22/86100%0/00%0/2225.58%22/86
lib/hive/Commands +
+
51.93%67/1290%0/220%0/2651.93%67/129
lib/result +
+
10.49%34/3240.64%2/3081.81%1/5510.69%34/318
lib/telemetry +
+
75.93%303/39968.72%156/22785.71%48/5676.09%296/389
lib/utils +
+
25.83%31/1200%0/480%0/3423%26/113
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLClient.ts.html b/coverage/lcov-report/lib/DBSQLClient.ts.html new file mode 100644 index 00000000..03b93290 --- /dev/null +++ b/coverage/lcov-report/lib/DBSQLClient.ts.html @@ -0,0 +1,1189 @@ + + + + + + Code coverage report for lib/DBSQLClient.ts + + + + + + + + + +
+
+

All files / lib DBSQLClient.ts

+
+ +
+ 17.92% + Statements + 19/106 +
+ + +
+ 0% + Branches + 0/54 +
+ + +
+ 4.54% + Functions + 1/22 +
+ + +
+ 17.92% + Lines + 19/106 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +3691x +1x +  +1x +  +1x +1x +  +  +  +  +1x +1x +  +  +1x +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +55x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import thrift from 'thrift';
+import Int64 from 'node-int64';
+ 
+import { EventEmitter } from 'events';
+import { HeadersInit } from 'node-fetch';
+import TCLIService from '../thrift/TCLIService';
+import { TProtocolVersion } from '../thrift/TCLIService_types';
+import IDBSQLClient, { ClientOptions, ConnectionOptions, OpenSessionRequest } from './contracts/IDBSQLClient';
+import IDriver from './contracts/IDriver';
+import IClientContext, { ClientConfig } from './contracts/IClientContext';
+import IThriftClient from './contracts/IThriftClient';
+import HiveDriver from './hive/HiveDriver';
+import DBSQLSession from './DBSQLSession';
+import IDBSQLSession from './contracts/IDBSQLSession';
+import IAuthentication from './connection/contracts/IAuthentication';
+import HttpConnection from './connection/connections/HttpConnection';
+import IConnectionOptions from './connection/contracts/IConnectionOptions';
+import Status from './dto/Status';
+import HiveDriverError from './errors/HiveDriverError';
+import { buildUserAgentString, definedOrError } from './utils';
+import PlainHttpAuthentication from './connection/auth/PlainHttpAuthentication';
+import DatabricksOAuth, { OAuthFlow } from './connection/auth/DatabricksOAuth';
+import {
+  TokenProviderAuthenticator,
+  StaticTokenProvider,
+  ExternalTokenProvider,
+  CachedTokenProvider,
+  FederationProvider,
+  ITokenProvider,
+} from './connection/auth/tokenProvider';
+import IDBSQLLogger, { LogLevel } from './contracts/IDBSQLLogger';
+import DBSQLLogger from './DBSQLLogger';
+import CloseableCollection from './utils/CloseableCollection';
+import IConnectionProvider from './connection/contracts/IConnectionProvider';
+ 
+function prependSlash(str: string): string {
+  if (str.length > 0 && str.charAt(0) !== '/') {
+    return `/${str}`;
+  }
+  return str;
+}
+ 
+function getInitialNamespaceOptions(catalogName?: string, schemaName?: string) {
+  if (!catalogName && !schemaName) {
+    return {};
+  }
+ 
+  return {
+    initialNamespace: {
+      catalogName,
+      schemaName,
+    },
+  };
+}
+ 
+export type ThriftLibrary = Pick<typeof thrift, 'createClient'>;
+ 
+export default class DBSQLClient extends EventEmitter implements IDBSQLClient, IClientContext {
+  private static defaultLogger?: IDBSQLLogger;
+ 
+  private readonly config: ClientConfig;
+ 
+  private connectionProvider?: IConnectionProvider;
+ 
+  private authProvider?: IAuthentication;
+ 
+  private client?: IThriftClient;
+ 
+  private readonly driver = new HiveDriver({
+    context: this,
+  });
+ 
+  private readonly logger: IDBSQLLogger;
+ 
+  private thrift: ThriftLibrary = thrift;
+ 
+  private readonly sessions = new CloseableCollection<DBSQLSession>();
+ 
+  private static getDefaultLogger(): IDBSQLLogger {
+    if (!this.defaultLogger) {
+      this.defaultLogger = new DBSQLLogger();
+    }
+    return this.defaultLogger;
+  }
+ 
+  private static getDefaultConfig(): ClientConfig {
+    return {
+      directResultsDefaultMaxRows: 100000,
+      fetchChunkDefaultMaxRows: 100000,
+ 
+      arrowEnabled: true,
+      useArrowNativeTypes: true,
+      socketTimeout: 15 * 60 * 1000, // 15 minutes
+ 
+      retryMaxAttempts: 5,
+      retriesTimeout: 15 * 60 * 1000, // 15 minutes
+      retryDelayMin: 1 * 1000, // 1 second
+      retryDelayMax: 60 * 1000, // 60 seconds (1 minute)
+ 
+      useCloudFetch: true, // enabling cloud fetch by default.
+      cloudFetchConcurrentDownloads: 10,
+      cloudFetchSpeedThresholdMBps: 0.1,
+ 
+      useLZ4Compression: true,
+    };
+  }
+ 
+  constructor(options?: ClientOptions) {
+    super();
+    this.config = DBSQLClient.getDefaultConfig();
+    this.logger = options?.logger ?? DBSQLClient.getDefaultLogger();
+    this.logger.log(LogLevel.info, 'Created DBSQLClient');
+  }
+ 
+  private getConnectionOptions(options: ConnectionOptions): IConnectionOptions {
+    return {
+      host: options.host,
+      port: options.port || 443,
+      path: prependSlash(options.path),
+      https: true,
+      socketTimeout: options.socketTimeout,
+      proxy: options.proxy,
+      headers: {
+        'User-Agent': buildUserAgentString(options.userAgentEntry),
+      },
+    };
+  }
+ 
+  private createAuthProvider(options: ConnectionOptions, authProvider?: IAuthentication): IAuthentication {
+    if (authProvider) {
+      return authProvider;
+    }
+ 
+    switch (options.authType) {
+      case undefined:
+      case 'access-token':
+        return new PlainHttpAuthentication({
+          username: 'token',
+          password: options.token,
+          context: this,
+        });
+      case 'databricks-oauth':
+        return new DatabricksOAuth({
+          flow: options.oauthClientSecret === undefined ? OAuthFlow.U2M : OAuthFlow.M2M,
+          host: options.host,
+          persistence: options.persistence,
+          azureTenantId: options.azureTenantId,
+          clientId: options.oauthClientId,
+          clientSecret: options.oauthClientSecret,
+          useDatabricksOAuthInAzure: options.useDatabricksOAuthInAzure,
+          context: this,
+        });
+      case 'custom':
+        return options.provider;
+      case 'token-provider':
+        return new TokenProviderAuthenticator(
+          this.wrapTokenProvider(
+            options.tokenProvider,
+            options.host,
+            options.enableTokenFederation,
+            options.federationClientId,
+          ),
+          this,
+        );
+      case 'external-token':
+        return new TokenProviderAuthenticator(
+          this.wrapTokenProvider(
+            new ExternalTokenProvider(options.getToken),
+            options.host,
+            options.enableTokenFederation,
+            options.federationClientId,
+          ),
+          this,
+        );
+      case 'static-token':
+        return new TokenProviderAuthenticator(
+          this.wrapTokenProvider(
+            StaticTokenProvider.fromJWT(options.staticToken),
+            options.host,
+            options.enableTokenFederation,
+            options.federationClientId,
+          ),
+          this,
+        );
+      // no default
+    }
+  }
+ 
+  /**
+   * Wraps a token provider with caching and optional federation.
+   * Caching is always enabled by default. Federation is opt-in.
+   */
+  private wrapTokenProvider(
+    provider: ITokenProvider,
+    host: string,
+    enableFederation?: boolean,
+    federationClientId?: string,
+  ): ITokenProvider {
+    // Always wrap with caching first
+    let wrapped: ITokenProvider = new CachedTokenProvider(provider);
+ 
+    // Optionally wrap with federation
+    if (enableFederation) {
+      wrapped = new FederationProvider(wrapped, host, {
+        clientId: federationClientId,
+      });
+    }
+ 
+    return wrapped;
+  }
+ 
+  private createConnectionProvider(options: ConnectionOptions): IConnectionProvider {
+    return new HttpConnection(this.getConnectionOptions(options), this);
+  }
+ 
+  /**
+   * Connects DBSQLClient to endpoint
+   * @public
+   * @param options - host, path, and token are required
+   * @param authProvider - [DEPRECATED - use `authType: 'custom'] Optional custom authentication provider
+   * @returns Session object that can be used to execute statements
+   * @example
+   * const session = client.connect({host, path, token});
+   */
+  public async connect(options: ConnectionOptions, authProvider?: IAuthentication): Promise<IDBSQLClient> {
+    const deprecatedClientId = (options as any).clientId;
+    if (deprecatedClientId !== undefined) {
+      this.logger.log(
+        LogLevel.warn,
+        'Warning: The "clientId" option is deprecated. Please use "userAgentEntry" instead.',
+      );
+      if (!options.userAgentEntry) {
+        options.userAgentEntry = deprecatedClientId;
+      }
+    }
+ 
+    // Store enableMetricViewMetadata configuration
+    if (options.enableMetricViewMetadata !== undefined) {
+      this.config.enableMetricViewMetadata = options.enableMetricViewMetadata;
+    }
+ 
+    this.authProvider = this.createAuthProvider(options, authProvider);
+ 
+    this.connectionProvider = this.createConnectionProvider(options);
+ 
+    const thriftConnection = await this.connectionProvider.getThriftConnection();
+ 
+    thriftConnection.on('error', (error: Error) => {
+      // Error.stack already contains error type and message, so log stack if available,
+      // otherwise fall back to just error type + message
+      this.logger.log(LogLevel.error, error.stack || `${error.name}: ${error.message}`);
+      try {
+        this.emit('error', error);
+      } catch (e) {
+        // EventEmitter will throw unhandled error when emitting 'error' event.
+        // Since we already logged it few lines above, just suppress this behaviour
+      }
+    });
+ 
+    thriftConnection.on('reconnecting', (params: { delay: number; attempt: number }) => {
+      this.logger.log(LogLevel.debug, `Reconnecting, params: ${JSON.stringify(params)}`);
+      this.emit('reconnecting', params);
+    });
+ 
+    thriftConnection.on('close', () => {
+      this.logger.log(LogLevel.debug, 'Closing connection.');
+      this.emit('close');
+    });
+ 
+    thriftConnection.on('timeout', () => {
+      this.logger.log(LogLevel.debug, 'Connection timed out.');
+      this.emit('timeout');
+    });
+ 
+    return this;
+  }
+ 
+  /**
+   * Starts new session
+   * @public
+   * @param request - Can be instantiated with initialSchema, empty by default
+   * @returns Session object that can be used to execute statements
+   * @throws {StatusError}
+   * @example
+   * const session = await client.openSession();
+   */
+  public async openSession(request: OpenSessionRequest = {}): Promise<IDBSQLSession> {
+    // Prepare session configuration
+    const configuration = request.configuration ? { ...request.configuration } : {};
+ 
+    // Add metric view metadata config if enabled
+    if (this.config.enableMetricViewMetadata) {
+      configuration['spark.sql.thriftserver.metadata.metricview.enabled'] = 'true';
+    }
+ 
+    const response = await this.driver.openSession({
+      client_protocol_i64: new Int64(TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V8),
+      ...getInitialNamespaceOptions(request.initialCatalog, request.initialSchema),
+      configuration,
+      canUseMultipleCatalogs: true,
+    });
+ 
+    Status.assert(response.status);
+    const session = new DBSQLSession({
+      handle: definedOrError(response.sessionHandle),
+      context: this,
+      serverProtocolVersion: response.serverProtocolVersion,
+    });
+    this.sessions.add(session);
+    return session;
+  }
+ 
+  public async close(): Promise<void> {
+    await this.sessions.closeAll();
+ 
+    this.client = undefined;
+    this.connectionProvider = undefined;
+    this.authProvider = undefined;
+  }
+ 
+  public getConfig(): ClientConfig {
+    return this.config;
+  }
+ 
+  public getLogger(): IDBSQLLogger {
+    return this.logger;
+  }
+ 
+  public async getConnectionProvider(): Promise<IConnectionProvider> {
+    if (!this.connectionProvider) {
+      throw new HiveDriverError('DBSQLClient: not connected');
+    }
+ 
+    return this.connectionProvider;
+  }
+ 
+  public async getClient(): Promise<IThriftClient> {
+    const connectionProvider = await this.getConnectionProvider();
+ 
+    if (!this.client) {
+      this.logger.log(LogLevel.info, 'DBSQLClient: initializing thrift client');
+      this.client = this.thrift.createClient(TCLIService, await connectionProvider.getThriftConnection());
+    }
+ 
+    if (this.authProvider) {
+      const authHeaders = await this.authProvider.authenticate();
+      connectionProvider.setHeaders(authHeaders);
+    }
+ 
+    return this.client;
+  }
+ 
+  public async getDriver(): Promise<IDriver> {
+    return this.driver;
+  }
+ 
+  public async getAuthHeaders(): Promise<HeadersInit> {
+    if (this.authProvider) {
+      try {
+        return await this.authProvider.authenticate();
+      } catch (error) {
+        this.logger.log(LogLevel.debug, `Error getting auth headers: ${error}`);
+        return {};
+      }
+    }
+    return {};
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLLogger.ts.html b/coverage/lcov-report/lib/DBSQLLogger.ts.html new file mode 100644 index 00000000..4e0bd4a9 --- /dev/null +++ b/coverage/lcov-report/lib/DBSQLLogger.ts.html @@ -0,0 +1,190 @@ + + + + + + Code coverage report for lib/DBSQLLogger.ts + + + + + + + + + +
+
+

All files / lib DBSQLLogger.ts

+
+ +
+ 25% + Statements + 3/12 +
+ + +
+ 0% + Branches + 0/6 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 25% + Lines + 3/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +361x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import winston, { Logger } from 'winston';
+import IDBSQLLogger, { LoggerOptions, LogLevel } from './contracts/IDBSQLLogger';
+ 
+export default class DBSQLLogger implements IDBSQLLogger {
+  logger: Logger;
+ 
+  transports: {
+    console: winston.transports.ConsoleTransportInstance;
+    file?: winston.transports.FileTransportInstance;
+  };
+ 
+  constructor({ level = LogLevel.info, filepath }: LoggerOptions = {}) {
+    this.transports = {
+      console: new winston.transports.Console({ handleExceptions: true, level }),
+    };
+    this.logger = winston.createLogger({
+      transports: [this.transports.console],
+    });
+    if (filepath) {
+      this.transports.file = new winston.transports.File({ filename: filepath, handleExceptions: true, level });
+      this.logger.add(this.transports.file);
+    }
+  }
+ 
+  async log(level: LogLevel, message: string) {
+    this.logger.log({ level, message });
+  }
+ 
+  setLevel(level: LogLevel) {
+    this.transports.console.level = level;
+    if (this.transports.file) {
+      this.transports.file.level = level;
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLOperation.ts.html b/coverage/lcov-report/lib/DBSQLOperation.ts.html new file mode 100644 index 00000000..69bfd8cc --- /dev/null +++ b/coverage/lcov-report/lib/DBSQLOperation.ts.html @@ -0,0 +1,1537 @@ + + + + + + Code coverage report for lib/DBSQLOperation.ts + + + + + + + + + +
+
+

All files / lib DBSQLOperation.ts

+
+ +
+ 9.58% + Statements + 16/167 +
+ + +
+ 0% + Branches + 0/165 +
+ + +
+ 0% + Functions + 0/24 +
+ + +
+ 9.58% + Lines + 16/167 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +4851x +1x +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { stringify, NIL } from 'uuid';
+import { Readable } from 'node:stream';
+import IOperation, {
+  FetchOptions,
+  FinishedOptions,
+  GetSchemaOptions,
+  WaitUntilReadyOptions,
+  IteratorOptions,
+  IOperationChunksIterator,
+  IOperationRowsIterator,
+  NodeStreamOptions,
+} from './contracts/IOperation';
+import {
+  TGetOperationStatusResp,
+  TOperationHandle,
+  TTableSchema,
+  TSparkDirectResults,
+  TGetResultSetMetadataResp,
+  TSparkRowSetType,
+  TCloseOperationResp,
+  TOperationState,
+} from '../thrift/TCLIService_types';
+import Status from './dto/Status';
+import { LogLevel } from './contracts/IDBSQLLogger';
+import OperationStateError, { OperationStateErrorCode } from './errors/OperationStateError';
+import IResultsProvider from './result/IResultsProvider';
+import RowSetProvider from './result/RowSetProvider';
+import JsonResultHandler from './result/JsonResultHandler';
+import ArrowResultHandler from './result/ArrowResultHandler';
+import CloudFetchResultHandler from './result/CloudFetchResultHandler';
+import ArrowResultConverter from './result/ArrowResultConverter';
+import ResultSlicer from './result/ResultSlicer';
+import { definedOrError } from './utils';
+import { OperationChunksIterator, OperationRowsIterator } from './utils/OperationIterator';
+import HiveDriverError from './errors/HiveDriverError';
+import IClientContext from './contracts/IClientContext';
+ 
+interface DBSQLOperationConstructorOptions {
+  handle: TOperationHandle;
+  directResults?: TSparkDirectResults;
+  context: IClientContext;
+}
+ 
+async function delay(ms?: number): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      resolve();
+    }, ms);
+  });
+}
+ 
+export default class DBSQLOperation implements IOperation {
+  private readonly context: IClientContext;
+ 
+  private readonly operationHandle: TOperationHandle;
+ 
+  public onClose?: () => void;
+ 
+  private readonly _data: RowSetProvider;
+ 
+  private readonly closeOperation?: TCloseOperationResp;
+ 
+  private closed: boolean = false;
+ 
+  private cancelled: boolean = false;
+ 
+  private metadata?: TGetResultSetMetadataResp;
+ 
+  private metadataPromise?: Promise<TGetResultSetMetadataResp>;
+ 
+  private state: TOperationState = TOperationState.INITIALIZED_STATE;
+ 
+  // Once operation is finished or fails - cache status response, because subsequent calls
+  // to `getOperationStatus()` may fail with irrelevant errors, e.g. HTTP 404
+  private operationStatus?: TGetOperationStatusResp;
+ 
+  private resultHandler?: ResultSlicer<any>;
+ 
+  constructor({ handle, directResults, context }: DBSQLOperationConstructorOptions) {
+    this.operationHandle = handle;
+    this.context = context;
+ 
+    const useOnlyPrefetchedResults = Boolean(directResults?.closeOperation);
+ 
+    if (directResults?.operationStatus) {
+      this.processOperationStatusResponse(directResults.operationStatus);
+    }
+ 
+    this.metadata = directResults?.resultSetMetadata;
+    this._data = new RowSetProvider(
+      this.context,
+      this.operationHandle,
+      [directResults?.resultSet],
+      useOnlyPrefetchedResults,
+    );
+    this.closeOperation = directResults?.closeOperation;
+    this.context.getLogger().log(LogLevel.debug, `Operation created with id: ${this.id}`);
+  }
+ 
+  public iterateChunks(options?: IteratorOptions): IOperationChunksIterator {
+    return new OperationChunksIterator(this, options);
+  }
+ 
+  public iterateRows(options?: IteratorOptions): IOperationRowsIterator {
+    return new OperationRowsIterator(this, options);
+  }
+ 
+  public toNodeStream(options?: NodeStreamOptions): Readable {
+    let iterable: IOperationChunksIterator | IOperationRowsIterator | undefined;
+ 
+    switch (options?.mode ?? 'chunks') {
+      case 'chunks':
+        iterable = this.iterateChunks(options?.iteratorOptions);
+        break;
+      case 'rows':
+        iterable = this.iterateRows(options?.iteratorOptions);
+        break;
+      default:
+        throw new Error(`IOperation.toNodeStream: unsupported mode ${options?.mode}`);
+    }
+ 
+    return Readable.from(iterable, options?.streamOptions);
+  }
+ 
+  public get id() {
+    const operationId = this.operationHandle?.operationId?.guid;
+    return operationId ? stringify(operationId) : NIL;
+  }
+ 
+  /**
+   * Fetches all data
+   * @public
+   * @param options - maxRows property can be set to limit chunk size
+   * @returns Array of data with length equal to option.maxRows
+   * @throws {StatusError}
+   * @example
+   * const result = await queryOperation.fetchAll();
+   */
+  public async fetchAll(options?: FetchOptions): Promise<Array<object>> {
+    const data: Array<Array<object>> = [];
+ 
+    const fetchChunkOptions = {
+      ...options,
+      // Tell slicer to return raw chunks. We're going to process all of them anyway,
+      // so no need to additionally buffer and slice chunks returned by server
+      disableBuffering: true,
+    };
+ 
+    do {
+      // eslint-disable-next-line no-await-in-loop
+      const chunk = await this.fetchChunk(fetchChunkOptions);
+      data.push(chunk);
+    } while (await this.hasMoreRows()); // eslint-disable-line no-await-in-loop
+    this.context.getLogger().log(LogLevel.debug, `Fetched all data from operation with id: ${this.id}`);
+ 
+    return data.flat();
+  }
+ 
+  /**
+   * Fetches chunk of data
+   * @public
+   * @param options - maxRows property sets chunk size
+   * @returns Array of data with length equal to option.maxRows
+   * @throws {StatusError}
+   * @example
+   * const result = await queryOperation.fetchChunk({maxRows: 1000});
+   */
+  public async fetchChunk(options?: FetchOptions): Promise<Array<object>> {
+    await this.failIfClosed();
+ 
+    if (!this.operationHandle.hasResultSet) {
+      return [];
+    }
+ 
+    await this.waitUntilReady(options);
+ 
+    const resultHandler = await this.getResultHandler();
+    await this.failIfClosed();
+ 
+    // All the library code is Promise-based, however, since Promises are microtasks,
+    // enqueueing a lot of promises may block macrotasks execution for a while.
+    // Usually, there are no much microtasks scheduled, however, when fetching query
+    // results (especially CloudFetch ones) it's quite easy to block event loop for
+    // long enough to break a lot of things. For example, with CloudFetch, after first
+    // set of files are downloaded and being processed immediately one by one, event
+    // loop easily gets blocked for enough time to break connection pool. `http.Agent`
+    // stops receiving socket events, and marks all sockets invalid on the next attempt
+    // to use them. See these similar issues that helped to debug this particular case -
+    // https://github.com/nodejs/node/issues/47130 and https://github.com/node-fetch/node-fetch/issues/1735
+    // This simple fix allows to clean up a microtasks queue and allow Node to process
+    // macrotasks as well, allowing the normal operation of other code. Also, this
+    // fix is added to `fetchChunk` method because, unlike other methods, `fetchChunk` is
+    // a potential source of issues described above
+    await new Promise<void>((resolve) => {
+      setTimeout(resolve, 0);
+    });
+ 
+    const defaultMaxRows = this.context.getConfig().fetchChunkDefaultMaxRows;
+ 
+    const result = resultHandler.fetchNext({
+      limit: options?.maxRows ?? defaultMaxRows,
+      disableBuffering: options?.disableBuffering,
+    });
+    await this.failIfClosed();
+ 
+    this.context
+      .getLogger()
+      .log(
+        LogLevel.debug,
+        `Fetched chunk of size: ${options?.maxRows ?? defaultMaxRows} from operation with id: ${this.id}`,
+      );
+    return result;
+  }
+ 
+  /**
+   * Requests operation status
+   * @param progress
+   * @throws {StatusError}
+   */
+  public async status(progress: boolean = false): Promise<TGetOperationStatusResp> {
+    await this.failIfClosed();
+    this.context.getLogger().log(LogLevel.debug, `Fetching status for operation with id: ${this.id}`);
+ 
+    if (this.operationStatus) {
+      return this.operationStatus;
+    }
+ 
+    const driver = await this.context.getDriver();
+    const response = await driver.getOperationStatus({
+      operationHandle: this.operationHandle,
+      getProgressUpdate: progress,
+    });
+ 
+    return this.processOperationStatusResponse(response);
+  }
+ 
+  /**
+   * Cancels operation
+   * @throws {StatusError}
+   */
+  public async cancel(): Promise<Status> {
+    if (this.closed || this.cancelled) {
+      return Status.success();
+    }
+ 
+    this.context.getLogger().log(LogLevel.debug, `Cancelling operation with id: ${this.id}`);
+ 
+    const driver = await this.context.getDriver();
+    const response = await driver.cancelOperation({
+      operationHandle: this.operationHandle,
+    });
+    Status.assert(response.status);
+    this.cancelled = true;
+    const result = new Status(response.status);
+ 
+    // Cancelled operation becomes unusable, similarly to being closed
+    this.onClose?.();
+    return result;
+  }
+ 
+  /**
+   * Closes operation
+   * @throws {StatusError}
+   */
+  public async close(): Promise<Status> {
+    if (this.closed || this.cancelled) {
+      return Status.success();
+    }
+ 
+    this.context.getLogger().log(LogLevel.debug, `Closing operation with id: ${this.id}`);
+ 
+    const driver = await this.context.getDriver();
+    const response =
+      this.closeOperation ??
+      (await driver.closeOperation({
+        operationHandle: this.operationHandle,
+      }));
+    Status.assert(response.status);
+    this.closed = true;
+    const result = new Status(response.status);
+ 
+    this.onClose?.();
+    return result;
+  }
+ 
+  public async finished(options?: FinishedOptions): Promise<void> {
+    await this.failIfClosed();
+    await this.waitUntilReady(options);
+  }
+ 
+  public async hasMoreRows(): Promise<boolean> {
+    // If operation is closed or cancelled - we should not try to get data from it
+    if (this.closed || this.cancelled) {
+      return false;
+    }
+ 
+    // Wait for operation to finish before checking for more rows
+    // This ensures metadata can be fetched successfully
+    if (this.operationHandle.hasResultSet) {
+      await this.waitUntilReady();
+    }
+ 
+    // If we fetched all the data from server - check if there's anything buffered in result handler
+    const resultHandler = await this.getResultHandler();
+    return resultHandler.hasMore();
+  }
+ 
+  public async getSchema(options?: GetSchemaOptions): Promise<TTableSchema | null> {
+    await this.failIfClosed();
+ 
+    if (!this.operationHandle.hasResultSet) {
+      return null;
+    }
+ 
+    await this.waitUntilReady(options);
+ 
+    this.context.getLogger().log(LogLevel.debug, `Fetching schema for operation with id: ${this.id}`);
+    const metadata = await this.fetchMetadata();
+    return metadata.schema ?? null;
+  }
+ 
+  public async getMetadata(): Promise<TGetResultSetMetadataResp> {
+    await this.failIfClosed();
+    await this.waitUntilReady();
+    return this.fetchMetadata();
+  }
+ 
+  private async failIfClosed(): Promise<void> {
+    if (this.closed) {
+      throw new OperationStateError(OperationStateErrorCode.Closed);
+    }
+    if (this.cancelled) {
+      throw new OperationStateError(OperationStateErrorCode.Canceled);
+    }
+  }
+ 
+  private async waitUntilReady(options?: WaitUntilReadyOptions) {
+    if (this.state === TOperationState.FINISHED_STATE) {
+      return;
+    }
+ 
+    let isReady = false;
+ 
+    while (!isReady) {
+      // eslint-disable-next-line no-await-in-loop
+      const response = await this.status(Boolean(options?.progress));
+ 
+      if (options?.callback) {
+        // eslint-disable-next-line no-await-in-loop
+        await Promise.resolve(options.callback(response));
+      }
+ 
+      switch (response.operationState) {
+        // For these states do nothing and continue waiting
+        case TOperationState.INITIALIZED_STATE:
+        case TOperationState.PENDING_STATE:
+        case TOperationState.RUNNING_STATE:
+          break;
+ 
+        // Operation is completed, so exit the loop
+        case TOperationState.FINISHED_STATE:
+          isReady = true;
+          break;
+ 
+        // Operation was cancelled, so set a flag and exit the loop (throw an error)
+        case TOperationState.CANCELED_STATE:
+          this.cancelled = true;
+          throw new OperationStateError(OperationStateErrorCode.Canceled, response);
+ 
+        // Operation was closed, so set a flag and exit the loop (throw an error)
+        case TOperationState.CLOSED_STATE:
+          this.closed = true;
+          throw new OperationStateError(OperationStateErrorCode.Closed, response);
+ 
+        // Error states - throw and exit the loop
+        case TOperationState.ERROR_STATE:
+          throw new OperationStateError(OperationStateErrorCode.Error, response);
+        case TOperationState.TIMEDOUT_STATE:
+          throw new OperationStateError(OperationStateErrorCode.Timeout, response);
+        case TOperationState.UKNOWN_STATE:
+        default:
+          throw new OperationStateError(OperationStateErrorCode.Unknown, response);
+      }
+ 
+      // If not ready yet - make some delay before the next status requests
+      if (!isReady) {
+        // eslint-disable-next-line no-await-in-loop
+        await delay(100);
+      }
+    }
+  }
+ 
+  private async fetchMetadata() {
+    // If metadata is already cached, return it immediately
+    if (this.metadata) {
+      return this.metadata;
+    }
+ 
+    // If a fetch is already in progress, wait for it to complete
+    if (this.metadataPromise) {
+      return this.metadataPromise;
+    }
+ 
+    // Start a new fetch and cache the promise to prevent concurrent fetches
+    this.metadataPromise = (async () => {
+      const driver = await this.context.getDriver();
+      const metadata = await driver.getResultSetMetadata({
+        operationHandle: this.operationHandle,
+      });
+      Status.assert(metadata.status);
+      this.metadata = metadata;
+      return metadata;
+    })();
+ 
+    try {
+      return await this.metadataPromise;
+    } finally {
+      // Clear the promise once completed (success or failure)
+      this.metadataPromise = undefined;
+    }
+  }
+ 
+  private async getResultHandler(): Promise<ResultSlicer<any>> {
+    const metadata = await this.fetchMetadata();
+    const resultFormat = definedOrError(metadata.resultFormat);
+ 
+    if (!this.resultHandler) {
+      let resultSource: IResultsProvider<Array<any>> | undefined;
+ 
+      switch (resultFormat) {
+        case TSparkRowSetType.COLUMN_BASED_SET:
+          resultSource = new JsonResultHandler(this.context, this._data, metadata);
+          break;
+        case TSparkRowSetType.ARROW_BASED_SET:
+          resultSource = new ArrowResultConverter(
+            this.context,
+            new ArrowResultHandler(this.context, this._data, metadata),
+            metadata,
+          );
+          break;
+        case TSparkRowSetType.URL_BASED_SET:
+          resultSource = new ArrowResultConverter(
+            this.context,
+            new CloudFetchResultHandler(this.context, this._data, metadata),
+            metadata,
+          );
+          break;
+        // no default
+      }
+ 
+      if (resultSource) {
+        this.resultHandler = new ResultSlicer(this.context, resultSource);
+      }
+    }
+ 
+    if (!this.resultHandler) {
+      throw new HiveDriverError(`Unsupported result format: ${TSparkRowSetType[resultFormat]}`);
+    }
+ 
+    return this.resultHandler;
+  }
+ 
+  private processOperationStatusResponse(response: TGetOperationStatusResp) {
+    Status.assert(response.status);
+ 
+    this.state = response.operationState ?? this.state;
+ 
+    if (typeof response.hasResultSet === 'boolean') {
+      this.operationHandle.hasResultSet = response.hasResultSet;
+    }
+ 
+    const isInProgress = [
+      TOperationState.INITIALIZED_STATE,
+      TOperationState.PENDING_STATE,
+      TOperationState.RUNNING_STATE,
+    ].includes(this.state);
+ 
+    if (!isInProgress) {
+      this.operationStatus = response;
+    }
+ 
+    return response;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLParameter.ts.html b/coverage/lcov-report/lib/DBSQLParameter.ts.html new file mode 100644 index 00000000..58d43aed --- /dev/null +++ b/coverage/lcov-report/lib/DBSQLParameter.ts.html @@ -0,0 +1,388 @@ + + + + + + Code coverage report for lib/DBSQLParameter.ts + + + + + + + + + +
+
+

All files / lib DBSQLParameter.ts

+
+ +
+ 54.54% + Statements + 18/33 +
+ + +
+ 4.65% + Branches + 2/43 +
+ + +
+ 33.33% + Functions + 1/3 +
+ + +
+ 54.54% + Lines + 18/33 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +1021x +1x +  +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Int64 from 'node-int64';
+import { TSparkParameter, TSparkParameterValue } from '../thrift/TCLIService_types';
+ 
+export type DBSQLParameterValue = undefined | null | boolean | number | bigint | Int64 | Date | string;
+ 
+export enum DBSQLParameterType {
+  VOID = 'VOID', // aka NULL
+  STRING = 'STRING',
+  DATE = 'DATE',
+  TIMESTAMP = 'TIMESTAMP',
+  FLOAT = 'FLOAT',
+  DECIMAL = 'DECIMAL',
+  DOUBLE = 'DOUBLE',
+  INTEGER = 'INTEGER',
+  BIGINT = 'BIGINT',
+  SMALLINT = 'SMALLINT',
+  TINYINT = 'TINYINT',
+  BOOLEAN = 'BOOLEAN',
+  INTERVALMONTH = 'INTERVAL MONTH',
+  INTERVALDAY = 'INTERVAL DAY',
+}
+ 
+interface DBSQLParameterOptions {
+  type?: DBSQLParameterType;
+  value: DBSQLParameterValue;
+}
+ 
+interface ToSparkParameterOptions {
+  name?: string;
+}
+ 
+export class DBSQLParameter {
+  public readonly type?: string;
+ 
+  public readonly value: DBSQLParameterValue;
+ 
+  constructor({ type, value }: DBSQLParameterOptions) {
+    this.type = type;
+    this.value = value;
+  }
+ 
+  public toSparkParameter({ name }: ToSparkParameterOptions = {}): TSparkParameter {
+    // If VOID type was set explicitly - ignore value
+    if (this.type === DBSQLParameterType.VOID) {
+      return new TSparkParameter({ name }); // for NULL neither `type` nor `value` should be set
+    }
+ 
+    // Infer NULL values
+    if (this.value === undefined || this.value === null) {
+      return new TSparkParameter({ name }); // for NULL neither `type` nor `value` should be set
+    }
+ 
+    if (typeof this.value === 'boolean') {
+      return new TSparkParameter({
+        name,
+        type: this.type ?? DBSQLParameterType.BOOLEAN,
+        value: new TSparkParameterValue({
+          stringValue: this.value ? 'TRUE' : 'FALSE',
+        }),
+      });
+    }
+ 
+    if (typeof this.value === 'number') {
+      return new TSparkParameter({
+        name,
+        type: this.type ?? (Number.isInteger(this.value) ? DBSQLParameterType.INTEGER : DBSQLParameterType.DOUBLE),
+        value: new TSparkParameterValue({
+          stringValue: Number(this.value).toString(),
+        }),
+      });
+    }
+ 
+    if (this.value instanceof Int64 || typeof this.value === 'bigint') {
+      return new TSparkParameter({
+        name,
+        type: this.type ?? DBSQLParameterType.BIGINT,
+        value: new TSparkParameterValue({
+          stringValue: this.value.toString(),
+        }),
+      });
+    }
+ 
+    if (this.value instanceof Date) {
+      return new TSparkParameter({
+        name,
+        type: this.type ?? DBSQLParameterType.TIMESTAMP,
+        value: new TSparkParameterValue({
+          stringValue: this.value.toISOString(),
+        }),
+      });
+    }
+ 
+    return new TSparkParameter({
+      name,
+      type: this.type ?? DBSQLParameterType.STRING,
+      value: new TSparkParameterValue({
+        stringValue: this.value,
+      }),
+    });
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLSession.ts.html b/coverage/lcov-report/lib/DBSQLSession.ts.html new file mode 100644 index 00000000..35b15f0c --- /dev/null +++ b/coverage/lcov-report/lib/DBSQLSession.ts.html @@ -0,0 +1,1972 @@ + + + + + + Code coverage report for lib/DBSQLSession.ts + + + + + + + + + +
+
+

All files / lib DBSQLSession.ts

+
+ +
+ 10.19% + Statements + 21/206 +
+ + +
+ 0% + Branches + 0/115 +
+ + +
+ 0% + Functions + 0/26 +
+ + +
+ 10.19% + Lines + 21/206 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +6301x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import * as fs from 'fs';
+import * as path from 'path';
+import stream from 'node:stream';
+import util from 'node:util';
+import { stringify, NIL } from 'uuid';
+import Int64 from 'node-int64';
+import fetch, { HeadersInit } from 'node-fetch';
+import {
+  TSessionHandle,
+  TStatus,
+  TOperationHandle,
+  TSparkDirectResults,
+  TSparkArrowTypes,
+  TSparkParameter,
+  TProtocolVersion,
+  TExecuteStatementReq,
+} from '../thrift/TCLIService_types';
+import IDBSQLSession, {
+  ExecuteStatementOptions,
+  TypeInfoRequest,
+  CatalogsRequest,
+  SchemasRequest,
+  TablesRequest,
+  TableTypesRequest,
+  ColumnsRequest,
+  FunctionsRequest,
+  PrimaryKeysRequest,
+  CrossReferenceRequest,
+} from './contracts/IDBSQLSession';
+import IOperation from './contracts/IOperation';
+import DBSQLOperation from './DBSQLOperation';
+import Status from './dto/Status';
+import InfoValue from './dto/InfoValue';
+import { definedOrError, LZ4, ProtocolVersion } from './utils';
+import CloseableCollection from './utils/CloseableCollection';
+import { LogLevel } from './contracts/IDBSQLLogger';
+import HiveDriverError from './errors/HiveDriverError';
+import StagingError from './errors/StagingError';
+import { DBSQLParameter, DBSQLParameterValue } from './DBSQLParameter';
+import ParameterError from './errors/ParameterError';
+import IClientContext, { ClientConfig } from './contracts/IClientContext';
+ 
+// Explicitly promisify a callback-style `pipeline` because `node:stream/promises` is not available in Node 14
+const pipeline = util.promisify(stream.pipeline);
+ 
+interface OperationResponseShape {
+  status: TStatus;
+  operationHandle?: TOperationHandle;
+  directResults?: TSparkDirectResults;
+}
+ 
+export function numberToInt64(value: number | bigint | Int64): Int64 {
+  if (value instanceof Int64) {
+    return value;
+  }
+ 
+  if (typeof value === 'bigint') {
+    const buffer = new ArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT);
+    const view = new DataView(buffer);
+    view.setBigInt64(0, value, false); // `false` to use big-endian order
+    return new Int64(Buffer.from(buffer));
+  }
+ 
+  return new Int64(value);
+}
+ 
+function getDirectResultsOptions(maxRows: number | bigint | Int64 | null | undefined, config: ClientConfig) {
+  if (maxRows === null) {
+    return {};
+  }
+ 
+  return {
+    getDirectResults: {
+      maxRows: numberToInt64(maxRows ?? config.directResultsDefaultMaxRows),
+    },
+  };
+}
+ 
+function getArrowOptions(
+  config: ClientConfig,
+  serverProtocolVersion: TProtocolVersion | undefined | null,
+): {
+  canReadArrowResult: boolean;
+  useArrowNativeTypes?: TSparkArrowTypes;
+} {
+  const { arrowEnabled = true, useArrowNativeTypes = true } = config;
+ 
+  if (!arrowEnabled || !ProtocolVersion.supportsArrowMetadata(serverProtocolVersion)) {
+    return {
+      canReadArrowResult: false,
+    };
+  }
+ 
+  return {
+    canReadArrowResult: true,
+    useArrowNativeTypes: {
+      timestampAsArrow: useArrowNativeTypes,
+      decimalAsArrow: useArrowNativeTypes,
+      complexTypesAsArrow: useArrowNativeTypes,
+      // TODO: currently unsupported by `apache-arrow` (see https://github.com/streamlit/streamlit/issues/4489)
+      intervalTypesAsArrow: false,
+    },
+  };
+}
+ 
+function getQueryParameters(
+  namedParameters?: Record<string, DBSQLParameter | DBSQLParameterValue>,
+  ordinalParameters?: Array<DBSQLParameter | DBSQLParameterValue>,
+): Array<TSparkParameter> {
+  const namedParametersProvided = namedParameters !== undefined && Object.keys(namedParameters).length > 0;
+  const ordinalParametersProvided = ordinalParameters !== undefined && ordinalParameters.length > 0;
+ 
+  if (namedParametersProvided && ordinalParametersProvided) {
+    throw new ParameterError('Driver does not support both ordinal and named parameters.');
+  }
+ 
+  if (!namedParametersProvided && !ordinalParametersProvided) {
+    return [];
+  }
+ 
+  const result: Array<TSparkParameter> = [];
+ 
+  if (namedParameters !== undefined) {
+    for (const name of Object.keys(namedParameters)) {
+      const value = namedParameters[name];
+      const param = value instanceof DBSQLParameter ? value : new DBSQLParameter({ value });
+      result.push(param.toSparkParameter({ name }));
+    }
+  }
+ 
+  if (ordinalParameters !== undefined) {
+    for (const value of ordinalParameters) {
+      const param = value instanceof DBSQLParameter ? value : new DBSQLParameter({ value });
+      result.push(param.toSparkParameter());
+    }
+  }
+ 
+  return result;
+}
+ 
+interface DBSQLSessionConstructorOptions {
+  handle: TSessionHandle;
+  context: IClientContext;
+  serverProtocolVersion?: TProtocolVersion;
+}
+ 
+export default class DBSQLSession implements IDBSQLSession {
+  private readonly context: IClientContext;
+ 
+  private readonly sessionHandle: TSessionHandle;
+ 
+  private isOpen = true;
+ 
+  private serverProtocolVersion?: TProtocolVersion;
+ 
+  public onClose?: () => void;
+ 
+  private operations = new CloseableCollection<DBSQLOperation>();
+ 
+  /**
+   * Helper method to determine if runAsync should be set for metadata operations
+   * @private
+   * @returns true if supported by protocol version, undefined otherwise
+   */
+  private getRunAsyncForMetadataOperations(): boolean | undefined {
+    return ProtocolVersion.supportsAsyncMetadataOperations(this.serverProtocolVersion) ? true : undefined;
+  }
+ 
+  constructor({ handle, context, serverProtocolVersion }: DBSQLSessionConstructorOptions) {
+    this.sessionHandle = handle;
+    this.context = context;
+    // Get the server protocol version from the provided parameter (from TOpenSessionResp)
+    this.serverProtocolVersion = serverProtocolVersion;
+    this.context.getLogger().log(LogLevel.debug, `Session created with id: ${this.id}`);
+    this.context.getLogger().log(LogLevel.debug, `Server protocol version: ${this.serverProtocolVersion}`);
+  }
+ 
+  public get id() {
+    const sessionId = this.sessionHandle?.sessionId?.guid;
+    return sessionId ? stringify(sessionId) : NIL;
+  }
+ 
+  /**
+   * Fetches info
+   * @public
+   * @param infoType - One of the values TCLIService_types.TGetInfoType
+   * @returns Value corresponding to info type requested
+   * @example
+   * const response = await session.getInfo(thrift.TCLIService_types.TGetInfoType.CLI_DBMS_VER);
+   */
+  public async getInfo(infoType: number): Promise<InfoValue> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const operationPromise = driver.getInfo({
+      sessionHandle: this.sessionHandle,
+      infoType,
+    });
+    const response = await this.handleResponse(operationPromise);
+    Status.assert(response.status);
+    return new InfoValue(response.infoValue);
+  }
+ 
+  /**
+   * Executes statement
+   * @public
+   * @param statement - SQL statement to be executed
+   * @param options - maxRows field is used to specify Direct Results
+   * @returns DBSQLOperation
+   * @example
+   * const operation = await session.executeStatement(query);
+   */
+  public async executeStatement(statement: string, options: ExecuteStatementOptions = {}): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const request = new TExecuteStatementReq({
+      sessionHandle: this.sessionHandle,
+      statement,
+      queryTimeout: options.queryTimeout ? numberToInt64(options.queryTimeout) : undefined,
+      runAsync: true,
+      ...getDirectResultsOptions(options.maxRows, clientConfig),
+      ...getArrowOptions(clientConfig, this.serverProtocolVersion),
+    });
+ 
+    if (ProtocolVersion.supportsParameterizedQueries(this.serverProtocolVersion)) {
+      request.parameters = getQueryParameters(options.namedParameters, options.ordinalParameters);
+    }
+ 
+    if (ProtocolVersion.supportsCloudFetch(this.serverProtocolVersion)) {
+      request.canDownloadResult = options.useCloudFetch ?? clientConfig.useCloudFetch;
+    }
+ 
+    if (ProtocolVersion.supportsArrowCompression(this.serverProtocolVersion) && request.canDownloadResult !== true) {
+      request.canDecompressLZ4Result = (options.useLZ4Compression ?? clientConfig.useLZ4Compression) && Boolean(LZ4());
+    }
+ 
+    const operationPromise = driver.executeStatement(request);
+    const response = await this.handleResponse(operationPromise);
+    const operation = this.createOperation(response);
+ 
+    // If `stagingAllowedLocalPath` is provided - assume that operation possibly may be a staging operation.
+    // To know for sure, fetch metadata and check a `isStagingOperation` flag. If it happens that it wasn't
+    // a staging operation - not a big deal, we just fetched metadata earlier, but operation is still usable
+    // and user can get data from it.
+    // If `stagingAllowedLocalPath` is not provided - don't do anything to the operation. In a case of regular
+    // operation, everything will work as usual. In a case of staging operation, it will be processed like any
+    // other query - it will be possible to get data from it as usual, or use other operation methods.
+    if (options.stagingAllowedLocalPath !== undefined) {
+      const metadata = await operation.getMetadata();
+      if (metadata.isStagingOperation) {
+        const allowedLocalPath = Array.isArray(options.stagingAllowedLocalPath)
+          ? options.stagingAllowedLocalPath
+          : [options.stagingAllowedLocalPath];
+        return this.handleStagingOperation(operation, allowedLocalPath);
+      }
+    }
+    return operation;
+  }
+ 
+  private async handleStagingOperation(operation: IOperation, allowedLocalPath: Array<string>): Promise<IOperation> {
+    type StagingResponse = {
+      presignedUrl: string;
+      localFile?: string;
+      headers: HeadersInit;
+      operation: string;
+    };
+    const rows = await operation.fetchAll();
+    if (rows.length !== 1) {
+      throw new StagingError('Staging operation: expected only one row in result');
+    }
+    const row = rows[0] as StagingResponse;
+ 
+    // For REMOVE operation local file is not available, so no need to validate it
+    if (row.localFile !== undefined) {
+      let allowOperation = false;
+ 
+      for (const filepath of allowedLocalPath) {
+        const relativePath = path.relative(filepath, row.localFile);
+ 
+        if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
+          allowOperation = true;
+        }
+      }
+ 
+      if (!allowOperation) {
+        throw new StagingError('Staging path not a subset of allowed local paths.');
+      }
+    }
+ 
+    const { localFile, presignedUrl, headers } = row;
+ 
+    switch (row.operation) {
+      case 'GET':
+        await this.handleStagingGet(localFile, presignedUrl, headers);
+        return operation;
+      case 'PUT':
+        await this.handleStagingPut(localFile, presignedUrl, headers);
+        return operation;
+      case 'REMOVE':
+        await this.handleStagingRemove(presignedUrl, headers);
+        return operation;
+      default:
+        throw new StagingError(`Staging query operation is not supported: ${row.operation}`);
+    }
+  }
+ 
+  private async handleStagingGet(
+    localFile: string | undefined,
+    presignedUrl: string,
+    headers: HeadersInit,
+  ): Promise<void> {
+    if (localFile === undefined) {
+      throw new StagingError('Local file path not provided');
+    }
+ 
+    const connectionProvider = await this.context.getConnectionProvider();
+    const agent = await connectionProvider.getAgent();
+ 
+    const response = await fetch(presignedUrl, { method: 'GET', headers, agent });
+    if (!response.ok) {
+      throw new StagingError(`HTTP error ${response.status} ${response.statusText}`);
+    }
+ 
+    const fileStream = fs.createWriteStream(localFile);
+    // `pipeline` will do all the dirty job for us, including error handling and closing all the streams properly
+    return pipeline(response.body, fileStream);
+  }
+ 
+  private async handleStagingRemove(presignedUrl: string, headers: HeadersInit): Promise<void> {
+    const connectionProvider = await this.context.getConnectionProvider();
+    const agent = await connectionProvider.getAgent();
+ 
+    const response = await fetch(presignedUrl, { method: 'DELETE', headers, agent });
+    // Looks that AWS and Azure have a different behavior of HTTP `DELETE` for non-existing files.
+    // AWS assumes that - since file already doesn't exist - the goal is achieved, and returns HTTP 200.
+    // Azure, on the other hand, is somewhat stricter and check if file exists before deleting it. And if
+    // file doesn't exist - Azure returns HTTP 404.
+    //
+    // For us, it's totally okay if file didn't exist before removing. So when we get an HTTP 404 -
+    // just ignore it and report success. This way we can have a uniform library behavior for all clouds
+    if (!response.ok && response.status !== 404) {
+      throw new StagingError(`HTTP error ${response.status} ${response.statusText}`);
+    }
+  }
+ 
+  private async handleStagingPut(
+    localFile: string | undefined,
+    presignedUrl: string,
+    headers: HeadersInit,
+  ): Promise<void> {
+    if (localFile === undefined) {
+      throw new StagingError('Local file path not provided');
+    }
+ 
+    const connectionProvider = await this.context.getConnectionProvider();
+    const agent = await connectionProvider.getAgent();
+ 
+    const fileStream = fs.createReadStream(localFile);
+    const fileInfo = fs.statSync(localFile, { bigint: true });
+ 
+    const response = await fetch(presignedUrl, {
+      method: 'PUT',
+      headers: {
+        ...headers,
+        // This header is required by server
+        'Content-Length': fileInfo.size.toString(),
+      },
+      agent,
+      body: fileStream,
+    });
+    if (!response.ok) {
+      throw new StagingError(`HTTP error ${response.status} ${response.statusText}`);
+    }
+  }
+ 
+  /**
+   * Information about supported data types
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getTypeInfo(request: TypeInfoRequest = {}): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getTypeInfo({
+      sessionHandle: this.sessionHandle,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Get list of catalogs
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getCatalogs(request: CatalogsRequest = {}): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getCatalogs({
+      sessionHandle: this.sessionHandle,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Get list of schemas
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getSchemas(request: SchemasRequest = {}): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getSchemas({
+      sessionHandle: this.sessionHandle,
+      catalogName: request.catalogName,
+      schemaName: request.schemaName,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Get list of tables
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getTables(request: TablesRequest = {}): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getTables({
+      sessionHandle: this.sessionHandle,
+      catalogName: request.catalogName,
+      schemaName: request.schemaName,
+      tableName: request.tableName,
+      tableTypes: request.tableTypes,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Get list of supported table types
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getTableTypes(request: TableTypesRequest = {}): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getTableTypes({
+      sessionHandle: this.sessionHandle,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Get full information about columns of the table
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getColumns(request: ColumnsRequest = {}): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getColumns({
+      sessionHandle: this.sessionHandle,
+      catalogName: request.catalogName,
+      schemaName: request.schemaName,
+      tableName: request.tableName,
+      columnName: request.columnName,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Get information about function
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getFunctions(request: FunctionsRequest): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getFunctions({
+      sessionHandle: this.sessionHandle,
+      catalogName: request.catalogName,
+      schemaName: request.schemaName,
+      functionName: request.functionName,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  public async getPrimaryKeys(request: PrimaryKeysRequest): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getPrimaryKeys({
+      sessionHandle: this.sessionHandle,
+      catalogName: request.catalogName,
+      schemaName: request.schemaName,
+      tableName: request.tableName,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Request information about foreign keys between two tables
+   * @public
+   * @param request
+   * @returns DBSQLOperation
+   */
+  public async getCrossReference(request: CrossReferenceRequest): Promise<IOperation> {
+    await this.failIfClosed();
+    const driver = await this.context.getDriver();
+    const clientConfig = this.context.getConfig();
+ 
+    const operationPromise = driver.getCrossReference({
+      sessionHandle: this.sessionHandle,
+      parentCatalogName: request.parentCatalogName,
+      parentSchemaName: request.parentSchemaName,
+      parentTableName: request.parentTableName,
+      foreignCatalogName: request.foreignCatalogName,
+      foreignSchemaName: request.foreignSchemaName,
+      foreignTableName: request.foreignTableName,
+      runAsync: this.getRunAsyncForMetadataOperations(),
+      ...getDirectResultsOptions(request.maxRows, clientConfig),
+    });
+    const response = await this.handleResponse(operationPromise);
+    return this.createOperation(response);
+  }
+ 
+  /**
+   * Closes the session
+   * @public
+   * @returns Operation status
+   */
+  public async close(): Promise<Status> {
+    if (!this.isOpen) {
+      return Status.success();
+    }
+ 
+    // Close owned operations one by one, removing successfully closed ones from the list
+    await this.operations.closeAll();
+ 
+    const driver = await this.context.getDriver();
+    const response = await driver.closeSession({
+      sessionHandle: this.sessionHandle,
+    });
+    // check status for being successful
+    Status.assert(response.status);
+ 
+    // notify owner connection
+    this.onClose?.();
+    this.isOpen = false;
+ 
+    this.context.getLogger().log(LogLevel.debug, `Session closed with id: ${this.id}`);
+    return new Status(response.status);
+  }
+ 
+  private createOperation(response: OperationResponseShape): DBSQLOperation {
+    Status.assert(response.status);
+    const handle = definedOrError(response.operationHandle);
+    const operation = new DBSQLOperation({
+      handle,
+      directResults: response.directResults,
+      context: this.context,
+    });
+ 
+    this.operations.add(operation);
+ 
+    return operation;
+  }
+ 
+  private async failIfClosed(): Promise<void> {
+    if (!this.isOpen) {
+      throw new HiveDriverError('The session was closed or has expired');
+    }
+  }
+ 
+  private async handleResponse<T>(requestPromise: Promise<T>): Promise<T> {
+    // Currently, after being closed sessions remains usable - server will not
+    // error out when trying to run operations on closed session. So it's
+    // basically useless to process any errors here
+    const result = await requestPromise;
+    await this.failIfClosed();
+    return result;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html new file mode 100644 index 00000000..ad593bb1 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html @@ -0,0 +1,658 @@ + + + + + + Code coverage report for lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/DatabricksOAuth AuthorizationCode.ts

+
+ +
+ 9.72% + Statements + 7/72 +
+ + +
+ 0% + Branches + 0/23 +
+ + +
+ 0% + Functions + 0/18 +
+ + +
+ 9.72% + Lines + 7/72 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +1921x +1x +1x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import http, { IncomingMessage, Server, ServerResponse } from 'http';
+import { BaseClient, CallbackParamsType, generators } from 'openid-client';
+import open from 'open';
+import { LogLevel } from '../../../contracts/IDBSQLLogger';
+import { OAuthScopes, scopeDelimiter } from './OAuthScope';
+import IClientContext from '../../../contracts/IClientContext';
+import AuthenticationError from '../../../errors/AuthenticationError';
+ 
+export type DefaultOpenAuthUrlCallback = (authUrl: string) => Promise<void>;
+ 
+export type OpenAuthUrlCallback = (authUrl: string, defaultOpenAuthUrl: DefaultOpenAuthUrlCallback) => Promise<void>;
+ 
+export interface AuthorizationCodeOptions {
+  client: BaseClient;
+  ports: Array<number>;
+  context: IClientContext;
+  openAuthUrl?: OpenAuthUrlCallback;
+}
+ 
+async function defaultOpenAuthUrl(authUrl: string): Promise<void> {
+  await open(authUrl);
+}
+ 
+export interface AuthorizationCodeFetchResult {
+  code: string;
+  verifier: string;
+  redirectUri: string;
+}
+ 
+export default class AuthorizationCode {
+  private readonly context: IClientContext;
+ 
+  private readonly client: BaseClient;
+ 
+  private readonly host: string = 'localhost';
+ 
+  private readonly options: AuthorizationCodeOptions;
+ 
+  constructor(options: AuthorizationCodeOptions) {
+    this.client = options.client;
+    this.context = options.context;
+    this.options = options;
+  }
+ 
+  public async fetch(scopes: OAuthScopes): Promise<AuthorizationCodeFetchResult> {
+    const verifierString = generators.codeVerifier(32);
+    const challengeString = generators.codeChallenge(verifierString);
+    const state = generators.state(16);
+ 
+    let receivedParams: CallbackParamsType | undefined;
+ 
+    const server = await this.createServer((req, res) => {
+      const params = this.client.callbackParams(req);
+      if (params.state === state) {
+        receivedParams = params;
+        res.writeHead(200);
+        res.end(this.renderCallbackResponse());
+        server.stop();
+      } else {
+        res.writeHead(404);
+        res.end();
+      }
+    });
+ 
+    const redirectUri = `http://${server.host}:${server.port}`;
+    const authUrl = this.client.authorizationUrl({
+      response_type: 'code',
+      response_mode: 'query',
+      scope: scopes.join(scopeDelimiter),
+      code_challenge: challengeString,
+      code_challenge_method: 'S256',
+      state,
+      redirect_uri: redirectUri,
+    });
+ 
+    const openAuthUrl = this.options.openAuthUrl ?? defaultOpenAuthUrl;
+    await openAuthUrl(authUrl, defaultOpenAuthUrl);
+    await server.stopped();
+ 
+    if (!receivedParams || !receivedParams.code) {
+      if (receivedParams?.error) {
+        const errorMessage = `OAuth error: ${receivedParams.error} ${receivedParams.error_description}`;
+        throw new AuthenticationError(errorMessage);
+      }
+      throw new AuthenticationError(`No path parameters were returned to the callback at ${redirectUri}`);
+    }
+ 
+    return { code: receivedParams.code, verifier: verifierString, redirectUri };
+  }
+ 
+  private async createServer(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) {
+    for (const port of this.options.ports) {
+      const host = this.host; // eslint-disable-line prefer-destructuring
+      try {
+        const server = await this.startServer(host, port, requestHandler); // eslint-disable-line no-await-in-loop
+        this.context.getLogger().log(LogLevel.info, `Listening for OAuth authorization callback at ${host}:${port}`);
+ 
+        let resolveStopped: () => void;
+        let rejectStopped: (reason?: any) => void;
+        const stoppedPromise = new Promise<void>((resolve, reject) => {
+          resolveStopped = resolve;
+          rejectStopped = reject;
+        });
+ 
+        return {
+          host,
+          port,
+          server,
+          stop: () => this.stopServer(server).then(resolveStopped).catch(rejectStopped),
+          stopped: () => stoppedPromise,
+        };
+      } catch (error) {
+        // if port already in use - try another one, otherwise re-throw an exception
+        if (error instanceof Error && 'code' in error && error.code === 'EADDRINUSE') {
+          this.context.getLogger().log(LogLevel.debug, `Failed to start server at ${host}:${port}: ${error.code}`);
+        } else {
+          throw error;
+        }
+      }
+    }
+ 
+    throw new AuthenticationError('Failed to start server: all ports are in use');
+  }
+ 
+  private createHttpServer(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) {
+    return http.createServer(requestHandler);
+  }
+ 
+  private async startServer(
+    host: string,
+    port: number,
+    requestHandler: (req: IncomingMessage, res: ServerResponse) => void,
+  ): Promise<Server> {
+    const server = this.createHttpServer(requestHandler);
+ 
+    return new Promise((resolve, reject) => {
+      const errorListener = (error: Error) => {
+        server.off('error', errorListener);
+        reject(error);
+      };
+ 
+      server.on('error', errorListener);
+      server.listen(port, host, () => {
+        server.off('error', errorListener);
+        resolve(server);
+      });
+    });
+  }
+ 
+  private async stopServer(server: Server): Promise<void> {
+    if (!server.listening) {
+      return;
+    }
+ 
+    return new Promise((resolve, reject) => {
+      const errorListener = (error: Error) => {
+        server.off('error', errorListener);
+        reject(error);
+      };
+ 
+      server.on('error', errorListener);
+      server.close(() => {
+        server.off('error', errorListener);
+        resolve();
+      });
+    });
+  }
+ 
+  private renderCallbackResponse(): string {
+    const applicationName = 'Databricks Sql Connector';
+ 
+    return `<html lang="en">
+<head>
+  <title>Close this Tab</title>
+  <style>
+    body {
+      font-family: "Barlow", Helvetica, Arial, sans-serif;
+      padding: 20px;
+      background-color: #f3f3f3;
+    }
+  </style>
+</head>
+<body>
+  <h1>Please close this tab.</h1>
+  <p>
+    The ${applicationName} received a response. You may close this tab.
+  </p>
+</body>
+</html>`;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html new file mode 100644 index 00000000..dacd09bb --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html @@ -0,0 +1,1036 @@ + + + + + + Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthManager.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/DatabricksOAuth OAuthManager.ts

+
+ +
+ 14.65% + Statements + 17/116 +
+ + +
+ 2.94% + Branches + 2/68 +
+ + +
+ 3.84% + Functions + 1/26 +
+ + +
+ 15.45% + Lines + 17/110 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318  +1x +1x +1x +1x +1x +1x +  +  +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import http from 'http';
+import { Issuer, BaseClient, custom } from 'openid-client';
+import AuthenticationError from '../../../errors/AuthenticationError';
+import { LogLevel } from '../../../contracts/IDBSQLLogger';
+import OAuthToken from './OAuthToken';
+import AuthorizationCode from './AuthorizationCode';
+import { OAuthScope, OAuthScopes, scopeDelimiter } from './OAuthScope';
+import IClientContext from '../../../contracts/IClientContext';
+ 
+export enum OAuthFlow {
+  U2M = 'U2M',
+  M2M = 'M2M',
+}
+ 
+export interface OAuthManagerOptions {
+  flow: OAuthFlow;
+  host: string;
+  callbackPorts?: Array<number>;
+  clientId?: string;
+  azureTenantId?: string;
+  clientSecret?: string;
+  useDatabricksOAuthInAzure?: boolean;
+  context: IClientContext;
+}
+ 
+function getDatabricksOIDCUrl(host: string): string {
+  const schema = host.startsWith('https://') ? '' : 'https://';
+  const trailingSlash = host.endsWith('/') ? '' : '/';
+  return `${schema}${host}${trailingSlash}oidc`;
+}
+ 
+export default abstract class OAuthManager {
+  protected readonly context: IClientContext;
+ 
+  protected readonly options: OAuthManagerOptions;
+ 
+  protected agent?: http.Agent;
+ 
+  protected issuer?: Issuer;
+ 
+  protected client?: BaseClient;
+ 
+  constructor(options: OAuthManagerOptions) {
+    this.options = options;
+    this.context = options.context;
+  }
+ 
+  protected abstract getOIDCConfigUrl(): string;
+ 
+  protected abstract getAuthorizationUrl(): string;
+ 
+  protected abstract getClientId(): string;
+ 
+  protected abstract getCallbackPorts(): Array<number>;
+ 
+  protected abstract getScopes(requestedScopes: OAuthScopes): OAuthScopes;
+ 
+  protected async getClient(): Promise<BaseClient> {
+    // Obtain http agent each time when we need an OAuth client
+    // to ensure that we always use a valid agent instance
+    const connectionProvider = await this.context.getConnectionProvider();
+    this.agent = await connectionProvider.getAgent();
+ 
+    const getHttpOptions = () => ({
+      agent: this.agent,
+    });
+ 
+    if (!this.issuer) {
+      // To use custom http agent in Issuer.discover(), we'd have to set Issuer[custom.http_options].
+      // However, that's a static field, and if multiple instances of OAuthManager used, race condition
+      // may occur when they simultaneously override that field and then try to use Issuer.discover().
+      // Therefore we create a local class derived from Issuer, and set that field for it, thus making
+      // sure that it will not interfere with other instances (or other code that may use Issuer)
+      class CustomIssuer extends Issuer {
+        static [custom.http_options] = getHttpOptions;
+      }
+ 
+      const issuer = await CustomIssuer.discover(this.getOIDCConfigUrl());
+ 
+      // Overwrite `authorization_endpoint` in default config (specifically needed for Azure flow
+      // where this URL has to be different)
+      this.issuer = new Issuer({
+        ...issuer.metadata,
+        authorization_endpoint: this.getAuthorizationUrl(),
+      });
+ 
+      this.issuer[custom.http_options] = getHttpOptions;
+    }
+ 
+    if (!this.client) {
+      this.client = new this.issuer.Client({
+        client_id: this.getClientId(),
+        client_secret: this.options.clientSecret,
+        token_endpoint_auth_method: this.options.clientSecret === undefined ? 'none' : 'client_secret_basic',
+      });
+ 
+      this.client[custom.http_options] = getHttpOptions;
+    }
+ 
+    return this.client;
+  }
+ 
+  private async refreshAccessTokenU2M(token: OAuthToken): Promise<OAuthToken> {
+    if (!token.refreshToken) {
+      const message = `OAuth access token expired on ${token.expirationTime}.`;
+      this.context.getLogger().log(LogLevel.error, message);
+      throw new AuthenticationError(message);
+    }
+ 
+    // Try to refresh using the refresh token
+    this.context
+      .getLogger()
+      .log(LogLevel.debug, `Attempting to refresh OAuth access token that expired on ${token.expirationTime}`);
+ 
+    const client = await this.getClient();
+    const { access_token: accessToken, refresh_token: refreshToken } = await client.refresh(token.refreshToken);
+    if (!accessToken || !refreshToken) {
+      throw new AuthenticationError('Failed to refresh token: invalid response');
+    }
+    return new OAuthToken(accessToken, refreshToken, token.scopes);
+  }
+ 
+  private async refreshAccessTokenM2M(token: OAuthToken): Promise<OAuthToken> {
+    return this.getTokenM2M(token.scopes ?? []);
+  }
+ 
+  public async refreshAccessToken(token: OAuthToken): Promise<OAuthToken> {
+    try {
+      if (!token.hasExpired) {
+        // The access token is fine. Just return it.
+        return token;
+      }
+    } catch (error) {
+      this.context.getLogger().log(LogLevel.error, `${error}`);
+      throw error;
+    }
+ 
+    switch (this.options.flow) {
+      case OAuthFlow.U2M:
+        return this.refreshAccessTokenU2M(token);
+      case OAuthFlow.M2M:
+        return this.refreshAccessTokenM2M(token);
+      // no default
+    }
+  }
+ 
+  private async getTokenU2M(scopes: OAuthScopes): Promise<OAuthToken> {
+    const client = await this.getClient();
+ 
+    const authCode = new AuthorizationCode({
+      client,
+      ports: this.getCallbackPorts(),
+      context: this.context,
+    });
+ 
+    const mappedScopes = this.getScopes(scopes);
+ 
+    const { code, verifier, redirectUri } = await authCode.fetch(mappedScopes);
+ 
+    const { access_token: accessToken, refresh_token: refreshToken } = await client.grant({
+      grant_type: 'authorization_code',
+      code,
+      code_verifier: verifier,
+      redirect_uri: redirectUri,
+    });
+ 
+    if (!accessToken) {
+      throw new AuthenticationError('Failed to fetch access token');
+    }
+    return new OAuthToken(accessToken, refreshToken, mappedScopes);
+  }
+ 
+  private async getTokenM2M(scopes: OAuthScopes): Promise<OAuthToken> {
+    const client = await this.getClient();
+ 
+    const mappedScopes = this.getScopes(scopes);
+ 
+    // M2M flow doesn't really support token refreshing, and refresh should not be available
+    // in response. Each time access token expires, client can just acquire a new one using
+    // client secret. Here we explicitly return access token only as a sign that we're not going
+    // to use refresh token for M2M flow anywhere later
+    const { access_token: accessToken } = await client.grant({
+      grant_type: 'client_credentials',
+      scope: mappedScopes.join(scopeDelimiter),
+    });
+ 
+    if (!accessToken) {
+      throw new AuthenticationError('Failed to fetch access token');
+    }
+    return new OAuthToken(accessToken, undefined, mappedScopes);
+  }
+ 
+  public async getToken(scopes: OAuthScopes): Promise<OAuthToken> {
+    switch (this.options.flow) {
+      case OAuthFlow.U2M:
+        return this.getTokenU2M(scopes);
+      case OAuthFlow.M2M:
+        return this.getTokenM2M(scopes);
+      // no default
+    }
+  }
+ 
+  public static getManager(options: OAuthManagerOptions): OAuthManager {
+    // normalize
+    const host = options.host.toLowerCase().replace('https://', '').split('/')[0];
+ 
+    const awsDomains = ['.cloud.databricks.com', '.dev.databricks.com'];
+    const isAWSDomain = awsDomains.some((domain) => host.endsWith(domain));
+    if (isAWSDomain) {
+      // eslint-disable-next-line @typescript-eslint/no-use-before-define
+      return new DatabricksOAuthManager(options);
+    }
+ 
+    const gcpDomains = ['.gcp.databricks.com'];
+    const isGCPDomain = gcpDomains.some((domain) => host.endsWith(domain));
+    if (isGCPDomain) {
+      // eslint-disable-next-line @typescript-eslint/no-use-before-define
+      return new DatabricksOAuthManager(options);
+    }
+ 
+    if (options.useDatabricksOAuthInAzure) {
+      const azureDomains = ['.azuredatabricks.net', '.databricks.azure.cn'];
+      const isAzureDomain = azureDomains.some((domain) => host.endsWith(domain));
+      if (isAzureDomain) {
+        // eslint-disable-next-line @typescript-eslint/no-use-before-define
+        return new DatabricksOAuthManager(options);
+      }
+    } else {
+      const azureDomains = ['.azuredatabricks.net', '.databricks.azure.us', '.databricks.azure.cn'];
+      const isAzureDomain = azureDomains.some((domain) => host.endsWith(domain));
+      if (isAzureDomain) {
+        // eslint-disable-next-line @typescript-eslint/no-use-before-define
+        return new AzureOAuthManager(options);
+      }
+    }
+ 
+    throw new AuthenticationError(`OAuth is not supported for ${options.host}`);
+  }
+}
+ 
+// Databricks InHouse OAuth Manager
+export class DatabricksOAuthManager extends OAuthManager {
+  public static defaultClientId = 'databricks-sql-connector';
+ 
+  public static defaultCallbackPorts = [8030];
+ 
+  protected getOIDCConfigUrl(): string {
+    return `${getDatabricksOIDCUrl(this.options.host)}/.well-known/oauth-authorization-server`;
+  }
+ 
+  protected getAuthorizationUrl(): string {
+    return `${getDatabricksOIDCUrl(this.options.host)}/oauth2/v2.0/authorize`;
+  }
+ 
+  protected getClientId(): string {
+    return this.options.clientId ?? DatabricksOAuthManager.defaultClientId;
+  }
+ 
+  protected getCallbackPorts(): Array<number> {
+    return this.options.callbackPorts ?? DatabricksOAuthManager.defaultCallbackPorts;
+  }
+ 
+  protected getScopes(requestedScopes: OAuthScopes): OAuthScopes {
+    if (this.options.flow === OAuthFlow.M2M) {
+      // this is the only allowed scope for M2M flow
+      return [OAuthScope.allAPIs];
+    }
+    return requestedScopes;
+  }
+}
+ 
+export class AzureOAuthManager extends OAuthManager {
+  public static defaultClientId = '96eecda7-19ea-49cc-abb5-240097d554f5';
+ 
+  public static defaultCallbackPorts = [8030];
+ 
+  public static datatricksAzureApp = '2ff814a6-3304-4ab8-85cb-cd0e6f879c1d';
+ 
+  protected getOIDCConfigUrl(): string {
+    return 'https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration';
+  }
+ 
+  protected getAuthorizationUrl(): string {
+    return `${getDatabricksOIDCUrl(this.options.host)}/oauth2/v2.0/authorize`;
+  }
+ 
+  protected getClientId(): string {
+    return this.options.clientId ?? AzureOAuthManager.defaultClientId;
+  }
+ 
+  protected getCallbackPorts(): Array<number> {
+    return this.options.callbackPorts ?? AzureOAuthManager.defaultCallbackPorts;
+  }
+ 
+  protected getScopes(requestedScopes: OAuthScopes): OAuthScopes {
+    // There is no corresponding scopes in Azure, instead, access control will be delegated to Databricks
+    const tenantId = this.options.azureTenantId ?? AzureOAuthManager.datatricksAzureApp;
+ 
+    const azureScopes = [];
+ 
+    switch (this.options.flow) {
+      case OAuthFlow.U2M:
+        azureScopes.push(`${tenantId}/user_impersonation`);
+        break;
+      case OAuthFlow.M2M:
+        azureScopes.push(`${tenantId}/.default`);
+        break;
+      // no default
+    }
+ 
+    if (requestedScopes.includes(OAuthScope.offlineAccess)) {
+      azureScopes.push(OAuthScope.offlineAccess);
+    }
+ 
+    return azureScopes;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html new file mode 100644 index 00000000..76fb812e --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html @@ -0,0 +1,142 @@ + + + + + + Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/DatabricksOAuth OAuthPersistence.ts

+
+ +
+ 25% + Statements + 1/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 25% + Lines + 1/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  + 
import OAuthToken from './OAuthToken';
+ 
+export default interface OAuthPersistence {
+  persist(host: string, token: OAuthToken): Promise<void>;
+ 
+  read(host: string): Promise<OAuthToken | undefined>;
+}
+ 
+export class OAuthPersistenceCache implements OAuthPersistence {
+  private tokens: Record<string, OAuthToken | undefined> = {};
+ 
+  async persist(host: string, token: OAuthToken) {
+    this.tokens[host] = token;
+  }
+ 
+  async read(host: string) {
+    return this.tokens[host];
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html new file mode 100644 index 00000000..0b965e0a --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html @@ -0,0 +1,118 @@ + + + + + + Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthScope.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/DatabricksOAuth OAuthScope.ts

+
+ +
+ 100% + Statements + 6/6 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 6/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +121x +1x +1x +1x +  +  +  +  +1x +  +1x + 
export enum OAuthScope {
+  offlineAccess = 'offline_access',
+  SQL = 'sql',
+  allAPIs = 'all-apis',
+}
+ 
+export type OAuthScopes = Array<string>;
+ 
+export const defaultOAuthScopes: OAuthScopes = [OAuthScope.SQL, OAuthScope.offlineAccess];
+ 
+export const scopeDelimiter = ' ';
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html new file mode 100644 index 00000000..c6e74dab --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html @@ -0,0 +1,226 @@ + + + + + + Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthToken.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/DatabricksOAuth OAuthToken.ts

+
+ +
+ 7.14% + Statements + 1/14 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 7.14% + Lines + 1/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { OAuthScopes } from './OAuthScope';
+ 
+export default class OAuthToken {
+  private readonly _accessToken: string;
+ 
+  private readonly _refreshToken?: string;
+ 
+  private readonly _scopes?: OAuthScopes;
+ 
+  private _expirationTime?: number;
+ 
+  constructor(accessToken: string, refreshToken?: string, scopes?: OAuthScopes) {
+    this._accessToken = accessToken;
+    this._refreshToken = refreshToken;
+    this._scopes = scopes;
+  }
+ 
+  get accessToken(): string {
+    return this._accessToken;
+  }
+ 
+  get refreshToken(): string | undefined {
+    return this._refreshToken;
+  }
+ 
+  get scopes(): OAuthScopes | undefined {
+    return this._scopes;
+  }
+ 
+  get expirationTime(): number {
+    // This token has already been verified, and we are just parsing it.
+    // If it has been tampered with, it will be rejected on the server side.
+    // This avoids having to fetch the public key from the issuer and perform
+    // an unnecessary signature verification.
+    if (this._expirationTime === undefined) {
+      const accessTokenPayload = Buffer.from(this._accessToken.split('.')[1], 'base64').toString('utf8');
+      const decoded = JSON.parse(accessTokenPayload);
+      this._expirationTime = Number(decoded.exp);
+    }
+    return this._expirationTime;
+  }
+ 
+  get hasExpired(): boolean {
+    const now = Math.floor(Date.now() / 1000); // convert it to seconds
+    return this.expirationTime <= now;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html new file mode 100644 index 00000000..a1e28835 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html @@ -0,0 +1,191 @@ + + + + + + Code coverage report for lib/connection/auth/DatabricksOAuth + + + + + + + + + +
+
+

All files lib/connection/auth/DatabricksOAuth

+
+ +
+ 16.01% + Statements + 37/231 +
+ + +
+ 3.73% + Branches + 4/107 +
+ + +
+ 3.44% + Functions + 2/58 +
+ + +
+ 16.44% + Lines + 37/225 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AuthorizationCode.ts +
+
9.72%7/720%0/230%0/189.72%7/72
OAuthManager.ts +
+
14.65%17/1162.94%2/683.84%1/2615.45%17/110
OAuthPersistence.ts +
+
25%1/4100%0/00%0/325%1/4
OAuthScope.ts +
+
100%6/6100%2/2100%1/1100%6/6
OAuthToken.ts +
+
7.14%1/140%0/20%0/67.14%1/14
index.ts +
+
26.31%5/190%0/120%0/426.31%5/19
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html new file mode 100644 index 00000000..15938aba --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html @@ -0,0 +1,250 @@ + + + + + + Code coverage report for lib/connection/auth/DatabricksOAuth/index.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/DatabricksOAuth index.ts

+
+ +
+ 26.31% + Statements + 5/19 +
+ + +
+ 0% + Branches + 0/12 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 26.31% + Lines + 5/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56  +  +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { HeadersInit } from 'node-fetch';
+import IAuthentication from '../../contracts/IAuthentication';
+import OAuthPersistence, { OAuthPersistenceCache } from './OAuthPersistence';
+import OAuthManager, { OAuthManagerOptions, OAuthFlow } from './OAuthManager';
+import { OAuthScopes, defaultOAuthScopes } from './OAuthScope';
+import IClientContext from '../../../contracts/IClientContext';
+ 
+export { OAuthFlow };
+ 
+export interface DatabricksOAuthOptions extends OAuthManagerOptions {
+  scopes?: OAuthScopes;
+  persistence?: OAuthPersistence;
+  headers?: HeadersInit;
+}
+ 
+export default class DatabricksOAuth implements IAuthentication {
+  private readonly context: IClientContext;
+ 
+  private readonly options: DatabricksOAuthOptions;
+ 
+  private manager?: OAuthManager;
+ 
+  private readonly defaultPersistence = new OAuthPersistenceCache();
+ 
+  constructor(options: DatabricksOAuthOptions) {
+    this.context = options.context;
+    this.options = options;
+  }
+ 
+  public async authenticate(): Promise<HeadersInit> {
+    const { host, scopes, headers } = this.options;
+ 
+    const persistence = this.options.persistence ?? this.defaultPersistence;
+ 
+    let token = await persistence.read(host);
+    if (!token) {
+      token = await this.getManager().getToken(scopes ?? defaultOAuthScopes);
+    }
+ 
+    token = await this.getManager().refreshAccessToken(token);
+    await persistence.persist(host, token);
+ 
+    return {
+      ...headers,
+      Authorization: `Bearer ${token.accessToken}`,
+    };
+  }
+ 
+  private getManager(): OAuthManager {
+    if (!this.manager) {
+      this.manager = OAuthManager.getManager(this.options);
+    }
+    return this.manager;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html b/coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html new file mode 100644 index 00000000..78a38ff2 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html @@ -0,0 +1,187 @@ + + + + + + Code coverage report for lib/connection/auth/PlainHttpAuthentication.ts + + + + + + + + + +
+
+

All files / lib/connection/auth PlainHttpAuthentication.ts

+
+ +
+ 16.66% + Statements + 1/6 +
+ + +
+ 0% + Branches + 0/20 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 16.66% + Lines + 1/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { HeadersInit } from 'node-fetch';
+import IAuthentication from '../contracts/IAuthentication';
+import IClientContext from '../../contracts/IClientContext';
+ 
+interface PlainHttpAuthenticationOptions {
+  username?: string;
+  password?: string;
+  headers?: HeadersInit;
+  context: IClientContext;
+}
+ 
+export default class PlainHttpAuthentication implements IAuthentication {
+  private readonly context: IClientContext;
+ 
+  private readonly username: string;
+ 
+  private readonly password: string;
+ 
+  private readonly headers: HeadersInit;
+ 
+  constructor(options: PlainHttpAuthenticationOptions) {
+    this.context = options.context;
+    this.username = options?.username || 'anonymous';
+    this.password = options?.password ?? 'anonymous';
+    this.headers = options?.headers || {};
+  }
+ 
+  public async authenticate(): Promise<HeadersInit> {
+    return {
+      ...this.headers,
+      Authorization: `Bearer ${this.password}`,
+    };
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/index.html b/coverage/lcov-report/lib/connection/auth/index.html new file mode 100644 index 00000000..ec2fc87e --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for lib/connection/auth + + + + + + + + + +
+
+

All files lib/connection/auth

+
+ +
+ 16.66% + Statements + 1/6 +
+ + +
+ 0% + Branches + 0/20 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 16.66% + Lines + 1/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
PlainHttpAuthentication.ts +
+
16.66%1/60%0/200%0/216.66%1/6
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html new file mode 100644 index 00000000..2bded7fb --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html @@ -0,0 +1,379 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/CachedTokenProvider.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider CachedTokenProvider.ts

+
+ +
+ 7.69% + Statements + 2/26 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 7.69% + Lines + 2/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99  +  +  +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import ITokenProvider from './ITokenProvider';
+import Token from './Token';
+ 
+/**
+ * Default refresh threshold in milliseconds (5 minutes).
+ * Tokens will be refreshed when they are within this threshold of expiring.
+ */
+const DEFAULT_REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
+ 
+/**
+ * A token provider that wraps another provider with automatic caching.
+ * Tokens are cached and reused until they are close to expiring.
+ */
+export default class CachedTokenProvider implements ITokenProvider {
+  private readonly baseProvider: ITokenProvider;
+ 
+  private readonly refreshThresholdMs: number;
+ 
+  private cache: Token | null = null;
+ 
+  private refreshPromise: Promise<Token> | null = null;
+ 
+  /**
+   * Creates a new CachedTokenProvider.
+   * @param baseProvider - The underlying token provider to cache
+   * @param options - Optional configuration
+   * @param options.refreshThresholdMs - Refresh tokens this many ms before expiry (default: 5 minutes)
+   */
+  constructor(
+    baseProvider: ITokenProvider,
+    options?: {
+      refreshThresholdMs?: number;
+    },
+  ) {
+    this.baseProvider = baseProvider;
+    this.refreshThresholdMs = options?.refreshThresholdMs ?? DEFAULT_REFRESH_THRESHOLD_MS;
+  }
+ 
+  async getToken(): Promise<Token> {
+    // Return cached token if it's still valid
+    if (this.cache && !this.shouldRefresh(this.cache)) {
+      return this.cache;
+    }
+ 
+    // If already refreshing, wait for that to complete
+    if (this.refreshPromise) {
+      return this.refreshPromise;
+    }
+ 
+    // Start refresh
+    this.refreshPromise = this.refreshToken();
+ 
+    try {
+      const token = await this.refreshPromise;
+      return token;
+    } finally {
+      this.refreshPromise = null;
+    }
+  }
+ 
+  getName(): string {
+    return `cached[${this.baseProvider.getName()}]`;
+  }
+ 
+  /**
+   * Clears the cached token, forcing a refresh on the next getToken() call.
+   */
+  clearCache(): void {
+    this.cache = null;
+  }
+ 
+  /**
+   * Determines if the token should be refreshed.
+   * @param token - The token to check
+   * @returns true if the token should be refreshed
+   */
+  private shouldRefresh(token: Token): boolean {
+    // If no expiration is known, don't refresh proactively
+    if (!token.expiresAt) {
+      return false;
+    }
+ 
+    const now = Date.now();
+    const expiresAtMs = token.expiresAt.getTime();
+    const refreshAtMs = expiresAtMs - this.refreshThresholdMs;
+ 
+    return now >= refreshAtMs;
+  }
+ 
+  /**
+   * Fetches a new token from the base provider and caches it.
+   */
+  private async refreshToken(): Promise<Token> {
+    const token = await this.baseProvider.getToken();
+    this.cache = token;
+    return token;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html new file mode 100644 index 00000000..99c33c49 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/ExternalTokenProvider.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider ExternalTokenProvider.ts

+
+ +
+ 20% + Statements + 2/10 +
+ + +
+ 0% + Branches + 0/18 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 20% + Lines + 2/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53  +1x +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import ITokenProvider from './ITokenProvider';
+import Token from './Token';
+ 
+/**
+ * Type for the callback function that retrieves tokens from external sources.
+ */
+export type TokenCallback = () => Promise<string>;
+ 
+/**
+ * A token provider that delegates token retrieval to an external callback function.
+ * Useful for integrating with secret managers, vaults, or other token sources.
+ */
+export default class ExternalTokenProvider implements ITokenProvider {
+  private readonly getTokenCallback: TokenCallback;
+ 
+  private readonly parseJWT: boolean;
+ 
+  private readonly providerName: string;
+ 
+  /**
+   * Creates a new ExternalTokenProvider.
+   * @param getToken - Callback function that returns the access token string
+   * @param options - Optional configuration
+   * @param options.parseJWT - If true, attempt to extract expiration from JWT payload (default: true)
+   * @param options.name - Custom name for this provider (default: "ExternalTokenProvider")
+   */
+  constructor(
+    getToken: TokenCallback,
+    options?: {
+      parseJWT?: boolean;
+      name?: string;
+    },
+  ) {
+    this.getTokenCallback = getToken;
+    this.parseJWT = options?.parseJWT ?? true;
+    this.providerName = options?.name ?? 'ExternalTokenProvider';
+  }
+ 
+  async getToken(): Promise<Token> {
+    const accessToken = await this.getTokenCallback();
+ 
+    if (this.parseJWT) {
+      return Token.fromJWT(accessToken);
+    }
+ 
+    return new Token(accessToken);
+  }
+ 
+  getName(): string {
+    return this.providerName;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html new file mode 100644 index 00000000..54f2c70d --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html @@ -0,0 +1,889 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/FederationProvider.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider FederationProvider.ts

+
+ +
+ 16% + Statements + 12/75 +
+ + +
+ 0% + Branches + 0/46 +
+ + +
+ 0% + Functions + 0/12 +
+ + +
+ 16.21% + Lines + 12/74 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +2691x +  +1x +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import fetch from 'node-fetch';
+import ITokenProvider from './ITokenProvider';
+import Token from './Token';
+import { getJWTIssuer, isSameHost } from './utils';
+ 
+/**
+ * Token exchange endpoint path for Databricks OIDC.
+ */
+const TOKEN_EXCHANGE_ENDPOINT = '/oidc/v1/token';
+ 
+/**
+ * Grant type for RFC 8693 token exchange.
+ */
+const TOKEN_EXCHANGE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange';
+ 
+/**
+ * Subject token type for JWT tokens.
+ */
+const SUBJECT_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:jwt';
+ 
+/**
+ * Default scope for SQL operations.
+ */
+const DEFAULT_SCOPE = 'sql';
+ 
+/**
+ * Timeout for token exchange requests in milliseconds.
+ */
+const REQUEST_TIMEOUT_MS = 30000;
+ 
+/**
+ * Maximum number of retry attempts for transient errors.
+ */
+const MAX_RETRY_ATTEMPTS = 3;
+ 
+/**
+ * Base delay in milliseconds for exponential backoff.
+ */
+const RETRY_BASE_DELAY_MS = 1000;
+ 
+/**
+ * HTTP status codes that are considered retryable.
+ */
+const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
+ 
+/**
+ * Error class for token exchange failures that includes the HTTP status code.
+ */
+class TokenExchangeError extends Error {
+  readonly statusCode: number;
+ 
+  constructor(message: string, statusCode: number) {
+    super(message);
+    this.name = 'TokenExchangeError';
+    this.statusCode = statusCode;
+  }
+}
+ 
+/**
+ * A token provider that wraps another provider with automatic token federation.
+ * When the base provider returns a token from a different issuer, this provider
+ * exchanges it for a Databricks-compatible token using RFC 8693.
+ */
+export default class FederationProvider implements ITokenProvider {
+  private readonly baseProvider: ITokenProvider;
+ 
+  private readonly databricksHost: string;
+ 
+  private readonly clientId?: string;
+ 
+  private readonly returnOriginalTokenOnFailure: boolean;
+ 
+  /**
+   * Creates a new FederationProvider.
+   * @param baseProvider - The underlying token provider
+   * @param databricksHost - The Databricks workspace host URL
+   * @param options - Optional configuration
+   * @param options.clientId - Client ID for M2M/service principal federation
+   * @param options.returnOriginalTokenOnFailure - Return original token if exchange fails (default: true)
+   */
+  constructor(
+    baseProvider: ITokenProvider,
+    databricksHost: string,
+    options?: {
+      clientId?: string;
+      returnOriginalTokenOnFailure?: boolean;
+    },
+  ) {
+    this.baseProvider = baseProvider;
+    this.databricksHost = databricksHost;
+    this.clientId = options?.clientId;
+    this.returnOriginalTokenOnFailure = options?.returnOriginalTokenOnFailure ?? true;
+  }
+ 
+  async getToken(): Promise<Token> {
+    const token = await this.baseProvider.getToken();
+ 
+    // Check if token needs exchange
+    if (!this.needsTokenExchange(token)) {
+      return token;
+    }
+ 
+    // Attempt token exchange
+    try {
+      return await this.exchangeToken(token);
+    } catch (error) {
+      if (this.returnOriginalTokenOnFailure) {
+        // Fall back to original token
+        return token;
+      }
+      throw error;
+    }
+  }
+ 
+  getName(): string {
+    return `federated[${this.baseProvider.getName()}]`;
+  }
+ 
+  /**
+   * Determines if the token needs to be exchanged.
+   * @param token - The token to check
+   * @returns true if the token should be exchanged
+   */
+  private needsTokenExchange(token: Token): boolean {
+    const issuer = getJWTIssuer(token.accessToken);
+ 
+    // If we can't extract the issuer, don't exchange (might not be a JWT)
+    if (!issuer) {
+      return false;
+    }
+ 
+    // If the issuer is the same as Databricks host, no exchange needed
+    if (isSameHost(issuer, this.databricksHost)) {
+      return false;
+    }
+ 
+    return true;
+  }
+ 
+  /**
+   * Exchanges the token for a Databricks-compatible token using RFC 8693.
+   * Includes retry logic for transient errors with exponential backoff.
+   * @param token - The token to exchange
+   * @returns The exchanged token
+   */
+  private async exchangeToken(token: Token): Promise<Token> {
+    return this.exchangeTokenWithRetry(token, 0);
+  }
+ 
+  /**
+   * Attempts a single token exchange request.
+   * @returns The exchanged token
+   */
+  private async attemptTokenExchange(body: string): Promise<Token> {
+    const url = this.buildExchangeUrl();
+    const controller = new AbortController();
+    const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
+ 
+    try {
+      const response = await fetch(url, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/x-www-form-urlencoded',
+        },
+        body,
+        signal: controller.signal,
+      });
+ 
+      if (!response.ok) {
+        const errorText = await response.text();
+        const error = new TokenExchangeError(
+          `Token exchange failed: ${response.status} ${response.statusText} - ${errorText}`,
+          response.status,
+        );
+        throw error;
+      }
+ 
+      const data = (await response.json()) as {
+        access_token?: string;
+        token_type?: string;
+        expires_in?: number;
+      };
+ 
+      if (!data.access_token) {
+        throw new Error('Token exchange response missing access_token');
+      }
+ 
+      // Calculate expiration from expires_in
+      let expiresAt: Date | undefined;
+      if (typeof data.expires_in === 'number') {
+        expiresAt = new Date(Date.now() + data.expires_in * 1000);
+      }
+ 
+      return new Token(data.access_token, {
+        tokenType: data.token_type ?? 'Bearer',
+        expiresAt,
+      });
+    } finally {
+      clearTimeout(timeoutId);
+    }
+  }
+ 
+  /**
+   * Recursively attempts token exchange with exponential backoff.
+   */
+  private async exchangeTokenWithRetry(token: Token, attempt: number): Promise<Token> {
+    const params = new URLSearchParams({
+      grant_type: TOKEN_EXCHANGE_GRANT_TYPE,
+      subject_token_type: SUBJECT_TOKEN_TYPE,
+      subject_token: token.accessToken,
+      scope: DEFAULT_SCOPE,
+    });
+ 
+    if (this.clientId) {
+      params.append('client_id', this.clientId);
+    }
+ 
+    try {
+      return await this.attemptTokenExchange(params.toString());
+    } catch (error) {
+      const canRetry = attempt < MAX_RETRY_ATTEMPTS && this.isRetryableError(error);
+ 
+      if (!canRetry) {
+        throw error;
+      }
+ 
+      // Exponential backoff: 1s, 2s, 4s
+      const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
+      await new Promise<void>((resolve) => {
+        setTimeout(resolve, delay);
+      });
+ 
+      return this.exchangeTokenWithRetry(token, attempt + 1);
+    }
+  }
+ 
+  /**
+   * Determines if an error is retryable (transient HTTP errors, network errors, timeouts).
+   */
+  private isRetryableError(error: unknown): boolean {
+    if (error instanceof TokenExchangeError) {
+      return RETRYABLE_STATUS_CODES.has(error.statusCode);
+    }
+    if (error instanceof Error) {
+      return error.name === 'AbortError' || error.name === 'FetchError';
+    }
+    return false;
+  }
+ 
+  /**
+   * Builds the token exchange URL.
+   */
+  private buildExchangeUrl(): string {
+    let host = this.databricksHost;
+ 
+    // Ensure host has a protocol
+    if (!host.includes('://')) {
+      host = `https://${host}`;
+    }
+ 
+    // Remove trailing slash
+    if (host.endsWith('/')) {
+      host = host.slice(0, -1);
+    }
+ 
+    return `${host}${TOKEN_EXCHANGE_ENDPOINT}`;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html new file mode 100644 index 00000000..0b389bd6 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html @@ -0,0 +1,214 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/StaticTokenProvider.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider StaticTokenProvider.ts

+
+ +
+ 28.57% + Statements + 2/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 28.57% + Lines + 2/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import ITokenProvider from './ITokenProvider';
+import Token, { TokenOptions, TokenFromJWTOptions } from './Token';
+ 
+/**
+ * A token provider that returns a static token.
+ * Useful for testing or when the token is obtained through external means.
+ */
+export default class StaticTokenProvider implements ITokenProvider {
+  private readonly token: Token;
+ 
+  /**
+   * Creates a new StaticTokenProvider.
+   * @param accessToken - The access token string
+   * @param options - Optional token configuration (tokenType, expiresAt, refreshToken, scopes)
+   */
+  constructor(accessToken: string, options?: TokenOptions) {
+    this.token = new Token(accessToken, options);
+  }
+ 
+  /**
+   * Creates a StaticTokenProvider from a JWT string.
+   * The expiration time will be extracted from the JWT payload.
+   * @param jwt - The JWT token string
+   * @param options - Optional token configuration
+   */
+  static fromJWT(jwt: string, options?: TokenFromJWTOptions): StaticTokenProvider {
+    const token = Token.fromJWT(jwt, options);
+    return new StaticTokenProvider(token.accessToken, {
+      tokenType: token.tokenType,
+      expiresAt: token.expiresAt,
+      refreshToken: token.refreshToken,
+      scopes: token.scopes,
+    });
+  }
+ 
+  async getToken(): Promise<Token> {
+    return this.token;
+  }
+ 
+  getName(): string {
+    return 'StaticTokenProvider';
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html new file mode 100644 index 00000000..9838a9f8 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html @@ -0,0 +1,556 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/Token.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider Token.ts

+
+ +
+ 7.4% + Statements + 2/27 +
+ + +
+ 0% + Branches + 0/42 +
+ + +
+ 0% + Functions + 0/10 +
+ + +
+ 7.4% + Lines + 2/27 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { HeadersInit } from 'node-fetch';
+ 
+/**
+ * Safety buffer in seconds to consider a token expired before its actual expiration time.
+ * This prevents using tokens that are about to expire during in-flight requests.
+ */
+const EXPIRATION_BUFFER_SECONDS = 30;
+ 
+/**
+ * Options for creating a Token instance.
+ */
+export interface TokenOptions {
+  /** The token type (e.g., "Bearer"). Defaults to "Bearer". */
+  tokenType?: string;
+  /** The expiration time of the token. */
+  expiresAt?: Date;
+  /** The refresh token, if available. */
+  refreshToken?: string;
+  /** The scopes associated with this token. */
+  scopes?: string[];
+}
+ 
+/**
+ * Options for creating a Token from a JWT string.
+ * Does not include expiresAt since it is extracted from the JWT payload.
+ */
+export type TokenFromJWTOptions = Omit<TokenOptions, 'expiresAt'>;
+ 
+/**
+ * Represents an access token with optional metadata and lifecycle management.
+ */
+export default class Token {
+  private readonly _accessToken: string;
+ 
+  private readonly _tokenType: string;
+ 
+  private readonly _expiresAt?: Date;
+ 
+  private readonly _refreshToken?: string;
+ 
+  private readonly _scopes?: string[];
+ 
+  constructor(accessToken: string, options?: TokenOptions) {
+    this._accessToken = accessToken;
+    this._tokenType = options?.tokenType ?? 'Bearer';
+    this._expiresAt = options?.expiresAt;
+    this._refreshToken = options?.refreshToken;
+    this._scopes = options?.scopes;
+  }
+ 
+  /**
+   * The access token string.
+   */
+  get accessToken(): string {
+    return this._accessToken;
+  }
+ 
+  /**
+   * The token type (e.g., "Bearer").
+   */
+  get tokenType(): string {
+    return this._tokenType;
+  }
+ 
+  /**
+   * The expiration time of the token, if known.
+   */
+  get expiresAt(): Date | undefined {
+    return this._expiresAt;
+  }
+ 
+  /**
+   * The refresh token, if available.
+   */
+  get refreshToken(): string | undefined {
+    return this._refreshToken;
+  }
+ 
+  /**
+   * The scopes associated with this token.
+   */
+  get scopes(): string[] | undefined {
+    return this._scopes;
+  }
+ 
+  /**
+   * Checks if the token has expired, including a safety buffer.
+   * Returns false if expiration time is unknown.
+   */
+  isExpired(): boolean {
+    if (!this._expiresAt) {
+      return false;
+    }
+    const now = new Date();
+    const bufferMs = EXPIRATION_BUFFER_SECONDS * 1000;
+    return this._expiresAt.getTime() - bufferMs <= now.getTime();
+  }
+ 
+  /**
+   * Sets the Authorization header on the provided headers object.
+   * @param headers - The headers object to modify
+   * @returns The modified headers object with Authorization set
+   */
+  setAuthHeader(headers: HeadersInit): HeadersInit {
+    return {
+      ...headers,
+      Authorization: `${this._tokenType} ${this._accessToken}`,
+    };
+  }
+ 
+  /**
+   * Creates a Token from a JWT string, extracting the expiration time from the payload.
+   * If the JWT cannot be decoded, the token is created without expiration info.
+   * The server will validate the token anyway, so decoding failures are handled gracefully.
+   * @param jwt - The JWT token string
+   * @param options - Additional token options (tokenType, refreshToken, scopes).
+   *                  Note: expiresAt is not accepted here as it is extracted from the JWT payload.
+   * @returns A new Token instance with expiration extracted from the JWT (if available)
+   */
+  static fromJWT(jwt: string, options?: TokenFromJWTOptions): Token {
+    let expiresAt: Date | undefined;
+ 
+    try {
+      const parts = jwt.split('.');
+      if (parts.length >= 2) {
+        const payload = Buffer.from(parts[1], 'base64').toString('utf8');
+        const decoded = JSON.parse(payload);
+        if (typeof decoded.exp === 'number') {
+          expiresAt = new Date(decoded.exp * 1000);
+        }
+      }
+    } catch {
+      // If we can't decode the JWT, we'll proceed without expiration info
+      // The server will validate the token anyway
+    }
+ 
+    return new Token(jwt, {
+      tokenType: options?.tokenType,
+      expiresAt,
+      refreshToken: options?.refreshToken,
+      scopes: options?.scopes,
+    });
+  }
+ 
+  /**
+   * Converts the token to a plain object for serialization.
+   */
+  toJSON(): Record<string, unknown> {
+    return {
+      accessToken: this._accessToken,
+      tokenType: this._tokenType,
+      expiresAt: this._expiresAt?.toISOString(),
+      refreshToken: this._refreshToken,
+      scopes: this._scopes,
+    };
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html new file mode 100644 index 00000000..66302640 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html @@ -0,0 +1,250 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider TokenProviderAuthenticator.ts

+
+ +
+ 11.76% + Statements + 2/17 +
+ + +
+ 0% + Branches + 0/8 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 11.76% + Lines + 2/17 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { HeadersInit } from 'node-fetch';
+import IAuthentication from '../../contracts/IAuthentication';
+import ITokenProvider from './ITokenProvider';
+import IClientContext from '../../../contracts/IClientContext';
+import { LogLevel } from '../../../contracts/IDBSQLLogger';
+ 
+/**
+ * Adapts an ITokenProvider to the IAuthentication interface used by the driver.
+ * This allows token providers to be used with the existing authentication system.
+ */
+export default class TokenProviderAuthenticator implements IAuthentication {
+  private readonly tokenProvider: ITokenProvider;
+ 
+  private readonly context: IClientContext;
+ 
+  private readonly headers: HeadersInit;
+ 
+  /**
+   * Creates a new TokenProviderAuthenticator.
+   * @param tokenProvider - The token provider to use for authentication
+   * @param context - The client context for logging
+   * @param headers - Additional headers to include with each request
+   */
+  constructor(tokenProvider: ITokenProvider, context: IClientContext, headers?: HeadersInit) {
+    this.tokenProvider = tokenProvider;
+    this.context = context;
+    this.headers = headers ?? {};
+  }
+ 
+  async authenticate(): Promise<HeadersInit> {
+    const logger = this.context.getLogger();
+    const providerName = this.tokenProvider.getName();
+ 
+    logger.log(LogLevel.debug, `TokenProviderAuthenticator: getting token from ${providerName}`);
+ 
+    let token = await this.tokenProvider.getToken();
+ 
+    if (token.isExpired()) {
+      logger.log(
+        LogLevel.warn,
+        `TokenProviderAuthenticator: token from ${providerName} is expired, requesting a new token`,
+      );
+ 
+      token = await this.tokenProvider.getToken();
+ 
+      if (token.isExpired()) {
+        const message = `TokenProviderAuthenticator: token from ${providerName} is still expired after refresh`;
+        logger.log(LogLevel.error, message);
+        throw new Error(message);
+      }
+    }
+ 
+    return token.setAuthHeader(this.headers);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/index.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/index.html new file mode 100644 index 00000000..fe806a33 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/index.html @@ -0,0 +1,221 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider + + + + + + + + + +
+
+

All files lib/connection/auth/tokenProvider

+
+ +
+ 19.9% + Statements + 41/206 +
+ + +
+ 0% + Branches + 0/144 +
+ + +
+ 0% + Functions + 0/50 +
+ + +
+ 16.32% + Lines + 32/196 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
CachedTokenProvider.ts +
+
7.69%2/260%0/160%0/67.69%2/26
ExternalTokenProvider.ts +
+
20%2/100%0/180%0/320%2/10
FederationProvider.ts +
+
16%12/750%0/460%0/1216.21%12/74
StaticTokenProvider.ts +
+
28.57%2/7100%0/00%0/428.57%2/7
Token.ts +
+
7.4%2/270%0/420%0/107.4%2/27
TokenProviderAuthenticator.ts +
+
11.76%2/170%0/80%0/211.76%2/17
index.ts +
+
100%16/16100%0/00%0/9100%7/7
utils.ts +
+
10.71%3/280%0/140%0/410.71%3/28
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html new file mode 100644 index 00000000..cb84ba38 --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html @@ -0,0 +1,109 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/index.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider index.ts

+
+ +
+ 100% + Statements + 16/16 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 100% + Lines + 7/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9  +1x +1x +1x +1x +1x +1x +1x + 
export { default as ITokenProvider } from './ITokenProvider';
+export { default as Token } from './Token';
+export { default as StaticTokenProvider } from './StaticTokenProvider';
+export { default as ExternalTokenProvider, TokenCallback } from './ExternalTokenProvider';
+export { default as TokenProviderAuthenticator } from './TokenProviderAuthenticator';
+export { default as CachedTokenProvider } from './CachedTokenProvider';
+export { default as FederationProvider } from './FederationProvider';
+export { decodeJWT, getJWTIssuer, isSameHost } from './utils';
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html new file mode 100644 index 00000000..257c87bc --- /dev/null +++ b/coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html @@ -0,0 +1,322 @@ + + + + + + Code coverage report for lib/connection/auth/tokenProvider/utils.ts + + + + + + + + + +
+
+

All files / lib/connection/auth/tokenProvider utils.ts

+
+ +
+ 10.71% + Statements + 3/28 +
+ + +
+ 0% + Branches + 0/14 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 10.71% + Lines + 3/28 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  + 
/**
+ * Decodes a JWT token without verifying the signature.
+ * This is safe because the server will validate the token anyway.
+ *
+ * @param token - The JWT token string
+ * @returns The decoded payload as a record, or null if decoding fails
+ */
+export function decodeJWT(token: string): Record<string, unknown> | null {
+  try {
+    const parts = token.split('.');
+    if (parts.length < 2) {
+      return null;
+    }
+    const payload = Buffer.from(parts[1], 'base64').toString('utf8');
+    return JSON.parse(payload);
+  } catch {
+    return null;
+  }
+}
+ 
+/**
+ * Extracts the issuer from a JWT token.
+ *
+ * @param token - The JWT token string
+ * @returns The issuer string, or null if not found
+ */
+export function getJWTIssuer(token: string): string | null {
+  const payload = decodeJWT(token);
+  if (!payload || typeof payload.iss !== 'string') {
+    return null;
+  }
+  return payload.iss;
+}
+ 
+/**
+ * Extracts the hostname from a URL or hostname string.
+ * Handles both full URLs and bare hostnames.
+ *
+ * @param urlOrHostname - A URL or hostname string
+ * @returns The extracted hostname
+ */
+function extractHostname(urlOrHostname: string): string {
+  // If it looks like a URL, parse it
+  if (urlOrHostname.includes('://')) {
+    const url = new URL(urlOrHostname);
+    return url.hostname;
+  }
+ 
+  // Handle hostname with port (e.g., "databricks.com:443")
+  const colonIndex = urlOrHostname.indexOf(':');
+  if (colonIndex !== -1) {
+    return urlOrHostname.substring(0, colonIndex);
+  }
+ 
+  // Bare hostname
+  return urlOrHostname;
+}
+ 
+/**
+ * Compares two host URLs, ignoring ports.
+ * Treats "databricks.com" and "databricks.com:443" as equivalent.
+ *
+ * @param url1 - First URL or hostname
+ * @param url2 - Second URL or hostname
+ * @returns true if the hosts are the same
+ */
+export function isSameHost(url1: string, url2: string): boolean {
+  try {
+    const host1 = extractHostname(url1);
+    const host2 = extractHostname(url2);
+    // Empty hostnames are not valid
+    if (!host1 || !host2) {
+      return false;
+    }
+    return host1.toLowerCase() === host2.toLowerCase();
+  } catch {
+    return false;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html b/coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html new file mode 100644 index 00000000..beccabda --- /dev/null +++ b/coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html @@ -0,0 +1,460 @@ + + + + + + Code coverage report for lib/connection/connections/HttpConnection.ts + + + + + + + + + +
+
+

All files / lib/connection/connections HttpConnection.ts

+
+ +
+ 20% + Statements + 7/35 +
+ + +
+ 0% + Branches + 0/40 +
+ + +
+ 0% + Functions + 0/11 +
+ + +
+ 20% + Lines + 7/35 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +1261x +1x +1x +  +1x +  +  +  +  +  +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import thrift from 'thrift';
+import https from 'https';
+import http from 'http';
+import { HeadersInit } from 'node-fetch';
+import { ProxyAgent } from 'proxy-agent';
+ 
+import IConnectionProvider, { HttpTransactionDetails } from '../contracts/IConnectionProvider';
+import IConnectionOptions, { ProxyOptions } from '../contracts/IConnectionOptions';
+import IClientContext from '../../contracts/IClientContext';
+ 
+import ThriftHttpConnection from './ThriftHttpConnection';
+import IRetryPolicy from '../contracts/IRetryPolicy';
+import HttpRetryPolicy from './HttpRetryPolicy';
+ 
+export default class HttpConnection implements IConnectionProvider {
+  private readonly options: IConnectionOptions;
+ 
+  private readonly context: IClientContext;
+ 
+  private headers: HeadersInit = {};
+ 
+  private connection?: ThriftHttpConnection;
+ 
+  private agent?: http.Agent;
+ 
+  constructor(options: IConnectionOptions, context: IClientContext) {
+    this.options = options;
+    this.context = context;
+  }
+ 
+  public setHeaders(headers: HeadersInit) {
+    this.headers = headers;
+    this.connection?.setHeaders({
+      ...this.options.headers,
+      ...this.headers,
+    });
+  }
+ 
+  public async getAgent(): Promise<http.Agent | undefined> {
+    if (!this.agent) {
+      if (this.options.proxy !== undefined) {
+        this.agent = this.createProxyAgent(this.options.proxy);
+      } else {
+        this.agent = this.options.https ? this.createHttpsAgent() : this.createHttpAgent();
+      }
+    }
+ 
+    return this.agent;
+  }
+ 
+  private getAgentDefaultOptions(): http.AgentOptions {
+    const clientConfig = this.context.getConfig();
+ 
+    return {
+      keepAlive: true,
+      keepAliveMsecs: 10000,
+      maxSockets: Infinity, // no limit
+      timeout: this.options.socketTimeout ?? clientConfig.socketTimeout,
+    };
+  }
+ 
+  private createHttpAgent(): http.Agent {
+    const httpAgentOptions = this.getAgentDefaultOptions();
+    return new http.Agent(httpAgentOptions);
+  }
+ 
+  private createHttpsAgent(): https.Agent {
+    const httpsAgentOptions: https.AgentOptions = {
+      ...this.getAgentDefaultOptions(),
+      minVersion: 'TLSv1.2',
+      rejectUnauthorized: false,
+      ca: this.options.ca,
+      cert: this.options.cert,
+      key: this.options.key,
+    };
+    return new https.Agent(httpsAgentOptions);
+  }
+ 
+  private createProxyAgent(proxyOptions: ProxyOptions): ProxyAgent {
+    const proxyAuth = proxyOptions.auth?.username
+      ? `${proxyOptions.auth.username}:${proxyOptions.auth?.password ?? ''}@`
+      : '';
+    const proxyUrl = `${proxyOptions.protocol}://${proxyAuth}${proxyOptions.host}:${proxyOptions.port}`;
+ 
+    return new ProxyAgent({
+      ...this.getAgentDefaultOptions(),
+      getProxyForUrl: () => proxyUrl,
+      httpsAgent: this.createHttpsAgent(),
+      httpAgent: this.createHttpAgent(),
+    });
+  }
+ 
+  public async getThriftConnection(): Promise<any> {
+    if (!this.connection) {
+      const { options } = this;
+      const clientConfig = this.context.getConfig();
+      const agent = await this.getAgent();
+ 
+      this.connection = new ThriftHttpConnection(
+        {
+          url: `${options.https ? 'https' : 'http'}://${options.host.replace(/\/$/, '')}:${options.port}${
+            options.path ? (options.path.startsWith('/') ? '' : '/') + options.path.replace(/\/$/, '') : '/'
+          }`,
+          transport: thrift.TBufferedTransport,
+          protocol: thrift.TBinaryProtocol,
+          getRetryPolicy: () => this.getRetryPolicy(),
+        },
+        {
+          agent,
+          timeout: options.socketTimeout ?? clientConfig.socketTimeout,
+          headers: {
+            ...options.headers,
+            ...this.headers,
+          },
+        },
+      );
+    }
+ 
+    return this.connection;
+  }
+ 
+  public async getRetryPolicy(): Promise<IRetryPolicy<HttpTransactionDetails>> {
+    return new HttpRetryPolicy(this.context);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html b/coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html new file mode 100644 index 00000000..6fd725ac --- /dev/null +++ b/coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html @@ -0,0 +1,391 @@ + + + + + + Code coverage report for lib/connection/connections/HttpRetryPolicy.ts + + + + + + + + + +
+
+

All files / lib/connection/connections HttpRetryPolicy.ts

+
+ +
+ 5.26% + Statements + 2/38 +
+ + +
+ 0% + Branches + 0/24 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 5.4% + Lines + 2/37 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103  +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import IRetryPolicy, { ShouldRetryResult, RetryableOperation } from '../contracts/IRetryPolicy';
+import { HttpTransactionDetails } from '../contracts/IConnectionProvider';
+import IClientContext from '../../contracts/IClientContext';
+import RetryError, { RetryErrorCode } from '../../errors/RetryError';
+ 
+function delay(milliseconds: number): Promise<void> {
+  return new Promise<void>((resolve) => {
+    setTimeout(() => resolve(), milliseconds);
+  });
+}
+ 
+export default class HttpRetryPolicy implements IRetryPolicy<HttpTransactionDetails> {
+  private context: IClientContext;
+ 
+  private startTime: number; // in milliseconds
+ 
+  private attempt: number;
+ 
+  constructor(context: IClientContext) {
+    this.context = context;
+    this.startTime = Date.now();
+    this.attempt = 0;
+  }
+ 
+  public async shouldRetry(details: HttpTransactionDetails): Promise<ShouldRetryResult> {
+    if (this.isRetryable(details)) {
+      const clientConfig = this.context.getConfig();
+ 
+      // Don't retry if overall retry timeout exceeded
+      const timeoutExceeded = Date.now() - this.startTime >= clientConfig.retriesTimeout;
+      if (timeoutExceeded) {
+        throw new RetryError(RetryErrorCode.TimeoutExceeded, details);
+      }
+ 
+      this.attempt += 1;
+ 
+      // Don't retry if max attempts count reached
+      const attemptsExceeded = this.attempt >= clientConfig.retryMaxAttempts;
+      if (attemptsExceeded) {
+        throw new RetryError(RetryErrorCode.AttemptsExceeded, details);
+      }
+ 
+      // If possible, use `Retry-After` header as a floor for a backoff algorithm
+      const retryAfterHeader = this.getRetryAfterHeader(details, clientConfig.retryDelayMin);
+      const retryAfter = this.getBackoffDelay(
+        this.attempt,
+        retryAfterHeader ?? clientConfig.retryDelayMin,
+        clientConfig.retryDelayMax,
+      );
+ 
+      return { shouldRetry: true, retryAfter };
+    }
+ 
+    return { shouldRetry: false };
+  }
+ 
+  public async invokeWithRetry(operation: RetryableOperation<HttpTransactionDetails>): Promise<HttpTransactionDetails> {
+    for (;;) {
+      const details = await operation(); // eslint-disable-line no-await-in-loop
+      const status = await this.shouldRetry(details); // eslint-disable-line no-await-in-loop
+      if (!status.shouldRetry) {
+        return details;
+      }
+      await delay(status.retryAfter); // eslint-disable-line no-await-in-loop
+    }
+  }
+ 
+  protected isRetryable({ response }: HttpTransactionDetails): boolean {
+    const statusCode = response.status;
+ 
+    const result =
+      // Retry on all codes below 100
+      statusCode < 100 ||
+      // ...and on `429 Too Many Requests`
+      statusCode === 429 ||
+      // ...and on all `5xx` codes except for `501 Not Implemented`
+      (statusCode >= 500 && statusCode !== 501);
+ 
+    return result;
+  }
+ 
+  protected getRetryAfterHeader({ response }: HttpTransactionDetails, delayMin: number): number | undefined {
+    // `Retry-After` header may contain a date after which to retry, or delay seconds. We support only delay seconds.
+    // Value from `Retry-After` header is used when:
+    // 1. it's available and is non-empty
+    // 2. it could be parsed as a number, and is greater than zero
+    // 3. additionally, we clamp it to not be smaller than minimal retry delay
+    const header = response.headers.get('Retry-After') || '';
+    if (header !== '') {
+      const value = Number(header);
+      if (Number.isFinite(value) && value > 0) {
+        return Math.max(delayMin, value);
+      }
+    }
+    return undefined;
+  }
+ 
+  protected getBackoffDelay(attempt: number, delayMin: number, delayMax: number): number {
+    const value = 2 ** attempt * delayMin;
+    return Math.min(value, delayMax);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html b/coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html new file mode 100644 index 00000000..9cda4b4c --- /dev/null +++ b/coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/connection/connections/NullRetryPolicy.ts + + + + + + + + + +
+
+

All files / lib/connection/connections NullRetryPolicy.ts

+
+ +
+ 33.33% + Statements + 1/3 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 33.33% + Lines + 1/3 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14  +  +1x +  +  +  +  +  +  +  +  +  +  + 
import IRetryPolicy, { ShouldRetryResult, RetryableOperation } from '../contracts/IRetryPolicy';
+ 
+export default class NullRetryPolicy<R> implements IRetryPolicy<R> {
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  public async shouldRetry(details: R): Promise<ShouldRetryResult> {
+    return { shouldRetry: false };
+  }
+ 
+  public async invokeWithRetry(operation: RetryableOperation<R>): Promise<R> {
+    // Just invoke the operation, don't attempt to retry it
+    return operation();
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html b/coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html new file mode 100644 index 00000000..97cf8bb3 --- /dev/null +++ b/coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html @@ -0,0 +1,766 @@ + + + + + + Code coverage report for lib/connection/connections/ThriftHttpConnection.ts + + + + + + + + + +
+
+

All files / lib/connection/connections ThriftHttpConnection.ts

+
+ +
+ 10.52% + Statements + 8/76 +
+ + +
+ 0% + Branches + 0/33 +
+ + +
+ 0% + Functions + 0/20 +
+ + +
+ 10.81% + Lines + 8/74 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228  +  +  +  +  +  +1x +1x +1x +  +1x +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
/**
+  This file is created using node_modules/thrift/lib/nodejs/lib/thrift/http_connection.js as an example
+ 
+  The code relies on thrift internals, so be careful when upgrading `thrift` library
+*/
+ 
+import { EventEmitter } from 'events';
+import { TBinaryProtocol, TBufferedTransport, Thrift, TProtocol, TProtocolConstructor, TTransport } from 'thrift';
+import fetch, { RequestInit, HeadersInit, Request, Response, FetchError } from 'node-fetch';
+// @ts-expect-error TS7016: Could not find a declaration file for module
+import InputBufferUnderrunError from 'thrift/lib/nodejs/lib/thrift/input_buffer_underrun_error';
+import IRetryPolicy from '../contracts/IRetryPolicy';
+import { HttpTransactionDetails } from '../contracts/IConnectionProvider';
+import NullRetryPolicy from './NullRetryPolicy';
+ 
+export class THTTPException extends Thrift.TApplicationException {
+  public readonly statusCode: unknown;
+ 
+  public readonly response: Response;
+ 
+  constructor(response: Response) {
+    super(
+      Thrift.TApplicationExceptionType.PROTOCOL_ERROR,
+      `Received a response with a bad HTTP status code: ${response.status}`,
+    );
+    this.statusCode = response.status;
+    this.response = response;
+  }
+}
+ 
+type TTransportType = typeof TBufferedTransport;
+ 
+interface ThriftHttpConnectionOptions {
+  url: string;
+  transport?: TTransportType;
+  protocol?: TProtocolConstructor;
+  getRetryPolicy(): Promise<IRetryPolicy<HttpTransactionDetails>>;
+}
+ 
+// This type describes a shape of internals of Thrift client object.
+// It is not perfect good enough for our needs
+type ThriftClient = {
+  // Internal map of callbacks of running requests. Once request is completed (either successfully or not) -
+  // callback should be removed from it
+  _reqs: Record<number, (error: unknown, response?: unknown) => void>;
+} & {
+  // For each client's public method Foo there are two private ones: send_Foo and recv_Foo.
+  // We have to access recv_Foo ones to properly parse the response
+  [key: string]: (input: TProtocol, mtype: Thrift.MessageType, seqId: number) => void;
+};
+ 
+const retryableThriftMethods = new Set([
+  'GetOperationStatus',
+  'CancelOperation',
+  'CloseOperation',
+  'GetResultSetMetadata',
+  'CloseSession',
+  'GetInfo',
+  'GetTypeInfo',
+  'GetCatalogs',
+  'GetSchemas',
+  'GetTables',
+  'GetTableTypes',
+  'GetColumns',
+  'GetFunctions',
+  'GetPrimaryKeys',
+  'GetCrossReference',
+]);
+ 
+export default class ThriftHttpConnection extends EventEmitter {
+  private readonly url: string;
+ 
+  private config: RequestInit;
+ 
+  private options: ThriftHttpConnectionOptions;
+ 
+  // This field is used by Thrift internally, so name and type are important
+  private readonly transport: TTransportType;
+ 
+  // This field is used by Thrift internally, so name and type are important
+  private readonly protocol: TProtocolConstructor;
+ 
+  // thrift.createClient sets this field internally
+  public client?: ThriftClient;
+ 
+  constructor(options: ThriftHttpConnectionOptions, config: RequestInit = {}) {
+    super();
+    this.url = options.url;
+    this.config = config;
+    this.options = options;
+    this.transport = options.transport ?? TBufferedTransport;
+    this.protocol = options.protocol ?? TBinaryProtocol;
+  }
+ 
+  protected async getRetryPolicy(thriftMethodName?: string): Promise<IRetryPolicy<HttpTransactionDetails>> {
+    // Allow retry behavior only for Thrift operations that are for sure safe to retry
+    if (thriftMethodName && retryableThriftMethods.has(thriftMethodName)) {
+      return this.options.getRetryPolicy();
+    }
+    // Don't retry everything that is not explicitly allowed to retry
+    return new NullRetryPolicy();
+  }
+ 
+  public setHeaders(headers: HeadersInit) {
+    this.config = {
+      ...this.config,
+      headers,
+    };
+  }
+ 
+  public write(data: Buffer, seqId: number) {
+    const requestConfig: RequestInit = {
+      ...this.config,
+      method: 'POST',
+      headers: {
+        ...this.config.headers,
+        Connection: 'keep-alive',
+        'Content-Length': `${data.length}`,
+        'Content-Type': 'application/x-thrift',
+      },
+      body: data,
+    };
+ 
+    this.getThriftMethodName(data)
+      .then((thriftMethod) => this.getRetryPolicy(thriftMethod))
+      .then((retryPolicy) => {
+        const makeRequest = () => {
+          const request = new Request(this.url, requestConfig);
+          return fetch(request).then((response) => ({ request, response }));
+        };
+        return retryPolicy.invokeWithRetry(makeRequest);
+      })
+      .then(({ response }) => {
+        if (response.status !== 200) {
+          throw new THTTPException(response);
+        }
+ 
+        return response.buffer();
+      })
+      .then((buffer) => {
+        this.transport.receiver((transportWithData) => this.handleThriftResponse(transportWithData), seqId)(buffer);
+      })
+      .catch((error) => {
+        if (error instanceof FetchError) {
+          if (error.type === 'request-timeout') {
+            error = new Thrift.TApplicationException(
+              Thrift.TApplicationExceptionType.PROTOCOL_ERROR,
+              'Request timed out',
+            );
+          }
+        }
+ 
+        const defaultErrorHandler = (err: unknown) => {
+          this.emit('error', err);
+        };
+ 
+        if (this.client) {
+          const callback = this.client._reqs[seqId] ?? defaultErrorHandler;
+          delete this.client._reqs[seqId];
+          callback(error);
+        } else {
+          defaultErrorHandler(error);
+        }
+      });
+  }
+ 
+  private getThriftMethodName(thriftMessage: Buffer): Promise<string | undefined> {
+    return new Promise((resolve) => {
+      try {
+        const receiver = this.transport.receiver((transportWithData) => {
+          const Protocol = this.protocol;
+          const proto = new Protocol(transportWithData);
+          const header = proto.readMessageBegin();
+          resolve(header.fname);
+        }, 0 /* `seqId` could be any because it's ignored */);
+ 
+        receiver(thriftMessage);
+      } catch {
+        resolve(undefined);
+      }
+    });
+  }
+ 
+  private handleThriftResponse(transportWithData: TTransport) {
+    if (!this.client) {
+      throw new Thrift.TApplicationException(Thrift.TApplicationExceptionType.INTERNAL_ERROR, 'Client not available');
+    }
+ 
+    const Protocol = this.protocol;
+    const proto = new Protocol(transportWithData);
+    try {
+      // eslint-disable-next-line no-constant-condition
+      while (true) {
+        const header = proto.readMessageBegin();
+        const dummySeqId = header.rseqid * -1;
+        const { client } = this;
+ 
+        client._reqs[dummySeqId] = (err, success) => {
+          transportWithData.commitPosition();
+          const clientCallback = client._reqs[header.rseqid];
+          delete client._reqs[header.rseqid];
+          if (clientCallback) {
+            process.nextTick(() => {
+              clientCallback(err, success);
+            });
+          }
+        };
+ 
+        if (client[`recv_${header.fname}`]) {
+          client[`recv_${header.fname}`](proto, header.mtype, dummySeqId);
+        } else {
+          delete client._reqs[dummySeqId];
+          throw new Thrift.TApplicationException(
+            Thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
+            'Received a response to an unknown RPC function',
+          );
+        }
+      }
+    } catch (error) {
+      if (error instanceof InputBufferUnderrunError) {
+        transportWithData.rollbackPosition();
+      } else {
+        throw error;
+      }
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/index.html b/coverage/lcov-report/lib/connection/connections/index.html new file mode 100644 index 00000000..dcf0ae81 --- /dev/null +++ b/coverage/lcov-report/lib/connection/connections/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for lib/connection/connections + + + + + + + + + +
+
+

All files lib/connection/connections

+
+ +
+ 11.84% + Statements + 18/152 +
+ + +
+ 0% + Branches + 0/97 +
+ + +
+ 0% + Functions + 0/42 +
+ + +
+ 12.08% + Lines + 18/149 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
HttpConnection.ts +
+
20%7/350%0/400%0/1120%7/35
HttpRetryPolicy.ts +
+
5.26%2/380%0/240%0/95.4%2/37
NullRetryPolicy.ts +
+
33.33%1/3100%0/00%0/233.33%1/3
ThriftHttpConnection.ts +
+
10.52%8/760%0/330%0/2010.81%8/74
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html b/coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html new file mode 100644 index 00000000..2c666fce --- /dev/null +++ b/coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html @@ -0,0 +1,130 @@ + + + + + + Code coverage report for lib/contracts/IDBSQLLogger.ts + + + + + + + + + +
+
+

All files / lib/contracts IDBSQLLogger.ts

+
+ +
+ 100% + Statements + 5/5 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 5/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  + 
export interface LoggerOptions {
+  filepath?: string;
+  level?: LogLevel;
+}
+ 
+export default interface IDBSQLLogger {
+  log(level: LogLevel, message: string): void;
+}
+ 
+export enum LogLevel {
+  error = 'error',
+  warn = 'warn',
+  info = 'info',
+  debug = 'debug',
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/contracts/index.html b/coverage/lcov-report/lib/contracts/index.html new file mode 100644 index 00000000..92f0ae48 --- /dev/null +++ b/coverage/lcov-report/lib/contracts/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for lib/contracts + + + + + + + + + +
+
+

All files lib/contracts

+
+ +
+ 100% + Statements + 5/5 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 5/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
IDBSQLLogger.ts +
+
100%5/5100%2/2100%1/1100%5/5
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/dto/InfoValue.ts.html b/coverage/lcov-report/lib/dto/InfoValue.ts.html new file mode 100644 index 00000000..8524617c --- /dev/null +++ b/coverage/lcov-report/lib/dto/InfoValue.ts.html @@ -0,0 +1,184 @@ + + + + + + Code coverage report for lib/dto/InfoValue.ts + + + + + + + + + +
+
+

All files / lib/dto InfoValue.ts

+
+ +
+ 7.14% + Statements + 1/14 +
+ + +
+ 0% + Branches + 0/10 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 7.14% + Lines + 1/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Int64 from 'node-int64';
+import { TGetInfoValue } from '../../thrift/TCLIService_types';
+ 
+type InfoResultType = string | number | Buffer | Int64 | null;
+ 
+export default class InfoValue {
+  private value: TGetInfoValue;
+ 
+  constructor(value: TGetInfoValue) {
+    this.value = value;
+  }
+ 
+  getValue(): InfoResultType {
+    const infoValue = this.value;
+ 
+    if (infoValue.stringValue) {
+      return infoValue.stringValue;
+    }
+    if (infoValue.smallIntValue) {
+      return infoValue.smallIntValue;
+    }
+    if (infoValue.integerBitmask) {
+      return infoValue.integerBitmask;
+    }
+    if (infoValue.integerFlag) {
+      return infoValue.integerFlag;
+    }
+    if (infoValue.lenValue) {
+      return infoValue.lenValue;
+    }
+    return null;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/dto/Status.ts.html b/coverage/lcov-report/lib/dto/Status.ts.html new file mode 100644 index 00000000..16f386c6 --- /dev/null +++ b/coverage/lcov-report/lib/dto/Status.ts.html @@ -0,0 +1,214 @@ + + + + + + Code coverage report for lib/dto/Status.ts + + + + + + + + + +
+
+

All files / lib/dto Status.ts

+
+ +
+ 20% + Statements + 3/15 +
+ + +
+ 0% + Branches + 0/11 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 20% + Lines + 3/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +441x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { TStatus, TStatusCode } from '../../thrift/TCLIService_types';
+import StatusError from '../errors/StatusError';
+ 
+export default class Status {
+  private readonly status: TStatus;
+ 
+  constructor(status: TStatus) {
+    this.status = status;
+  }
+ 
+  public get isSuccess(): boolean {
+    const { statusCode } = this.status;
+    return statusCode === TStatusCode.SUCCESS_STATUS || statusCode === TStatusCode.SUCCESS_WITH_INFO_STATUS;
+  }
+ 
+  public get isExecuting(): boolean {
+    const { statusCode } = this.status;
+    return statusCode === TStatusCode.STILL_EXECUTING_STATUS;
+  }
+ 
+  public get isError(): boolean {
+    const { statusCode } = this.status;
+    return statusCode === TStatusCode.ERROR_STATUS || statusCode === TStatusCode.INVALID_HANDLE_STATUS;
+  }
+ 
+  public get info(): Array<string> {
+    return this.status.infoMessages || [];
+  }
+ 
+  public static assert(status: TStatus) {
+    const statusWrapper = new Status(status);
+    if (statusWrapper.isError) {
+      throw new StatusError(status);
+    }
+  }
+ 
+  public static success(info: Array<string> = []): Status {
+    return new Status({
+      statusCode: info.length > 0 ? TStatusCode.SUCCESS_WITH_INFO_STATUS : TStatusCode.SUCCESS_STATUS,
+      infoMessages: info,
+    });
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/dto/index.html b/coverage/lcov-report/lib/dto/index.html new file mode 100644 index 00000000..9b74e63b --- /dev/null +++ b/coverage/lcov-report/lib/dto/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for lib/dto + + + + + + + + + +
+
+

All files lib/dto

+
+ +
+ 13.79% + Statements + 4/29 +
+ + +
+ 0% + Branches + 0/21 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 13.79% + Lines + 4/29 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
InfoValue.ts +
+
7.14%1/140%0/100%0/27.14%1/14
Status.ts +
+
20%3/150%0/110%0/720%3/15
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/AuthenticationError.ts.html b/coverage/lcov-report/lib/errors/AuthenticationError.ts.html new file mode 100644 index 00000000..a9ca0a2b --- /dev/null +++ b/coverage/lcov-report/lib/errors/AuthenticationError.ts.html @@ -0,0 +1,94 @@ + + + + + + Code coverage report for lib/errors/AuthenticationError.ts + + + + + + + + + +
+
+

All files / lib/errors AuthenticationError.ts

+
+ +
+ 100% + Statements + 2/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 2/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +41x +  +1x + 
import HiveDriverError from './HiveDriverError';
+ 
+export default class AuthenticationError extends HiveDriverError {}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/HiveDriverError.ts.html b/coverage/lcov-report/lib/errors/HiveDriverError.ts.html new file mode 100644 index 00000000..eae0a9a0 --- /dev/null +++ b/coverage/lcov-report/lib/errors/HiveDriverError.ts.html @@ -0,0 +1,88 @@ + + + + + + Code coverage report for lib/errors/HiveDriverError.ts + + + + + + + + + +
+
+

All files / lib/errors HiveDriverError.ts

+
+ +
+ 100% + Statements + 1/1 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 1/1 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +21x + 
export default class HiveDriverError extends Error {}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/OperationStateError.ts.html b/coverage/lcov-report/lib/errors/OperationStateError.ts.html new file mode 100644 index 00000000..ad5996a4 --- /dev/null +++ b/coverage/lcov-report/lib/errors/OperationStateError.ts.html @@ -0,0 +1,178 @@ + + + + + + Code coverage report for lib/errors/OperationStateError.ts + + + + + + + + + +
+
+

All files / lib/errors OperationStateError.ts

+
+ +
+ 75% + Statements + 9/12 +
+ + +
+ 20% + Branches + 2/10 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 75% + Lines + 9/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +321x +  +  +1x +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  + 
import HiveDriverError from './HiveDriverError';
+import { TGetOperationStatusResp } from '../../thrift/TCLIService_types';
+ 
+export enum OperationStateErrorCode {
+  Canceled = 'CANCELED',
+  Closed = 'CLOSED',
+  Error = 'ERROR',
+  Timeout = 'TIMEOUT',
+  Unknown = 'UNKNOWN',
+}
+ 
+const errorMessages: Record<OperationStateErrorCode, string> = {
+  [OperationStateErrorCode.Canceled]: 'The operation was canceled by a client',
+  [OperationStateErrorCode.Closed]: 'The operation was closed by a client',
+  [OperationStateErrorCode.Error]: 'The operation failed due to an error',
+  [OperationStateErrorCode.Timeout]: 'The operation is in a timed out state',
+  [OperationStateErrorCode.Unknown]: 'The operation is in an unrecognized state',
+};
+ 
+export default class OperationStateError extends HiveDriverError {
+  public errorCode: OperationStateErrorCode;
+ 
+  public response?: TGetOperationStatusResp;
+ 
+  constructor(errorCode: OperationStateErrorCode, response?: TGetOperationStatusResp) {
+    super(response?.displayMessage ?? errorMessages[errorCode]);
+ 
+    this.errorCode = errorCode;
+    this.response = response;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/ParameterError.ts.html b/coverage/lcov-report/lib/errors/ParameterError.ts.html new file mode 100644 index 00000000..b521d421 --- /dev/null +++ b/coverage/lcov-report/lib/errors/ParameterError.ts.html @@ -0,0 +1,88 @@ + + + + + + Code coverage report for lib/errors/ParameterError.ts + + + + + + + + + +
+
+

All files / lib/errors ParameterError.ts

+
+ +
+ 100% + Statements + 1/1 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 1/1 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +21x + 
export default class ParameterError extends Error {}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/RetryError.ts.html b/coverage/lcov-report/lib/errors/RetryError.ts.html new file mode 100644 index 00000000..413fab13 --- /dev/null +++ b/coverage/lcov-report/lib/errors/RetryError.ts.html @@ -0,0 +1,148 @@ + + + + + + Code coverage report for lib/errors/RetryError.ts + + + + + + + + + +
+
+

All files / lib/errors RetryError.ts

+
+ +
+ 62.5% + Statements + 5/8 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 62.5% + Lines + 5/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +221x +1x +1x +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  + 
export enum RetryErrorCode {
+  AttemptsExceeded = 'ATTEMPTS_EXCEEDED',
+  TimeoutExceeded = 'TIMEOUT_EXCEEDED',
+}
+ 
+const errorMessages: Record<RetryErrorCode, string> = {
+  [RetryErrorCode.AttemptsExceeded]: 'Max retry count exceeded',
+  [RetryErrorCode.TimeoutExceeded]: 'Retry timeout exceeded',
+};
+ 
+export default class RetryError extends Error {
+  public readonly errorCode: RetryErrorCode;
+ 
+  public readonly payload: unknown;
+ 
+  constructor(errorCode: RetryErrorCode, payload?: unknown) {
+    super(errorMessages[errorCode]);
+    this.errorCode = errorCode;
+    this.payload = payload;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/StagingError.ts.html b/coverage/lcov-report/lib/errors/StagingError.ts.html new file mode 100644 index 00000000..c9c11c5d --- /dev/null +++ b/coverage/lcov-report/lib/errors/StagingError.ts.html @@ -0,0 +1,88 @@ + + + + + + Code coverage report for lib/errors/StagingError.ts + + + + + + + + + +
+
+

All files / lib/errors StagingError.ts

+
+ +
+ 100% + Statements + 1/1 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 1/1 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +21x + 
export default class StagingError extends Error {}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/StatusError.ts.html b/coverage/lcov-report/lib/errors/StatusError.ts.html new file mode 100644 index 00000000..cf5f2dbf --- /dev/null +++ b/coverage/lcov-report/lib/errors/StatusError.ts.html @@ -0,0 +1,148 @@ + + + + + + Code coverage report for lib/errors/StatusError.ts + + + + + + + + + +
+
+

All files / lib/errors StatusError.ts

+
+ +
+ 16.66% + Statements + 1/6 +
+ + +
+ 0% + Branches + 0/6 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 16.66% + Lines + 1/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { TStatus } from '../../thrift/TCLIService_types';
+ 
+export default class StatusError implements Error {
+  public name: string;
+ 
+  public message: string;
+ 
+  public code: number;
+ 
+  public stack?: string;
+ 
+  constructor(status: TStatus) {
+    this.name = 'Status Error';
+    this.message = status.errorMessage || '';
+    this.code = status.errorCode || -1;
+ 
+    if (Array.isArray(status.infoMessages)) {
+      this.stack = status.infoMessages.join('\n');
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/index.html b/coverage/lcov-report/lib/errors/index.html new file mode 100644 index 00000000..2e96fdc3 --- /dev/null +++ b/coverage/lcov-report/lib/errors/index.html @@ -0,0 +1,206 @@ + + + + + + Code coverage report for lib/errors + + + + + + + + + +
+
+

All files lib/errors

+
+ +
+ 64.51% + Statements + 20/31 +
+ + +
+ 22.22% + Branches + 4/18 +
+ + +
+ 40% + Functions + 2/5 +
+ + +
+ 64.51% + Lines + 20/31 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AuthenticationError.ts +
+
100%2/2100%0/0100%0/0100%2/2
HiveDriverError.ts +
+
100%1/1100%0/0100%0/0100%1/1
OperationStateError.ts +
+
75%9/1220%2/1050%1/275%9/12
ParameterError.ts +
+
100%1/1100%0/0100%0/0100%1/1
RetryError.ts +
+
62.5%5/8100%2/250%1/262.5%5/8
StagingError.ts +
+
100%1/1100%0/0100%0/0100%1/1
StatusError.ts +
+
16.66%1/60%0/60%0/116.66%1/6
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html new file mode 100644 index 00000000..a037b54e --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html @@ -0,0 +1,295 @@ + + + + + + Code coverage report for lib/hive/Commands/BaseCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands BaseCommand.ts

+
+ +
+ 16.66% + Statements + 4/24 +
+ + +
+ 0% + Branches + 0/22 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 16.66% + Lines + 4/24 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +711x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Response } from 'node-fetch';
+import HiveDriverError from '../../errors/HiveDriverError';
+import RetryError, { RetryErrorCode } from '../../errors/RetryError';
+import IClientContext from '../../contracts/IClientContext';
+ 
+export default abstract class BaseCommand<ClientType> {
+  protected client: ClientType;
+ 
+  protected context: IClientContext;
+ 
+  constructor(client: ClientType, context: IClientContext) {
+    this.client = client;
+    this.context = context;
+  }
+ 
+  protected async executeCommand<Response>(request: object, command: Function | void): Promise<Response> {
+    try {
+      return await this.invokeCommand<Response>(request, command);
+    } catch (error) {
+      if (error instanceof RetryError) {
+        let statusCode: number | undefined;
+        if (
+          error.payload &&
+          typeof error.payload === 'object' &&
+          'response' in error.payload &&
+          error.payload.response instanceof Response
+        ) {
+          statusCode = error.payload.response.status;
+        }
+ 
+        switch (error.errorCode) {
+          case RetryErrorCode.AttemptsExceeded:
+            throw new HiveDriverError(
+              `Hive driver: ${statusCode ?? 'Error'} when connecting to resource. Max retry count exceeded.`,
+            );
+          case RetryErrorCode.TimeoutExceeded:
+            throw new HiveDriverError(
+              `Hive driver: ${statusCode ?? 'Error'} when connecting to resource. Retry timeout exceeded.`,
+            );
+          // no default
+        }
+      }
+ 
+      // Re-throw error we didn't handle
+      throw error;
+    }
+  }
+ 
+  private invokeCommand<Response>(request: object, command: Function | void): Promise<Response> {
+    if (typeof command !== 'function') {
+      return Promise.reject(
+        new HiveDriverError('Hive driver: the operation does not exist, try to choose another Thrift file.'),
+      );
+    }
+ 
+    return new Promise((resolve, reject) => {
+      try {
+        command.call(this.client, request, (err: Error, response: Response) => {
+          if (err) {
+            reject(err);
+          } else {
+            resolve(response);
+          }
+        });
+      } catch (error) {
+        reject(error);
+      }
+    });
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html new file mode 100644 index 00000000..9f074939 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/CancelDelegationTokenCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands CancelDelegationTokenCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TCancelDelegationTokenReq, TCancelDelegationTokenResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'CancelDelegationToken'>;
+ 
+export default class CancelDelegationTokenCommand extends BaseCommand<Client> {
+  execute(data: TCancelDelegationTokenReq): Promise<TCancelDelegationTokenResp> {
+    const request = new TCancelDelegationTokenReq(data);
+ 
+    return this.executeCommand<TCancelDelegationTokenResp>(request, this.client.CancelDelegationToken);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html new file mode 100644 index 00000000..6ca660aa --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/CancelOperationCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands CancelOperationCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TCancelOperationReq, TCancelOperationResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'CancelOperation'>;
+ 
+export default class CancelOperationCommand extends BaseCommand<Client> {
+  execute(data: TCancelOperationReq): Promise<TCancelOperationResp> {
+    const request = new TCancelOperationReq(data);
+ 
+    return this.executeCommand<TCancelOperationResp>(request, this.client.CancelOperation);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html new file mode 100644 index 00000000..5299771d --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/CloseOperationCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands CloseOperationCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TCloseOperationReq, TCloseOperationResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'CloseOperation'>;
+ 
+export default class CloseOperationCommand extends BaseCommand<Client> {
+  execute(data: TCloseOperationReq): Promise<TCloseOperationResp> {
+    const request = new TCloseOperationReq(data);
+ 
+    return this.executeCommand<TCloseOperationResp>(request, this.client.CloseOperation);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html new file mode 100644 index 00000000..d959e629 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/CloseSessionCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands CloseSessionCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TCloseSessionReq, TCloseSessionResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'CloseSession'>;
+ 
+export default class CloseSessionCommand extends BaseCommand<Client> {
+  execute(openSessionRequest: TCloseSessionReq): Promise<TCloseSessionResp> {
+    const request = new TCloseSessionReq(openSessionRequest);
+ 
+    return this.executeCommand<TCloseSessionResp>(request, this.client.CloseSession);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html new file mode 100644 index 00000000..0a8bb12a --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/ExecuteStatementCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands ExecuteStatementCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TExecuteStatementReq, TExecuteStatementResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'ExecuteStatement'>;
+ 
+export default class ExecuteStatementCommand extends BaseCommand<Client> {
+  execute(executeStatementRequest: TExecuteStatementReq): Promise<TExecuteStatementResp> {
+    const request = new TExecuteStatementReq(executeStatementRequest);
+ 
+    return this.executeCommand<TExecuteStatementResp>(request, this.client.ExecuteStatement);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html new file mode 100644 index 00000000..6c823598 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html @@ -0,0 +1,133 @@ + + + + + + Code coverage report for lib/hive/Commands/FetchResultsCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands FetchResultsCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +171x +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TFetchResultsReq, TFetchResultsResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'FetchResults'>;
+ 
+/**
+ * TFetchResultsReq.fetchType - 0 represents Query output. 1 represents Log
+ */
+export default class FetchResultsCommand extends BaseCommand<Client> {
+  execute(data: TFetchResultsReq): Promise<TFetchResultsResp> {
+    const request = new TFetchResultsReq(data);
+ 
+    return this.executeCommand<TFetchResultsResp>(request, this.client.FetchResults);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html new file mode 100644 index 00000000..b0fb30da --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetCatalogsCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetCatalogsCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetCatalogsReq, TGetCatalogsResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetCatalogs'>;
+ 
+export default class GetCatalogsCommand extends BaseCommand<Client> {
+  execute(data: TGetCatalogsReq): Promise<TGetCatalogsResp> {
+    const request = new TGetCatalogsReq(data);
+ 
+    return this.executeCommand<TGetCatalogsResp>(request, this.client.GetCatalogs);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html new file mode 100644 index 00000000..8c463f2a --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetColumnsCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetColumnsCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetColumnsReq, TGetColumnsResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetColumns'>;
+ 
+export default class GetColumnsCommand extends BaseCommand<Client> {
+  execute(data: TGetColumnsReq): Promise<TGetColumnsResp> {
+    const request = new TGetColumnsReq(data);
+ 
+    return this.executeCommand<TGetColumnsResp>(request, this.client.GetColumns);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html new file mode 100644 index 00000000..df48823b --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetCrossReferenceCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetCrossReferenceCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetCrossReferenceReq, TGetCrossReferenceResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetCrossReference'>;
+ 
+export default class GetCrossReferenceCommand extends BaseCommand<Client> {
+  execute(data: TGetCrossReferenceReq): Promise<TGetCrossReferenceResp> {
+    const request = new TGetCrossReferenceReq(data);
+ 
+    return this.executeCommand<TGetCrossReferenceResp>(request, this.client.GetCrossReference);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html new file mode 100644 index 00000000..1c3f4396 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetDelegationTokenCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetDelegationTokenCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetDelegationTokenReq, TGetDelegationTokenResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetDelegationToken'>;
+ 
+export default class GetDelegationTokenCommand extends BaseCommand<Client> {
+  execute(data: TGetDelegationTokenReq): Promise<TGetDelegationTokenResp> {
+    const request = new TGetDelegationTokenReq(data);
+ 
+    return this.executeCommand<TGetDelegationTokenResp>(request, this.client.GetDelegationToken);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html new file mode 100644 index 00000000..89a7a924 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetFunctionsCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetFunctionsCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetFunctionsReq, TGetFunctionsResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetFunctions'>;
+ 
+export default class GetFunctionsCommand extends BaseCommand<Client> {
+  execute(data: TGetFunctionsReq): Promise<TGetFunctionsResp> {
+    const request = new TGetFunctionsReq(data);
+ 
+    return this.executeCommand<TGetFunctionsResp>(request, this.client.GetFunctions);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html new file mode 100644 index 00000000..d7bd6e61 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetInfoCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetInfoCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetInfoReq, TGetInfoResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetInfo'>;
+ 
+export default class GetInfoCommand extends BaseCommand<Client> {
+  execute(data: TGetInfoReq): Promise<TGetInfoResp> {
+    const request = new TGetInfoReq(data);
+ 
+    return this.executeCommand<TGetInfoResp>(request, this.client.GetInfo);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html new file mode 100644 index 00000000..f64831b6 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetOperationStatusCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetOperationStatusCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetOperationStatusReq, TGetOperationStatusResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetOperationStatus'>;
+ 
+export default class GetOperationStatusCommand extends BaseCommand<Client> {
+  execute(data: TGetOperationStatusReq): Promise<TGetOperationStatusResp> {
+    const request = new TGetOperationStatusReq(data);
+ 
+    return this.executeCommand<TGetOperationStatusResp>(request, this.client.GetOperationStatus);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html new file mode 100644 index 00000000..b81a276e --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetPrimaryKeysCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetPrimaryKeysCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetPrimaryKeysReq, TGetPrimaryKeysResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetPrimaryKeys'>;
+ 
+export default class GetPrimaryKeysCommand extends BaseCommand<Client> {
+  execute(data: TGetPrimaryKeysReq): Promise<TGetPrimaryKeysResp> {
+    const request = new TGetPrimaryKeysReq(data);
+ 
+    return this.executeCommand<TGetPrimaryKeysResp>(request, this.client.GetPrimaryKeys);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html new file mode 100644 index 00000000..69b89491 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetResultSetMetadataCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetResultSetMetadataCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetResultSetMetadataReq, TGetResultSetMetadataResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetResultSetMetadata'>;
+ 
+export default class GetResultSetMetadataCommand extends BaseCommand<Client> {
+  execute(getResultSetMetadataRequest: TGetResultSetMetadataReq): Promise<TGetResultSetMetadataResp> {
+    const request = new TGetResultSetMetadataReq(getResultSetMetadataRequest);
+ 
+    return this.executeCommand<TGetResultSetMetadataResp>(request, this.client.GetResultSetMetadata);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html new file mode 100644 index 00000000..f9caada1 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetSchemasCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetSchemasCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetSchemasReq, TGetSchemasResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetSchemas'>;
+ 
+export default class GetSchemasCommand extends BaseCommand<Client> {
+  execute(data: TGetSchemasReq): Promise<TGetSchemasResp> {
+    const request = new TGetSchemasReq(data);
+ 
+    return this.executeCommand<TGetSchemasResp>(request, this.client.GetSchemas);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html new file mode 100644 index 00000000..2b61c3a0 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetTableTypesCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetTableTypesCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetTableTypesReq, TGetTableTypesResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetTableTypes'>;
+ 
+export default class GetTableTypesCommand extends BaseCommand<Client> {
+  execute(data: TGetTableTypesReq): Promise<TGetTableTypesResp> {
+    const request = new TGetTableTypesReq(data);
+ 
+    return this.executeCommand<TGetTableTypesResp>(request, this.client.GetTableTypes);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html new file mode 100644 index 00000000..816bef9e --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetTablesCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetTablesCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetTablesReq, TGetTablesResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetTables'>;
+ 
+export default class GetTablesCommand extends BaseCommand<Client> {
+  execute(data: TGetTablesReq): Promise<TGetTablesResp> {
+    const request = new TGetTablesReq(data);
+ 
+    return this.executeCommand<TGetTablesResp>(request, this.client.GetTables);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html new file mode 100644 index 00000000..d2091647 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/GetTypeInfoCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands GetTypeInfoCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TGetTypeInfoReq, TGetTypeInfoResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'GetTypeInfo'>;
+ 
+export default class GetTypeInfoCommand extends BaseCommand<Client> {
+  execute(data: TGetTypeInfoReq): Promise<TGetTypeInfoResp> {
+    const request = new TGetTypeInfoReq(data);
+ 
+    return this.executeCommand<TGetTypeInfoResp>(request, this.client.GetTypeInfo);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html new file mode 100644 index 00000000..b3bf7066 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html @@ -0,0 +1,151 @@ + + + + + + Code coverage report for lib/hive/Commands/OpenSessionCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands OpenSessionCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +231x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TOpenSessionReq, TOpenSessionResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'OpenSession'>;
+ 
+/**
+ * For auth mechanism GSSAPI the host and service should be provided when session is opened.
+ *
+ * TOpenSessionReq.configuration: {
+ *   krb_host?: string;
+ *   krb_service?: string;
+ *   [key: string]: any;
+ * }
+ */
+export default class OpenSessionCommand extends BaseCommand<Client> {
+  execute(openSessionRequest: TOpenSessionReq): Promise<TOpenSessionResp> {
+    const request = new TOpenSessionReq(openSessionRequest);
+ 
+    return this.executeCommand<TOpenSessionResp>(request, this.client.OpenSession);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html new file mode 100644 index 00000000..1564fca2 --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for lib/hive/Commands/RenewDelegationTokenCommand.ts + + + + + + + + + +
+
+

All files / lib/hive/Commands RenewDelegationTokenCommand.ts

+
+ +
+ 60% + Statements + 3/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 60% + Lines + 3/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +1x +  +  +  +  +1x +  +  +  +  +  +  + 
import BaseCommand from './BaseCommand';
+import { TRenewDelegationTokenReq, TRenewDelegationTokenResp } from '../../../thrift/TCLIService_types';
+import IThriftClient from '../../contracts/IThriftClient';
+ 
+type Client = Pick<IThriftClient, 'RenewDelegationToken'>;
+ 
+export default class RenewDelegationTokenCommand extends BaseCommand<Client> {
+  execute(data: TRenewDelegationTokenReq): Promise<TRenewDelegationTokenResp> {
+    const request = new TRenewDelegationTokenReq(data);
+ 
+    return this.executeCommand<TRenewDelegationTokenResp>(request, this.client.RenewDelegationToken);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/index.html b/coverage/lcov-report/lib/hive/Commands/index.html new file mode 100644 index 00000000..ad23a77a --- /dev/null +++ b/coverage/lcov-report/lib/hive/Commands/index.html @@ -0,0 +1,431 @@ + + + + + + Code coverage report for lib/hive/Commands + + + + + + + + + +
+
+

All files lib/hive/Commands

+
+ +
+ 51.93% + Statements + 67/129 +
+ + +
+ 0% + Branches + 0/22 +
+ + +
+ 0% + Functions + 0/26 +
+ + +
+ 51.93% + Lines + 67/129 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
BaseCommand.ts +
+
16.66%4/240%0/220%0/516.66%4/24
CancelDelegationTokenCommand.ts +
+
60%3/5100%0/00%0/160%3/5
CancelOperationCommand.ts +
+
60%3/5100%0/00%0/160%3/5
CloseOperationCommand.ts +
+
60%3/5100%0/00%0/160%3/5
CloseSessionCommand.ts +
+
60%3/5100%0/00%0/160%3/5
ExecuteStatementCommand.ts +
+
60%3/5100%0/00%0/160%3/5
FetchResultsCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetCatalogsCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetColumnsCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetCrossReferenceCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetDelegationTokenCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetFunctionsCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetInfoCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetOperationStatusCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetPrimaryKeysCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetResultSetMetadataCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetSchemasCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetTableTypesCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetTablesCommand.ts +
+
60%3/5100%0/00%0/160%3/5
GetTypeInfoCommand.ts +
+
60%3/5100%0/00%0/160%3/5
OpenSessionCommand.ts +
+
60%3/5100%0/00%0/160%3/5
RenewDelegationTokenCommand.ts +
+
60%3/5100%0/00%0/160%3/5
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/HiveDriver.ts.html b/coverage/lcov-report/lib/hive/HiveDriver.ts.html new file mode 100644 index 00000000..f7734430 --- /dev/null +++ b/coverage/lcov-report/lib/hive/HiveDriver.ts.html @@ -0,0 +1,637 @@ + + + + + + Code coverage report for lib/hive/HiveDriver.ts + + + + + + + + + +
+
+

All files / lib/hive HiveDriver.ts

+
+ +
+ 25.58% + Statements + 22/86 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/22 +
+ + +
+ 25.58% + Lines + 22/86 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import {
+  TOpenSessionReq,
+  TCloseSessionReq,
+  TExecuteStatementReq,
+  TGetResultSetMetadataReq,
+  TFetchResultsReq,
+  TGetInfoReq,
+  TGetTypeInfoReq,
+  TGetCatalogsReq,
+  TGetSchemasReq,
+  TGetTablesReq,
+  TGetTableTypesReq,
+  TGetColumnsReq,
+  TGetFunctionsReq,
+  TGetPrimaryKeysReq,
+  TGetCrossReferenceReq,
+  TGetOperationStatusReq,
+  TCancelOperationReq,
+  TCloseOperationReq,
+  TGetDelegationTokenReq,
+  TCancelDelegationTokenReq,
+  TRenewDelegationTokenReq,
+} from '../../thrift/TCLIService_types';
+import OpenSessionCommand from './Commands/OpenSessionCommand';
+import CloseSessionCommand from './Commands/CloseSessionCommand';
+import ExecuteStatementCommand from './Commands/ExecuteStatementCommand';
+import GetResultSetMetadataCommand from './Commands/GetResultSetMetadataCommand';
+import FetchResultsCommand from './Commands/FetchResultsCommand';
+import GetInfoCommand from './Commands/GetInfoCommand';
+import GetTypeInfoCommand from './Commands/GetTypeInfoCommand';
+import GetCatalogsCommand from './Commands/GetCatalogsCommand';
+import GetSchemasCommand from './Commands/GetSchemasCommand';
+import GetTablesCommand from './Commands/GetTablesCommand';
+import GetTableTypesCommand from './Commands/GetTableTypesCommand';
+import GetColumnsCommand from './Commands/GetColumnsCommand';
+import GetFunctionsCommand from './Commands/GetFunctionsCommand';
+import GetPrimaryKeysCommand from './Commands/GetPrimaryKeysCommand';
+import GetCrossReferenceCommand from './Commands/GetCrossReferenceCommand';
+import GetOperationStatusCommand from './Commands/GetOperationStatusCommand';
+import CancelOperationCommand from './Commands/CancelOperationCommand';
+import CloseOperationCommand from './Commands/CloseOperationCommand';
+import GetDelegationTokenCommand from './Commands/GetDelegationTokenCommand';
+import CancelDelegationTokenCommand from './Commands/CancelDelegationTokenCommand';
+import RenewDelegationTokenCommand from './Commands/RenewDelegationTokenCommand';
+import IDriver from '../contracts/IDriver';
+import IClientContext from '../contracts/IClientContext';
+ 
+export interface HiveDriverOptions {
+  context: IClientContext;
+}
+ 
+export default class HiveDriver implements IDriver {
+  private readonly context: IClientContext;
+ 
+  constructor(options: HiveDriverOptions) {
+    this.context = options.context;
+  }
+ 
+  async openSession(request: TOpenSessionReq) {
+    const client = await this.context.getClient();
+    const action = new OpenSessionCommand(client, this.context);
+    return action.execute(request);
+  }
+ 
+  async closeSession(request: TCloseSessionReq) {
+    const client = await this.context.getClient();
+    const command = new CloseSessionCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async executeStatement(request: TExecuteStatementReq) {
+    const client = await this.context.getClient();
+    const command = new ExecuteStatementCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getResultSetMetadata(request: TGetResultSetMetadataReq) {
+    const client = await this.context.getClient();
+    const command = new GetResultSetMetadataCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async fetchResults(request: TFetchResultsReq) {
+    const client = await this.context.getClient();
+    const command = new FetchResultsCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getInfo(request: TGetInfoReq) {
+    const client = await this.context.getClient();
+    const command = new GetInfoCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getTypeInfo(request: TGetTypeInfoReq) {
+    const client = await this.context.getClient();
+    const command = new GetTypeInfoCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getCatalogs(request: TGetCatalogsReq) {
+    const client = await this.context.getClient();
+    const command = new GetCatalogsCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getSchemas(request: TGetSchemasReq) {
+    const client = await this.context.getClient();
+    const command = new GetSchemasCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getTables(request: TGetTablesReq) {
+    const client = await this.context.getClient();
+    const command = new GetTablesCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getTableTypes(request: TGetTableTypesReq) {
+    const client = await this.context.getClient();
+    const command = new GetTableTypesCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getColumns(request: TGetColumnsReq) {
+    const client = await this.context.getClient();
+    const command = new GetColumnsCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getFunctions(request: TGetFunctionsReq) {
+    const client = await this.context.getClient();
+    const command = new GetFunctionsCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getPrimaryKeys(request: TGetPrimaryKeysReq) {
+    const client = await this.context.getClient();
+    const command = new GetPrimaryKeysCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getCrossReference(request: TGetCrossReferenceReq) {
+    const client = await this.context.getClient();
+    const command = new GetCrossReferenceCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getOperationStatus(request: TGetOperationStatusReq) {
+    const client = await this.context.getClient();
+    const command = new GetOperationStatusCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async cancelOperation(request: TCancelOperationReq) {
+    const client = await this.context.getClient();
+    const command = new CancelOperationCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async closeOperation(request: TCloseOperationReq) {
+    const client = await this.context.getClient();
+    const command = new CloseOperationCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async getDelegationToken(request: TGetDelegationTokenReq) {
+    const client = await this.context.getClient();
+    const command = new GetDelegationTokenCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async cancelDelegationToken(request: TCancelDelegationTokenReq) {
+    const client = await this.context.getClient();
+    const command = new CancelDelegationTokenCommand(client, this.context);
+    return command.execute(request);
+  }
+ 
+  async renewDelegationToken(request: TRenewDelegationTokenReq) {
+    const client = await this.context.getClient();
+    const command = new RenewDelegationTokenCommand(client, this.context);
+    return command.execute(request);
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/index.html b/coverage/lcov-report/lib/hive/index.html new file mode 100644 index 00000000..3afd7322 --- /dev/null +++ b/coverage/lcov-report/lib/hive/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for lib/hive + + + + + + + + + +
+
+

All files lib/hive

+
+ +
+ 25.58% + Statements + 22/86 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/22 +
+ + +
+ 25.58% + Lines + 22/86 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
HiveDriver.ts +
+
25.58%22/86100%0/00%0/2225.58%22/86
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/index.html b/coverage/lcov-report/lib/index.html new file mode 100644 index 00000000..2eafd6dd --- /dev/null +++ b/coverage/lcov-report/lib/index.html @@ -0,0 +1,206 @@ + + + + + + Code coverage report for lib + + + + + + + + + +
+
+

All files lib

+
+ +
+ 14.44% + Statements + 78/540 +
+ + +
+ 0.5% + Branches + 2/397 +
+ + +
+ 2.43% + Functions + 2/82 +
+ + +
+ 14.44% + Lines + 78/540 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
DBSQLClient.ts +
+
17.92%19/1060%0/544.54%1/2217.92%19/106
DBSQLLogger.ts +
+
25%3/120%0/60%0/325%3/12
DBSQLOperation.ts +
+
9.58%16/1670%0/1650%0/249.58%16/167
DBSQLParameter.ts +
+
54.54%18/334.65%2/4333.33%1/354.54%18/33
DBSQLSession.ts +
+
10.19%21/2060%0/1150%0/2610.19%21/206
polyfills.ts +
+
0%0/150%0/140%0/40%0/15
version.ts +
+
100%1/1100%0/0100%0/0100%1/1
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/polyfills.ts.html b/coverage/lcov-report/lib/polyfills.ts.html new file mode 100644 index 00000000..1d6b3ade --- /dev/null +++ b/coverage/lcov-report/lib/polyfills.ts.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for lib/polyfills.ts + + + + + + + + + +
+
+

All files / lib polyfills.ts

+
+ +
+ 0% + Statements + 0/15 +
+ + +
+ 0% + Branches + 0/14 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 0% + Lines + 0/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
/* eslint-disable import/prefer-default-export */
+ 
+// `Array.at` / `TypedArray.at` is supported only since Nodejs@16.6.0
+// These methods are massively used by `apache-arrow@13`, but we have
+// to use this version because older ones contain some other nasty bugs
+ 
+// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tointegerorinfinity
+function toIntegerOrInfinity(value: unknown): number {
+  const result = Number(value);
+ 
+  // Return `0` for NaN; return `+Infinity` / `-Infinity` as is
+  if (!Number.isFinite(result)) {
+    return Number.isNaN(result) ? 0 : result;
+  }
+ 
+  return Math.trunc(result);
+}
+ 
+// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tolength
+function toLength(value: unknown): number {
+  const result = toIntegerOrInfinity(value);
+  return result > 0 ? Math.min(result, Number.MAX_SAFE_INTEGER) : 0;
+}
+ 
+// https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.at
+export function at<T>(this: ArrayLike<T>, index: unknown): T | undefined {
+  const length = toLength(this.length);
+  const relativeIndex = toIntegerOrInfinity(index);
+  const absoluteIndex = relativeIndex >= 0 ? relativeIndex : length + relativeIndex;
+  return absoluteIndex >= 0 && absoluteIndex < length ? this[absoluteIndex] : undefined;
+}
+ 
+const ArrayConstructors = [
+  global.Array,
+  global.Int8Array,
+  global.Uint8Array,
+  global.Uint8ClampedArray,
+  global.Int16Array,
+  global.Uint16Array,
+  global.Int32Array,
+  global.Uint32Array,
+  global.Float32Array,
+  global.Float64Array,
+  global.BigInt64Array,
+  global.BigUint64Array,
+];
+ 
+ArrayConstructors.forEach((ArrayConstructor) => {
+  if (typeof ArrayConstructor.prototype.at !== 'function') {
+    ArrayConstructor.prototype.at = at;
+  }
+});
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/ArrowResultConverter.ts.html b/coverage/lcov-report/lib/result/ArrowResultConverter.ts.html new file mode 100644 index 00000000..cff8b838 --- /dev/null +++ b/coverage/lcov-report/lib/result/ArrowResultConverter.ts.html @@ -0,0 +1,754 @@ + + + + + + Code coverage report for lib/result/ArrowResultConverter.ts + + + + + + + + + +
+
+

All files / lib/result ArrowResultConverter.ts

+
+ +
+ 5.81% + Statements + 5/86 +
+ + +
+ 0% + Branches + 0/93 +
+ + +
+ 0% + Functions + 0/11 +
+ + +
+ 5.95% + Lines + 5/84 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +2241x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { Buffer } from 'buffer';
+import {
+  Table,
+  Schema,
+  Field,
+  TypeMap,
+  DataType,
+  Type,
+  StructRow,
+  MapRow,
+  Vector,
+  RecordBatch,
+  RecordBatchReader,
+  util as arrowUtils,
+} from 'apache-arrow';
+import { TGetResultSetMetadataResp, TColumnDesc } from '../../thrift/TCLIService_types';
+import IClientContext from '../contracts/IClientContext';
+import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
+import { ArrowBatch, getSchemaColumns, convertThriftValue } from './utils';
+ 
+const { isArrowBigNumSymbol, bigNumToBigInt } = arrowUtils;
+ 
+type ArrowSchema = Schema<TypeMap>;
+type ArrowSchemaField = Field<DataType<Type, TypeMap>>;
+ 
+export default class ArrowResultConverter implements IResultsProvider<Array<any>> {
+  private readonly context: IClientContext;
+ 
+  private readonly source: IResultsProvider<ArrowBatch>;
+ 
+  private readonly schema: Array<TColumnDesc>;
+ 
+  private recordBatchReader?: IterableIterator<RecordBatch<TypeMap>>;
+ 
+  // Remaining rows in current Arrow batch (not the record batch!)
+  private remainingRows: number = 0;
+ 
+  // This is the next (!!) record batch to be read. It is unset only in two cases:
+  // - prior to the first call to `fetchNext`
+  // - when no more data available
+  // This field is primarily used by a `hasMore`, so it can tell if next `fetchNext` will
+  // actually return a non-empty result
+  private prefetchedRecordBatch?: RecordBatch<TypeMap>;
+ 
+  constructor(context: IClientContext, source: IResultsProvider<ArrowBatch>, { schema }: TGetResultSetMetadataResp) {
+    this.context = context;
+    this.source = source;
+    this.schema = getSchemaColumns(schema);
+  }
+ 
+  public async hasMore() {
+    if (this.schema.length === 0) {
+      return false;
+    }
+    if (this.prefetchedRecordBatch) {
+      return true;
+    }
+    return this.source.hasMore();
+  }
+ 
+  public async fetchNext(options: ResultsProviderFetchNextOptions) {
+    if (this.schema.length === 0) {
+      return [];
+    }
+ 
+    // It's not possible to know if iterator has more items until trying to get the next item.
+    // So each time we read one batch ahead and store it, but process the batch prefetched on
+    // a previous `fetchNext` call. Because we actually already have the next item - it's easy
+    // to tell if the subsequent `fetchNext` will be able to read anything, and `hasMore` logic
+    // becomes trivial
+ 
+    // This prefetch handles a first call to `fetchNext`, when all the internal fields are not initialized yet.
+    // On subsequent calls to `fetchNext` it will do nothing
+    await this.prefetch(options);
+ 
+    if (this.prefetchedRecordBatch) {
+      // Consume a record batch fetched during previous call to `fetchNext`
+      const table = new Table(this.prefetchedRecordBatch);
+      this.prefetchedRecordBatch = undefined;
+      // Get table rows, but not more than remaining count
+      const arrowRows = table.toArray().slice(0, this.remainingRows);
+      const result = this.getRows(table.schema, arrowRows);
+ 
+      // Reduce remaining rows count by a count of rows we just processed.
+      // If the remaining count reached zero - we're done with current arrow
+      // batch, so discard the batch reader
+      this.remainingRows -= result.length;
+      if (this.remainingRows === 0) {
+        this.recordBatchReader = undefined;
+      }
+ 
+      // Prefetch the next record batch
+      await this.prefetch(options);
+ 
+      return result;
+    }
+ 
+    return [];
+  }
+ 
+  // This method tries to read one more record batch and store it in `prefetchedRecordBatch` field.
+  // If `prefetchedRecordBatch` is already non-empty - the method does nothing.
+  // This method pulls the next item from source if needed, initializes a record batch reader and
+  // gets the next item from it - until either reaches end of data or finds a non-empty record batch
+  private async prefetch(options: ResultsProviderFetchNextOptions) {
+    // This loop will be executed until a next non-empty record batch is retrieved
+    // Another implicit loop condition (end of data) is checked in the loop body
+    while (!this.prefetchedRecordBatch) {
+      // First, try to fetch next item from source and initialize record batch reader.
+      // If source has no more data - exit prematurely
+      if (!this.recordBatchReader) {
+        const sourceHasMore = await this.source.hasMore(); // eslint-disable-line no-await-in-loop
+        if (!sourceHasMore) {
+          return;
+        }
+ 
+        const arrowBatch = await this.source.fetchNext(options); // eslint-disable-line no-await-in-loop
+        if (arrowBatch.batches.length > 0 && arrowBatch.rowCount > 0) {
+          const reader = RecordBatchReader.from<TypeMap>(arrowBatch.batches);
+          this.recordBatchReader = reader[Symbol.iterator]();
+          this.remainingRows = arrowBatch.rowCount;
+        }
+      }
+ 
+      // Try to get a next item from current record batch reader. The reader may be unavailable at this point -
+      // in this case we fall back to a "done" state, and the `while` loop will do one more iteration attempting
+      // to create a new reader. Eventually it will either succeed or reach end of source. This scenario also
+      // handles readers which are already empty
+      const item = this.recordBatchReader?.next() ?? { done: true, value: undefined };
+      if (item.done || item.value === undefined) {
+        this.recordBatchReader = undefined;
+      } else {
+        // Skip empty batches
+        // eslint-disable-next-line no-lonely-if
+        if (item.value.numRows > 0) {
+          this.prefetchedRecordBatch = item.value;
+        }
+      }
+    }
+  }
+ 
+  private getRows(schema: ArrowSchema, rows: Array<StructRow | MapRow>): Array<any> {
+    return rows.map((row) => {
+      // First, convert native Arrow values to corresponding plain JS objects
+      const record = this.convertArrowTypes(row, undefined, schema.fields);
+      // Second, cast all the values to original Thrift types
+      return this.convertThriftTypes(record);
+    });
+  }
+ 
+  private convertArrowTypes(value: any, valueType: DataType | undefined, fields: Array<ArrowSchemaField> = []): any {
+    if (value === null) {
+      return value;
+    }
+ 
+    const fieldsMap: Record<string, ArrowSchemaField> = {};
+    for (const field of fields) {
+      fieldsMap[field.name] = field;
+    }
+ 
+    // Convert structures to plain JS object and process all its fields recursively
+    if (value instanceof StructRow) {
+      const result = value.toJSON();
+      for (const key of Object.keys(result)) {
+        const field: ArrowSchemaField | undefined = fieldsMap[key];
+        result[key] = this.convertArrowTypes(result[key], field?.type, field?.type.children || []);
+      }
+      return result;
+    }
+    if (value instanceof MapRow) {
+      const result = value.toJSON();
+      // Map type consists of its key and value types. We need only value type here, key will be cast to string anyway
+      const field = fieldsMap.entries?.type.children.find((item) => item.name === 'value');
+      for (const key of Object.keys(result)) {
+        result[key] = this.convertArrowTypes(result[key], field?.type, field?.type.children || []);
+      }
+      return result;
+    }
+ 
+    // Convert lists to JS array and process items recursively
+    if (value instanceof Vector) {
+      const result = value.toJSON();
+      // Array type contains the only child which defines a type of each array's element
+      const field = fieldsMap.element;
+      return result.map((item) => this.convertArrowTypes(item, field?.type, field?.type.children || []));
+    }
+ 
+    if (DataType.isTimestamp(valueType)) {
+      return new Date(value);
+    }
+ 
+    // Convert big number values to BigInt
+    // Decimals are also represented as big numbers in Arrow, so additionally process them (convert to float)
+    if (value instanceof Object && value[isArrowBigNumSymbol]) {
+      const result = bigNumToBigInt(value);
+      if (DataType.isDecimal(valueType)) {
+        return Number(result) / 10 ** valueType.scale;
+      }
+      return result;
+    }
+ 
+    // Convert binary data to Buffer
+    if (value instanceof Uint8Array) {
+      return Buffer.from(value);
+    }
+ 
+    // Return other values as is
+    return typeof value === 'bigint' ? Number(value) : value;
+  }
+ 
+  private convertThriftTypes(record: Record<string, any>): any {
+    const result: Record<string, any> = {};
+ 
+    this.schema.forEach((column) => {
+      const typeDescriptor = column.typeDesc.types[0]?.primitiveEntry;
+      const field = column.columnName;
+      const value = record[field];
+      result[field] = value === null ? null : convertThriftValue(typeDescriptor, value);
+    });
+ 
+    return result;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/ArrowResultHandler.ts.html b/coverage/lcov-report/lib/result/ArrowResultHandler.ts.html new file mode 100644 index 00000000..15272858 --- /dev/null +++ b/coverage/lcov-report/lib/result/ArrowResultHandler.ts.html @@ -0,0 +1,301 @@ + + + + + + Code coverage report for lib/result/ArrowResultHandler.ts + + + + + + + + + +
+
+

All files / lib/result ArrowResultHandler.ts

+
+ +
+ 16% + Statements + 4/25 +
+ + +
+ 0% + Branches + 0/30 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 16% + Lines + 4/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73  +1x +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { TGetResultSetMetadataResp, TRowSet } from '../../thrift/TCLIService_types';
+import HiveDriverError from '../errors/HiveDriverError';
+import IClientContext from '../contracts/IClientContext';
+import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
+import { ArrowBatch, hiveSchemaToArrowSchema } from './utils';
+import { LZ4 } from '../utils';
+ 
+export default class ArrowResultHandler implements IResultsProvider<ArrowBatch> {
+  private readonly context: IClientContext;
+ 
+  private readonly source: IResultsProvider<TRowSet | undefined>;
+ 
+  private readonly arrowSchema?: Buffer;
+ 
+  private readonly isLZ4Compressed: boolean;
+ 
+  constructor(
+    context: IClientContext,
+    source: IResultsProvider<TRowSet | undefined>,
+    { schema, arrowSchema, lz4Compressed }: TGetResultSetMetadataResp,
+  ) {
+    this.context = context;
+    this.source = source;
+    // Arrow schema is not available in old DBR versions, which also don't support native Arrow types,
+    // so it's possible to infer Arrow schema from Hive schema ignoring `useArrowNativeTypes` option
+    this.arrowSchema = arrowSchema ?? hiveSchemaToArrowSchema(schema);
+    this.isLZ4Compressed = lz4Compressed ?? false;
+ 
+    if (this.isLZ4Compressed && !LZ4()) {
+      throw new HiveDriverError('Cannot handle LZ4 compressed result: module `lz4` not installed');
+    }
+  }
+ 
+  public async hasMore() {
+    if (!this.arrowSchema) {
+      return false;
+    }
+    return this.source.hasMore();
+  }
+ 
+  public async fetchNext(options: ResultsProviderFetchNextOptions) {
+    if (!this.arrowSchema) {
+      return {
+        batches: [],
+        rowCount: 0,
+      };
+    }
+ 
+    const rowSet = await this.source.fetchNext(options);
+ 
+    const batches: Array<Buffer> = [];
+    let totalRowCount = 0;
+    rowSet?.arrowBatches?.forEach(({ batch, rowCount }) => {
+      if (batch) {
+        batches.push(this.isLZ4Compressed ? LZ4()!.decode(batch) : batch);
+        totalRowCount += rowCount.toNumber(true);
+      }
+    });
+ 
+    if (batches.length === 0) {
+      return {
+        batches: [],
+        rowCount: 0,
+      };
+    }
+ 
+    return {
+      batches: [this.arrowSchema, ...batches],
+      rowCount: totalRowCount,
+    };
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html b/coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html new file mode 100644 index 00000000..3b6cf6ac --- /dev/null +++ b/coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html @@ -0,0 +1,466 @@ + + + + + + Code coverage report for lib/result/CloudFetchResultHandler.ts + + + + + + + + + +
+
+

All files / lib/result CloudFetchResultHandler.ts

+
+ +
+ 8.77% + Statements + 5/57 +
+ + +
+ 0% + Branches + 0/32 +
+ + +
+ 0% + Functions + 0/11 +
+ + +
+ 9.25% + Lines + 5/54 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +1281x +  +1x +  +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import fetch, { RequestInfo, RequestInit, Request } from 'node-fetch';
+import { TGetResultSetMetadataResp, TRowSet, TSparkArrowResultLink } from '../../thrift/TCLIService_types';
+import HiveDriverError from '../errors/HiveDriverError';
+import IClientContext from '../contracts/IClientContext';
+import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
+import { ArrowBatch } from './utils';
+import { LZ4 } from '../utils';
+import { LogLevel } from '../contracts/IDBSQLLogger';
+ 
+export default class CloudFetchResultHandler implements IResultsProvider<ArrowBatch> {
+  private readonly context: IClientContext;
+ 
+  private readonly source: IResultsProvider<TRowSet | undefined>;
+ 
+  private readonly isLZ4Compressed: boolean;
+ 
+  private pendingLinks: Array<TSparkArrowResultLink> = [];
+ 
+  private downloadTasks: Array<Promise<ArrowBatch>> = [];
+ 
+  constructor(
+    context: IClientContext,
+    source: IResultsProvider<TRowSet | undefined>,
+    { lz4Compressed }: TGetResultSetMetadataResp,
+  ) {
+    this.context = context;
+    this.source = source;
+    this.isLZ4Compressed = lz4Compressed ?? false;
+ 
+    if (this.isLZ4Compressed && !LZ4()) {
+      throw new HiveDriverError('Cannot handle LZ4 compressed result: module `lz4` not installed');
+    }
+  }
+ 
+  public async hasMore() {
+    if (this.pendingLinks.length > 0 || this.downloadTasks.length > 0) {
+      return true;
+    }
+    return this.source.hasMore();
+  }
+ 
+  public async fetchNext(options: ResultsProviderFetchNextOptions) {
+    const data = await this.source.fetchNext(options);
+ 
+    data?.resultLinks?.forEach((link) => {
+      this.pendingLinks.push(link);
+    });
+ 
+    const clientConfig = this.context.getConfig();
+    const freeTaskSlotsCount = clientConfig.cloudFetchConcurrentDownloads - this.downloadTasks.length;
+ 
+    if (freeTaskSlotsCount > 0) {
+      const links = this.pendingLinks.splice(0, freeTaskSlotsCount);
+      const tasks = links.map((link) => this.downloadLink(link));
+      this.downloadTasks.push(...tasks);
+    }
+ 
+    const batch = await this.downloadTasks.shift();
+    if (!batch) {
+      return {
+        batches: [],
+        rowCount: 0,
+      };
+    }
+ 
+    if (this.isLZ4Compressed) {
+      batch.batches = batch.batches.map((buffer) => LZ4()!.decode(buffer));
+    }
+    return batch;
+  }
+ 
+  private logDownloadMetrics(url: string, fileSizeBytes: number, downloadTimeMs: number): void {
+    const speedMBps = fileSizeBytes / (1024 * 1024) / (downloadTimeMs / 1000);
+    const cleanUrl = url.split('?')[0];
+ 
+    this.context
+      .getLogger()
+      .log(LogLevel.info, `Result File Download speed from cloud storage ${cleanUrl}: ${speedMBps.toFixed(4)} MB/s`);
+ 
+    const speedThresholdMBps = this.context.getConfig().cloudFetchSpeedThresholdMBps;
+    if (speedMBps < speedThresholdMBps) {
+      this.context
+        .getLogger()
+        .log(
+          LogLevel.warn,
+          `Results download is slower than threshold speed of ${speedThresholdMBps.toFixed(
+            4,
+          )} MB/s: ${speedMBps.toFixed(4)} MB/s`,
+        );
+    }
+  }
+ 
+  private async downloadLink(link: TSparkArrowResultLink): Promise<ArrowBatch> {
+    if (Date.now() >= link.expiryTime.toNumber()) {
+      throw new Error('CloudFetch link has expired');
+    }
+ 
+    const startTime = Date.now();
+    const response = await this.fetch(link.fileLink, { headers: link.httpHeaders });
+    if (!response.ok) {
+      throw new Error(`CloudFetch HTTP error ${response.status} ${response.statusText}`);
+    }
+ 
+    const result = await response.arrayBuffer();
+    const downloadTimeMs = Date.now() - startTime;
+ 
+    this.logDownloadMetrics(link.fileLink, result.byteLength, downloadTimeMs);
+ 
+    return {
+      batches: [Buffer.from(result)],
+      rowCount: link.rowCount.toNumber(true),
+    };
+  }
+ 
+  private async fetch(url: RequestInfo, init?: RequestInit) {
+    const connectionProvider = await this.context.getConnectionProvider();
+    const agent = await connectionProvider.getAgent();
+    const retryPolicy = await connectionProvider.getRetryPolicy();
+ 
+    const requestConfig: RequestInit = { agent, ...init };
+    const result = await retryPolicy.invokeWithRetry(() => {
+      const request = new Request(url, requestConfig);
+      return fetch(request).then((response) => ({ request, response }));
+    });
+    return result.response;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/JsonResultHandler.ts.html b/coverage/lcov-report/lib/result/JsonResultHandler.ts.html new file mode 100644 index 00000000..f00443e4 --- /dev/null +++ b/coverage/lcov-report/lib/result/JsonResultHandler.ts.html @@ -0,0 +1,328 @@ + + + + + + Code coverage report for lib/result/JsonResultHandler.ts + + + + + + + + + +
+
+

All files / lib/result JsonResultHandler.ts

+
+ +
+ 6.45% + Statements + 2/31 +
+ + +
+ 0% + Branches + 0/18 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 6.45% + Lines + 2/31 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { TGetResultSetMetadataResp, TRowSet, TColumn, TColumnDesc } from '../../thrift/TCLIService_types';
+import IClientContext from '../contracts/IClientContext';
+import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
+import { getSchemaColumns, convertThriftValue, getColumnValue } from './utils';
+ 
+export default class JsonResultHandler implements IResultsProvider<Array<any>> {
+  private readonly context: IClientContext;
+ 
+  private readonly source: IResultsProvider<TRowSet | undefined>;
+ 
+  private readonly schema: Array<TColumnDesc>;
+ 
+  constructor(
+    context: IClientContext,
+    source: IResultsProvider<TRowSet | undefined>,
+    { schema }: TGetResultSetMetadataResp,
+  ) {
+    this.context = context;
+    this.source = source;
+    this.schema = getSchemaColumns(schema);
+  }
+ 
+  public async hasMore() {
+    return this.source.hasMore();
+  }
+ 
+  public async fetchNext(options: ResultsProviderFetchNextOptions) {
+    if (this.schema.length === 0) {
+      return [];
+    }
+ 
+    const data = await this.source.fetchNext(options);
+    if (!data) {
+      return [];
+    }
+ 
+    const columns = data.columns || [];
+    return this.getRows(columns, this.schema);
+  }
+ 
+  private getRows(columns: Array<TColumn>, descriptors: Array<TColumnDesc>): Array<any> {
+    return descriptors.reduce(
+      (rows, descriptor) =>
+        this.getSchemaValues(descriptor, columns[descriptor.position - 1]).reduce((result, value, i) => {
+          if (!result[i]) {
+            result[i] = {};
+          }
+ 
+          const { columnName } = descriptor;
+ 
+          result[i][columnName] = value;
+ 
+          return result;
+        }, rows),
+      [],
+    );
+  }
+ 
+  private getSchemaValues(descriptor: TColumnDesc, column?: TColumn): Array<any> {
+    const typeDescriptor = descriptor.typeDesc.types[0]?.primitiveEntry;
+    const columnValue = getColumnValue(column);
+ 
+    if (!columnValue) {
+      return [];
+    }
+ 
+    return columnValue.values.map((value: any, i: number) => {
+      if (columnValue.nulls && this.isNull(columnValue.nulls, i)) {
+        return null;
+      }
+      return convertThriftValue(typeDescriptor, value);
+    });
+  }
+ 
+  private isNull(nulls: Buffer, i: number): boolean {
+    const byte = nulls[Math.floor(i / 8)];
+    const ofs = 2 ** (i % 8);
+ 
+    return (byte & ofs) !== 0;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/ResultSlicer.ts.html b/coverage/lcov-report/lib/result/ResultSlicer.ts.html new file mode 100644 index 00000000..80e69a90 --- /dev/null +++ b/coverage/lcov-report/lib/result/ResultSlicer.ts.html @@ -0,0 +1,313 @@ + + + + + + Code coverage report for lib/result/ResultSlicer.ts + + + + + + + + + +
+
+

All files / lib/result ResultSlicer.ts

+
+ +
+ 3.12% + Statements + 1/32 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 3.12% + Lines + 1/32 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import IClientContext from '../contracts/IClientContext';
+import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
+ 
+export interface ResultSlicerFetchNextOptions extends ResultsProviderFetchNextOptions {
+  // Setting this to `true` will disable slicer, and it will return unprocessed chunks
+  // from underlying results provider
+  disableBuffering?: boolean;
+}
+ 
+export default class ResultSlicer<T> implements IResultsProvider<Array<T>> {
+  private readonly context: IClientContext;
+ 
+  private readonly source: IResultsProvider<Array<T>>;
+ 
+  private remainingResults: Array<T> = [];
+ 
+  constructor(context: IClientContext, source: IResultsProvider<Array<T>>) {
+    this.context = context;
+    this.source = source;
+  }
+ 
+  public async hasMore(): Promise<boolean> {
+    if (this.remainingResults.length > 0) {
+      return true;
+    }
+    return this.source.hasMore();
+  }
+ 
+  public async fetchNext(options: ResultSlicerFetchNextOptions): Promise<Array<T>> {
+    // If we're asked to not use buffer - first try to return whatever we have in buffer.
+    // If buffer is empty - just proxy the call to underlying results provider
+    if (options.disableBuffering) {
+      if (this.remainingResults.length > 0) {
+        const result = this.remainingResults;
+        this.remainingResults = [];
+        return result;
+      }
+ 
+      return this.source.fetchNext(options);
+    }
+ 
+    const result: Array<Array<T>> = [];
+    let resultsCount = 0;
+ 
+    // First, use remaining items from the previous fetch
+    if (this.remainingResults.length > 0) {
+      result.push(this.remainingResults);
+      resultsCount += this.remainingResults.length;
+      this.remainingResults = [];
+    }
+ 
+    // Fetch items from source results provider until we reach a requested count
+    while (resultsCount < options.limit) {
+      // eslint-disable-next-line no-await-in-loop
+      const hasMore = await this.source.hasMore();
+      if (!hasMore) {
+        break;
+      }
+ 
+      // eslint-disable-next-line no-await-in-loop
+      const chunk = await this.source.fetchNext(options);
+      result.push(chunk);
+      resultsCount += chunk.length;
+    }
+ 
+    // If we collected more results than requested, slice the excess items and store them for the next time
+    if (resultsCount > options.limit) {
+      const lastChunk = result.pop() ?? [];
+      const neededCount = options.limit - (resultsCount - lastChunk.length);
+      result.push(lastChunk.splice(0, neededCount));
+      this.remainingResults = lastChunk;
+    }
+ 
+    return result.flat();
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/RowSetProvider.ts.html b/coverage/lcov-report/lib/result/RowSetProvider.ts.html new file mode 100644 index 00000000..79832b4f --- /dev/null +++ b/coverage/lcov-report/lib/result/RowSetProvider.ts.html @@ -0,0 +1,412 @@ + + + + + + Code coverage report for lib/result/RowSetProvider.ts + + + + + + + + + +
+
+

All files / lib/result RowSetProvider.ts

+
+ +
+ 19.04% + Statements + 8/42 +
+ + +
+ 5.26% + Branches + 2/38 +
+ + +
+ 12.5% + Functions + 1/8 +
+ + +
+ 19.04% + Lines + 8/42 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +1101x +1x +1x +  +  +1x +  +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Int64 from 'node-int64';
+import { TFetchOrientation, TFetchResultsResp, TOperationHandle, TRowSet } from '../../thrift/TCLIService_types';
+import Status from '../dto/Status';
+import IClientContext from '../contracts/IClientContext';
+import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
+import { getColumnValue } from './utils';
+ 
+export enum FetchType {
+  Data = 0,
+  Logs = 1,
+}
+ 
+function checkIfOperationHasMoreRows(response: TFetchResultsResp): boolean {
+  if (response.hasMoreRows) {
+    return true;
+  }
+ 
+  const columns = response.results?.columns || [];
+ 
+  const columnValue = getColumnValue(columns[0]);
+  return (columnValue?.values?.length ?? 0) > 0;
+}
+ 
+export default class RowSetProvider implements IResultsProvider<TRowSet | undefined> {
+  private readonly context: IClientContext;
+ 
+  private readonly operationHandle: TOperationHandle;
+ 
+  private fetchOrientation: TFetchOrientation = TFetchOrientation.FETCH_FIRST;
+ 
+  private prefetchedResults: TFetchResultsResp[] = [];
+ 
+  private readonly returnOnlyPrefetchedResults: boolean;
+ 
+  private hasMoreRowsFlag?: boolean = undefined;
+ 
+  private get hasMoreRows(): boolean {
+    // `hasMoreRowsFlag` is populated only after fetching the first row set.
+    // Prior to that, we use a `operationHandle.hasResultSet` flag which
+    // is set if there are any data at all. Also, we have to choose appropriate
+    // flag in a getter because both `hasMoreRowsFlag` and `operationHandle.hasResultSet`
+    // may change between this getter calls
+    return this.hasMoreRowsFlag ?? this.operationHandle.hasResultSet;
+  }
+ 
+  constructor(
+    context: IClientContext,
+    operationHandle: TOperationHandle,
+    prefetchedResults: Array<TFetchResultsResp | undefined>,
+    returnOnlyPrefetchedResults: boolean,
+  ) {
+    this.context = context;
+    this.operationHandle = operationHandle;
+    prefetchedResults.forEach((item) => {
+      if (item) {
+        this.prefetchedResults.push(item);
+      }
+    });
+    this.returnOnlyPrefetchedResults = returnOnlyPrefetchedResults;
+  }
+ 
+  private processFetchResponse(response: TFetchResultsResp): TRowSet | undefined {
+    Status.assert(response.status);
+    this.fetchOrientation = TFetchOrientation.FETCH_NEXT;
+    this.hasMoreRowsFlag = checkIfOperationHasMoreRows(response);
+    return response.results;
+  }
+ 
+  public async fetchNext({ limit }: ResultsProviderFetchNextOptions) {
+    const prefetchedResponse = this.prefetchedResults.shift();
+    if (prefetchedResponse) {
+      return this.processFetchResponse(prefetchedResponse);
+    }
+ 
+    // We end up here if no more prefetched results available (checked above)
+    if (this.returnOnlyPrefetchedResults) {
+      return undefined;
+    }
+ 
+    // Don't fetch next chunk if there are no more data available
+    if (!this.hasMoreRows) {
+      return undefined;
+    }
+ 
+    const driver = await this.context.getDriver();
+    const response = await driver.fetchResults({
+      operationHandle: this.operationHandle,
+      orientation: this.fetchOrientation,
+      maxRows: new Int64(limit),
+      fetchType: FetchType.Data,
+    });
+ 
+    return this.processFetchResponse(response);
+  }
+ 
+  public async hasMore() {
+    // If there are prefetched results available - return `true` regardless of
+    // the actual state of `hasMoreRows` flag (because we actually have some data)
+    if (this.prefetchedResults.length > 0) {
+      return true;
+    }
+    // We end up here if no more prefetched results available (checked above)
+    if (this.returnOnlyPrefetchedResults) {
+      return false;
+    }
+ 
+    return this.hasMoreRows;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/index.html b/coverage/lcov-report/lib/result/index.html new file mode 100644 index 00000000..3ed33e30 --- /dev/null +++ b/coverage/lcov-report/lib/result/index.html @@ -0,0 +1,206 @@ + + + + + + Code coverage report for lib/result + + + + + + + + + +
+
+

All files lib/result

+
+ +
+ 10.49% + Statements + 34/324 +
+ + +
+ 0.64% + Branches + 2/308 +
+ + +
+ 1.81% + Functions + 1/55 +
+ + +
+ 10.69% + Lines + 34/318 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ArrowResultConverter.ts +
+
5.81%5/860%0/930%0/115.95%5/84
ArrowResultHandler.ts +
+
16%4/250%0/300%0/416%4/25
CloudFetchResultHandler.ts +
+
8.77%5/570%0/320%0/119.25%5/54
JsonResultHandler.ts +
+
6.45%2/310%0/180%0/96.45%2/31
ResultSlicer.ts +
+
3.12%1/320%0/160%0/33.12%1/32
RowSetProvider.ts +
+
19.04%8/425.26%2/3812.5%1/819.04%8/42
utils.ts +
+
17.64%9/510%0/810%0/918%9/50
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/utils.ts.html b/coverage/lcov-report/lib/result/utils.ts.html new file mode 100644 index 00000000..77b41a4d --- /dev/null +++ b/coverage/lcov-report/lib/result/utils.ts.html @@ -0,0 +1,574 @@ + + + + + + Code coverage report for lib/result/utils.ts + + + + + + + + + +
+
+

All files / lib/result utils.ts

+
+ +
+ 17.64% + Statements + 9/51 +
+ + +
+ 0% + Branches + 0/81 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 18% + Lines + 9/50 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +1641x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import Int64 from 'node-int64';
+import {
+  Schema,
+  Field,
+  DataType,
+  Bool as ArrowBool,
+  Int8 as ArrowInt8,
+  Int16 as ArrowInt16,
+  Int32 as ArrowInt32,
+  Int64 as ArrowInt64,
+  Float32 as ArrowFloat32,
+  Float64 as ArrowFloat64,
+  Utf8 as ArrowString,
+  Date_ as ArrowDate,
+  Binary as ArrowBinary,
+  DateUnit,
+  RecordBatchWriter,
+} from 'apache-arrow';
+import { TTableSchema, TColumnDesc, TPrimitiveTypeEntry, TTypeId, TColumn } from '../../thrift/TCLIService_types';
+import HiveDriverError from '../errors/HiveDriverError';
+ 
+export interface ArrowBatch {
+  batches: Array<Buffer>;
+  rowCount: number;
+}
+ 
+export function getSchemaColumns(schema?: TTableSchema): Array<TColumnDesc> {
+  if (!schema) {
+    return [];
+  }
+ 
+  return [...schema.columns].sort((c1, c2) => c1.position - c2.position);
+}
+ 
+function isString(value: unknown): value is string {
+  return typeof value === 'string' || value instanceof String;
+}
+ 
+function convertJSON(value: any, defaultValue: any): any {
+  if (!isString(value)) {
+    return value;
+  }
+ 
+  try {
+    return JSON.parse(value);
+  } catch (e) {
+    return defaultValue;
+  }
+}
+ 
+function convertBigInt(value: any): any {
+  if (typeof value === 'bigint') {
+    return Number(value);
+  }
+  if (value instanceof Int64) {
+    return value.toNumber();
+  }
+  return value;
+}
+ 
+export function convertThriftValue(typeDescriptor: TPrimitiveTypeEntry | undefined, value: any): any {
+  if (!typeDescriptor) {
+    return value;
+  }
+ 
+  switch (typeDescriptor.type) {
+    case TTypeId.DATE_TYPE:
+    case TTypeId.TIMESTAMP_TYPE:
+      return value;
+    case TTypeId.UNION_TYPE:
+    case TTypeId.USER_DEFINED_TYPE:
+      return String(value);
+    case TTypeId.DECIMAL_TYPE:
+      return Number(value);
+    case TTypeId.STRUCT_TYPE:
+    case TTypeId.MAP_TYPE:
+      return convertJSON(value, {});
+    case TTypeId.ARRAY_TYPE:
+      return convertJSON(value, []);
+    case TTypeId.BIGINT_TYPE:
+      return convertBigInt(value);
+    case TTypeId.NULL_TYPE:
+    case TTypeId.BINARY_TYPE:
+    case TTypeId.INTERVAL_YEAR_MONTH_TYPE:
+    case TTypeId.INTERVAL_DAY_TIME_TYPE:
+    case TTypeId.FLOAT_TYPE:
+    case TTypeId.DOUBLE_TYPE:
+    case TTypeId.INT_TYPE:
+    case TTypeId.SMALLINT_TYPE:
+    case TTypeId.TINYINT_TYPE:
+    case TTypeId.BOOLEAN_TYPE:
+    case TTypeId.STRING_TYPE:
+    case TTypeId.CHAR_TYPE:
+    case TTypeId.VARCHAR_TYPE:
+    default:
+      return value;
+  }
+}
+ 
+// This type map corresponds to Arrow without native types support (most complex types are serialized as strings)
+const hiveTypeToArrowType: Record<TTypeId, DataType | null> = {
+  [TTypeId.BOOLEAN_TYPE]: new ArrowBool(),
+  [TTypeId.TINYINT_TYPE]: new ArrowInt8(),
+  [TTypeId.SMALLINT_TYPE]: new ArrowInt16(),
+  [TTypeId.INT_TYPE]: new ArrowInt32(),
+  [TTypeId.BIGINT_TYPE]: new ArrowInt64(),
+  [TTypeId.FLOAT_TYPE]: new ArrowFloat32(),
+  [TTypeId.DOUBLE_TYPE]: new ArrowFloat64(),
+  [TTypeId.STRING_TYPE]: new ArrowString(),
+  [TTypeId.TIMESTAMP_TYPE]: new ArrowString(),
+  [TTypeId.BINARY_TYPE]: new ArrowBinary(),
+  [TTypeId.ARRAY_TYPE]: new ArrowString(),
+  [TTypeId.MAP_TYPE]: new ArrowString(),
+  [TTypeId.STRUCT_TYPE]: new ArrowString(),
+  [TTypeId.UNION_TYPE]: new ArrowString(),
+  [TTypeId.USER_DEFINED_TYPE]: new ArrowString(),
+  [TTypeId.DECIMAL_TYPE]: new ArrowString(),
+  [TTypeId.NULL_TYPE]: null,
+  [TTypeId.DATE_TYPE]: new ArrowDate(DateUnit.DAY),
+  [TTypeId.VARCHAR_TYPE]: new ArrowString(),
+  [TTypeId.CHAR_TYPE]: new ArrowString(),
+  [TTypeId.INTERVAL_YEAR_MONTH_TYPE]: new ArrowString(),
+  [TTypeId.INTERVAL_DAY_TIME_TYPE]: new ArrowString(),
+};
+ 
+export function hiveSchemaToArrowSchema(schema?: TTableSchema): Buffer | undefined {
+  if (!schema) {
+    return undefined;
+  }
+ 
+  const columns = getSchemaColumns(schema);
+ 
+  const arrowFields = columns.map((column) => {
+    const hiveType = column.typeDesc.types[0].primitiveEntry?.type ?? undefined;
+    const arrowType = hiveType !== undefined ? hiveTypeToArrowType[hiveType] : undefined;
+    if (!arrowType) {
+      throw new HiveDriverError(`Unsupported column type: ${hiveType ? TTypeId[hiveType] : 'undefined'}`);
+    }
+    return new Field(column.columnName, arrowType, true);
+  });
+ 
+  const arrowSchema = new Schema(arrowFields);
+  const writer = new RecordBatchWriter();
+  writer.reset(undefined, arrowSchema);
+  writer.finish();
+  return Buffer.from(writer.toUint8Array(true));
+}
+ 
+export function getColumnValue(column?: TColumn) {
+  if (!column) {
+    return undefined;
+  }
+  return (
+    column.binaryVal ??
+    column.boolVal ??
+    column.byteVal ??
+    column.doubleVal ??
+    column.i16Val ??
+    column.i32Val ??
+    column.i64Val ??
+    column.stringVal
+  );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html b/coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html new file mode 100644 index 00000000..38a0463c --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html @@ -0,0 +1,793 @@ + + + + + + Code coverage report for lib/telemetry/CircuitBreaker.ts + + + + + + + + + +
+
+

All files / lib/telemetry CircuitBreaker.ts

+
+ +
+ 100% + Statements + 61/61 +
+ + +
+ 100% + Branches + 18/18 +
+ + +
+ 100% + Functions + 13/13 +
+ + +
+ 100% + Lines + 61/61 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +1x +  +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +50x +  +50x +  +50x +  +  +  +  +  +50x +50x +  +  +  +  +  +  +  +  +  +  +  +  +  +110x +  +  +110x +13x +5x +  +  +8x +8x +8x +  +  +105x +105x +20x +20x +  +85x +85x +  +  +  +  +  +  +  +27x +  +  +  +  +  +  +9x +  +  +  +  +  +  +5x +  +  +  +  +  +  +20x +  +  +20x +  +20x +11x +11x +  +  +  +  +11x +  +3x +3x +3x +3x +  +  +  +  +  +  +  +  +85x +  +85x +85x +  +85x +  +  +  +85x +18x +18x +18x +  +  +  +  +  +  +  +  +  +1x +  +  +27x +27x +  +  +  +  +  +  +  +  +  +  +34x +34x +31x +31x +31x +31x +  +34x +  +  +  +  +  +  +  +6x +  +  +  +  +  +  +  +  +  +3x +3x +3x +  +  +  +  +  +  +  +1x +  +  + 
/**
+ * Copyright (c) 2025 Databricks Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+import IClientContext from '../contracts/IClientContext';
+import { LogLevel } from '../contracts/IDBSQLLogger';
+ 
+/**
+ * States of the circuit breaker.
+ */
+export enum CircuitBreakerState {
+  /** Normal operation, requests pass through */
+  CLOSED = 'CLOSED',
+  /** After threshold failures, all requests rejected immediately */
+  OPEN = 'OPEN',
+  /** After timeout, allows test requests to check if endpoint recovered */
+  HALF_OPEN = 'HALF_OPEN',
+}
+ 
+/**
+ * Configuration for circuit breaker behavior.
+ */
+export interface CircuitBreakerConfig {
+  /** Number of consecutive failures before opening the circuit */
+  failureThreshold: number;
+  /** Time in milliseconds to wait before attempting recovery */
+  timeout: number;
+  /** Number of consecutive successes in HALF_OPEN state to close the circuit */
+  successThreshold: number;
+}
+ 
+/**
+ * Default circuit breaker configuration.
+ */
+export const DEFAULT_CIRCUIT_BREAKER_CONFIG: CircuitBreakerConfig = {
+  failureThreshold: 5,
+  timeout: 60000, // 1 minute
+  successThreshold: 2,
+};
+ 
+/**
+ * Circuit breaker for telemetry exporter.
+ * Protects against failing telemetry endpoint with automatic recovery.
+ *
+ * States:
+ * - CLOSED: Normal operation, requests pass through
+ * - OPEN: After threshold failures, all requests rejected immediately
+ * - HALF_OPEN: After timeout, allows test requests to check if endpoint recovered
+ */
+export class CircuitBreaker {
+  private state: CircuitBreakerState = CircuitBreakerState.CLOSED;
+ 
+  private failureCount = 0;
+ 
+  private successCount = 0;
+ 
+  private nextAttempt?: Date;
+ 
+  private readonly config: CircuitBreakerConfig;
+ 
+  constructor(private context: IClientContext, config?: Partial<CircuitBreakerConfig>) {
+    this.config = {
+      ...DEFAULT_CIRCUIT_BREAKER_CONFIG,
+      ...config,
+    };
+  }
+ 
+  /**
+   * Executes an operation with circuit breaker protection.
+   *
+   * @param operation The operation to execute
+   * @returns Promise resolving to the operation result
+   * @throws Error if circuit is OPEN or operation fails
+   */
+  async execute<T>(operation: () => Promise<T>): Promise<T> {
+    const logger = this.context.getLogger();
+ 
+    // Check if circuit is open
+    if (this.state === CircuitBreakerState.OPEN) {
+      if (this.nextAttempt && Date.now() < this.nextAttempt.getTime()) {
+        throw new Error('Circuit breaker OPEN');
+      }
+      // Timeout expired, transition to HALF_OPEN
+      this.state = CircuitBreakerState.HALF_OPEN;
+      this.successCount = 0;
+      logger.log(LogLevel.debug, 'Circuit breaker transitioned to HALF_OPEN');
+    }
+ 
+    try {
+      const result = await operation();
+      this.onSuccess();
+      return result;
+    } catch (error) {
+      this.onFailure();
+      throw error;
+    }
+  }
+ 
+  /**
+   * Gets the current state of the circuit breaker.
+   */
+  getState(): CircuitBreakerState {
+    return this.state;
+  }
+ 
+  /**
+   * Gets the current failure count.
+   */
+  getFailureCount(): number {
+    return this.failureCount;
+  }
+ 
+  /**
+   * Gets the current success count (relevant in HALF_OPEN state).
+   */
+  getSuccessCount(): number {
+    return this.successCount;
+  }
+ 
+  /**
+   * Handles successful operation execution.
+   */
+  private onSuccess(): void {
+    const logger = this.context.getLogger();
+ 
+    // Reset failure count on any success
+    this.failureCount = 0;
+ 
+    if (this.state === CircuitBreakerState.HALF_OPEN) {
+      this.successCount += 1;
+      logger.log(
+        LogLevel.debug,
+        `Circuit breaker success in HALF_OPEN (${this.successCount}/${this.config.successThreshold})`,
+      );
+ 
+      if (this.successCount >= this.config.successThreshold) {
+        // Transition to CLOSED
+        this.state = CircuitBreakerState.CLOSED;
+        this.successCount = 0;
+        this.nextAttempt = undefined;
+        logger.log(LogLevel.debug, 'Circuit breaker transitioned to CLOSED');
+      }
+    }
+  }
+ 
+  /**
+   * Handles failed operation execution.
+   */
+  private onFailure(): void {
+    const logger = this.context.getLogger();
+ 
+    this.failureCount += 1;
+    this.successCount = 0; // Reset success count on failure
+ 
+    logger.log(LogLevel.debug, `Circuit breaker failure (${this.failureCount}/${this.config.failureThreshold})`);
+ 
+    // In HALF_OPEN state, any failure immediately reopens the circuit.
+    // In CLOSED state, reopen only after failureThreshold consecutive failures.
+    if (this.state === CircuitBreakerState.HALF_OPEN || this.failureCount >= this.config.failureThreshold) {
+      this.state = CircuitBreakerState.OPEN;
+      this.nextAttempt = new Date(Date.now() + this.config.timeout);
+      logger.log(LogLevel.debug, `Circuit breaker transitioned to OPEN (will retry after ${this.config.timeout}ms)`);
+    }
+  }
+}
+ 
+/**
+ * Manages circuit breakers per host.
+ * Ensures each host has its own isolated circuit breaker to prevent
+ * failures on one host from affecting telemetry to other hosts.
+ */
+export class CircuitBreakerRegistry {
+  private breakers: Map<string, CircuitBreaker>;
+ 
+  constructor(private context: IClientContext) {
+    this.breakers = new Map();
+  }
+ 
+  /**
+   * Gets or creates a circuit breaker for the specified host.
+   *
+   * @param host The host identifier (e.g., "workspace.cloud.databricks.com")
+   * @param config Optional configuration overrides
+   * @returns Circuit breaker for the host
+   */
+  getCircuitBreaker(host: string, config?: Partial<CircuitBreakerConfig>): CircuitBreaker {
+    let breaker = this.breakers.get(host);
+    if (!breaker) {
+      breaker = new CircuitBreaker(this.context, config);
+      this.breakers.set(host, breaker);
+      const logger = this.context.getLogger();
+      logger.log(LogLevel.debug, `Created circuit breaker for host: ${host}`);
+    }
+    return breaker;
+  }
+ 
+  /**
+   * Gets all registered circuit breakers.
+   * Useful for testing and diagnostics.
+   */
+  getAllBreakers(): Map<string, CircuitBreaker> {
+    return new Map(this.breakers);
+  }
+ 
+  /**
+   * Removes a circuit breaker for the specified host.
+   * Useful for cleanup when a host is no longer in use.
+   *
+   * @param host The host identifier
+   */
+  removeCircuitBreaker(host: string): void {
+    this.breakers.delete(host);
+    const logger = this.context.getLogger();
+    logger.log(LogLevel.debug, `Removed circuit breaker for host: ${host}`);
+  }
+ 
+  /**
+   * Clears all circuit breakers.
+   * Useful for testing.
+   */
+  clear(): void {
+    this.breakers.clear();
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html b/coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html new file mode 100644 index 00000000..77c07e2c --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html @@ -0,0 +1,1171 @@ + + + + + + Code coverage report for lib/telemetry/DatabricksTelemetryExporter.ts + + + + + + + + + +
+
+

All files / lib/telemetry DatabricksTelemetryExporter.ts

+
+ +
+ 88.76% + Statements + 79/89 +
+ + +
+ 68.96% + Branches + 40/58 +
+ + +
+ 100% + Functions + 14/14 +
+ + +
+ 88.37% + Lines + 76/86 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +14x +14x +14x +  +  +14x +14x +  +  +14x +  +  +  +  +  +  +  +  +16x +1x +  +  +15x +  +15x +15x +13x +  +  +  +9x +2x +  +7x +  +  +  +  +  +  +  +  +  +13x +13x +13x +  +13x +  +  +13x +17x +17x +6x +  +11x +  +  +11x +2x +2x +  +  +  +9x +2x +2x +  +  +  +7x +3x +3x +  +  +  +4x +4x +4x +  +4x +  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +17x +17x +  +  +17x +17x +  +  +  +  +18x +18x +  +17x +  +  +  +  +  +17x +  +  +  +  +  +  +  +17x +  +  +17x +17x +  +  +17x +  +  +  +  +  +  +  +  +  +  +15x +9x +9x +9x +  +  +6x +  +  +  +  +  +  +18x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +18x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +18x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +18x +  +  +  +  +  +  +18x +  +  +  +  +  +  +17x +1x +  +16x +  +  +  +  +  +  +18x +558x +558x +558x +  +  +  +  +  +  +  +14x +  +14x +  +  +  +  +  +  +  +  +  +4x +4x +  +  +  + 
/**
+ * Copyright (c) 2025 Databricks Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+import fetch, { Response } from 'node-fetch';
+import IClientContext from '../contracts/IClientContext';
+import { LogLevel } from '../contracts/IDBSQLLogger';
+import { TelemetryMetric, DEFAULT_TELEMETRY_CONFIG } from './types';
+import { CircuitBreakerRegistry } from './CircuitBreaker';
+import ExceptionClassifier from './ExceptionClassifier';
+ 
+/**
+ * Databricks telemetry log format for export.
+ */
+interface DatabricksTelemetryLog {
+  workspace_id?: string;
+  frontend_log_event_id: string;
+  context: {
+    client_context: {
+      timestamp_millis: number;
+      user_agent: string;
+    };
+  };
+  entry: {
+    sql_driver_log: {
+      session_id?: string;
+      sql_statement_id?: string;
+      system_configuration?: {
+        driver_version?: string;
+        runtime_name?: string;
+        runtime_version?: string;
+        runtime_vendor?: string;
+        os_name?: string;
+        os_version?: string;
+        os_arch?: string;
+        driver_name?: string;
+        client_app_name?: string;
+        locale_name?: string;
+        char_set_encoding?: string;
+        process_name?: string;
+      };
+      driver_connection_params?: any;
+      operation_latency_ms?: number;
+      sql_operation?: {
+        execution_result?: string;
+        chunk_details?: {
+          total_chunks_present?: number;
+          total_chunks_iterated?: number;
+          initial_chunk_latency_millis?: number;
+          slowest_chunk_latency_millis?: number;
+          sum_chunks_download_time_millis?: number;
+        };
+      };
+      error_info?: {
+        error_name: string;
+        stack_trace: string;
+      };
+    };
+  };
+}
+ 
+/**
+ * Payload format for Databricks telemetry export.
+ * Matches JDBC TelemetryRequest format with protoLogs.
+ */
+interface DatabricksTelemetryPayload {
+  uploadTime: number;
+  items: string[]; // Always empty - required field
+  protoLogs: string[]; // JSON-stringified DatabricksTelemetryLog objects
+}
+ 
+/**
+ * Exports telemetry metrics to Databricks telemetry service.
+ *
+ * Endpoints:
+ * - Authenticated: /api/2.0/sql/telemetry-ext
+ * - Unauthenticated: /api/2.0/sql/telemetry-unauth
+ *
+ * Features:
+ * - Circuit breaker integration for endpoint protection
+ * - Retry logic with exponential backoff for retryable errors
+ * - Terminal error detection (no retry on 400, 401, 403, 404)
+ * - CRITICAL: export() method NEVER throws - all exceptions swallowed
+ * - CRITICAL: All logging at LogLevel.debug ONLY
+ */
+export default class DatabricksTelemetryExporter {
+  private circuitBreaker;
+ 
+  private readonly userAgent: string;
+ 
+  private fetchFn: typeof fetch;
+ 
+  constructor(
+    private context: IClientContext,
+    private host: string,
+    private circuitBreakerRegistry: CircuitBreakerRegistry,
+    fetchFunction?: typeof fetch,
+  ) {
+    this.circuitBreaker = circuitBreakerRegistry.getCircuitBreaker(host);
+    this.fetchFn = fetchFunction || fetch;
+ 
+    // Get driver version for user agent
+    this.userAgent = `databricks-sql-nodejs/${this.getDriverVersion()}`;
+  }
+ 
+  /**
+   * Export metrics to Databricks service. Never throws.
+   *
+   * @param metrics - Array of telemetry metrics to export
+   */
+  async export(metrics: TelemetryMetric[]): Promise<void> {
+    if (!metrics || metrics.length === 0) {
+      return;
+    }
+ 
+    const logger = this.context.getLogger();
+ 
+    try {
+      await this.circuitBreaker.execute(async () => {
+        await this.exportWithRetry(metrics);
+      });
+    } catch (error: any) {
+      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
+      if (error.message === 'Circuit breaker OPEN') {
+        logger.log(LogLevel.debug, 'Circuit breaker OPEN - dropping telemetry');
+      } else {
+        logger.log(LogLevel.debug, `Telemetry export error: ${error.message}`);
+      }
+    }
+  }
+ 
+  /**
+   * Export metrics with retry logic for retryable errors.
+   * Implements exponential backoff with jitter.
+   */
+  private async exportWithRetry(metrics: TelemetryMetric[]): Promise<void> {
+    const config = this.context.getConfig();
+    const logger = this.context.getLogger();
+    const maxRetries = config.telemetryMaxRetries ?? DEFAULT_TELEMETRY_CONFIG.maxRetries;
+ 
+    let lastError: Error | null = null;
+ 
+    /* eslint-disable no-await-in-loop */
+    for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
+      try {
+        await this.exportInternal(metrics);
+        return; // Success
+      } catch (error: any) {
+        lastError = error;
+ 
+        // Check if error is terminal (don't retry)
+        if (ExceptionClassifier.isTerminal(error)) {
+          logger.log(LogLevel.debug, `Terminal error - no retry: ${error.message}`);
+          throw error; // Terminal error, propagate to circuit breaker
+        }
+ 
+        // Check if error is retryable
+        if (!ExceptionClassifier.isRetryable(error)) {
+          logger.log(LogLevel.debug, `Non-retryable error: ${error.message}`);
+          throw error; // Not retryable, propagate to circuit breaker
+        }
+ 
+        // Last attempt reached
+        if (attempt >= maxRetries) {
+          logger.log(LogLevel.debug, `Max retries reached (${maxRetries}): ${error.message}`);
+          throw error; // Max retries exhausted, propagate to circuit breaker
+        }
+ 
+        // Calculate backoff with exponential + jitter (100ms - 1000ms)
+        const baseDelay = Math.min(100 * 2 ** attempt, 1000);
+        const jitter = Math.random() * 100;
+        const delay = baseDelay + jitter;
+ 
+        logger.log(
+          LogLevel.debug,
+          `Retrying telemetry export (attempt ${attempt + 1}/${maxRetries}) after ${Math.round(delay)}ms`,
+        );
+ 
+        await this.sleep(delay);
+      }
+    }
+    /* eslint-enable no-await-in-loop */
+ 
+    // Should not reach here, but just in case
+    if (lastError) {
+      throw lastError;
+    }
+  }
+ 
+  /**
+   * Internal export implementation that makes the HTTP call.
+   */
+  private async exportInternal(metrics: TelemetryMetric[]): Promise<void> {
+    const config = this.context.getConfig();
+    const logger = this.context.getLogger();
+ 
+    // Determine endpoint based on authentication mode
+    const authenticatedExport = config.telemetryAuthenticatedExport ?? DEFAULT_TELEMETRY_CONFIG.authenticatedExport;
+    const endpoint = authenticatedExport
+      ? this.buildUrl(this.host, '/telemetry-ext')
+      : this.buildUrl(this.host, '/telemetry-unauth');
+ 
+    // Format payload - each log is JSON-stringified to match JDBC format
+    const telemetryLogs = metrics.map((m) => this.toTelemetryLog(m));
+    const protoLogs = telemetryLogs.map((log) => JSON.stringify(log));
+ 
+    const payload: DatabricksTelemetryPayload = {
+      uploadTime: Date.now(),
+      items: [], // Required but unused
+      protoLogs,
+    };
+ 
+    logger.log(
+      LogLevel.debug,
+      `Exporting ${metrics.length} telemetry metrics to ${
+        authenticatedExport ? 'authenticated' : 'unauthenticated'
+      } endpoint`,
+    );
+ 
+    // Get authentication headers if using authenticated endpoint
+    const authHeaders = authenticatedExport ? await this.context.getAuthHeaders() : {};
+ 
+    // Get agent with proxy settings (same pattern as CloudFetchResultHandler and DBSQLSession)
+    const connectionProvider = await this.context.getConnectionProvider();
+    const agent = await connectionProvider.getAgent();
+ 
+    // Make HTTP POST request with authentication and proxy support
+    const response: Response = await this.fetchFn(endpoint, {
+      method: 'POST',
+      headers: {
+        ...authHeaders,
+        'Content-Type': 'application/json',
+        'User-Agent': this.userAgent,
+      },
+      body: JSON.stringify(payload),
+      agent, // Include agent for proxy support
+    });
+ 
+    if (!response.ok) {
+      const error: any = new Error(`Telemetry export failed: ${response.status} ${response.statusText}`);
+      error.statusCode = response.status;
+      throw error;
+    }
+ 
+    logger.log(LogLevel.debug, `Successfully exported ${metrics.length} telemetry metrics`);
+  }
+ 
+  /**
+   * Convert TelemetryMetric to Databricks telemetry log format.
+   */
+  private toTelemetryLog(metric: TelemetryMetric): DatabricksTelemetryLog {
+    const log: DatabricksTelemetryLog = {
+      frontend_log_event_id: this.generateUUID(),
+      context: {
+        client_context: {
+          timestamp_millis: metric.timestamp,
+          user_agent: this.userAgent,
+        },
+      },
+      entry: {
+        sql_driver_log: {
+          session_id: metric.sessionId,
+          sql_statement_id: metric.statementId,
+        },
+      },
+    };
+ 
+    // Add metric-specific fields based on proto definition
+    Iif (metric.metricType === 'connection' && metric.driverConfig) {
+      // Map driverConfig to system_configuration (snake_case as per proto)
+      log.entry.sql_driver_log.system_configuration = {
+        driver_version: metric.driverConfig.driverVersion,
+        driver_name: metric.driverConfig.driverName,
+        runtime_name: 'Node.js',
+        runtime_version: metric.driverConfig.nodeVersion,
+        runtime_vendor: metric.driverConfig.runtimeVendor,
+        os_name: metric.driverConfig.platform,
+        os_version: metric.driverConfig.osVersion,
+        os_arch: metric.driverConfig.osArch,
+        locale_name: metric.driverConfig.localeName,
+        char_set_encoding: metric.driverConfig.charSetEncoding,
+        process_name: metric.driverConfig.processName,
+      };
+    } else Iif (metric.metricType === 'statement') {
+      log.entry.sql_driver_log.operation_latency_ms = metric.latencyMs;
+ 
+      if (metric.resultFormat || metric.chunkCount) {
+        log.entry.sql_driver_log.sql_operation = {
+          execution_result: metric.resultFormat,
+        };
+ 
+        if (metric.chunkCount && metric.chunkCount > 0) {
+          log.entry.sql_driver_log.sql_operation.chunk_details = {
+            total_chunks_present: metric.chunkCount,
+            total_chunks_iterated: metric.chunkCount,
+          };
+        }
+      }
+    } else Iif (metric.metricType === 'error') {
+      log.entry.sql_driver_log.error_info = {
+        error_name: metric.errorName || 'UnknownError',
+        stack_trace: metric.errorMessage || '',
+      };
+    }
+ 
+    return log;
+  }
+ 
+  /**
+   * Build full URL from host and path, handling protocol correctly.
+   */
+  private buildUrl(host: string, path: string): string {
+    if (host.startsWith('http://') || host.startsWith('https://')) {
+      return `${host}${path}`;
+    }
+    return `https://${host}${path}`;
+  }
+ 
+  /**
+   * Generate a UUID v4.
+   */
+  private generateUUID(): string {
+    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+      const r = (Math.random() * 16) | 0;
+      const v = c === 'x' ? r : (r & 0x3) | 0x8;
+      return v.toString(16);
+    });
+  }
+ 
+  /**
+   * Get driver version from package.json.
+   */
+  private getDriverVersion(): string {
+    try {
+      // In production, this would read from package.json
+      return '1.0.0';
+    } catch {
+      return 'unknown';
+    }
+  }
+ 
+  /**
+   * Sleep for the specified number of milliseconds.
+   */
+  private sleep(ms: number): Promise<void> {
+    return new Promise((resolve) => {
+      setTimeout(resolve, ms);
+    });
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html b/coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html new file mode 100644 index 00000000..a3d6c1ae --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html @@ -0,0 +1,388 @@ + + + + + + Code coverage report for lib/telemetry/ExceptionClassifier.ts + + + + + + + + + +
+
+

All files / lib/telemetry ExceptionClassifier.ts

+
+ +
+ 82.35% + Statements + 14/17 +
+ + +
+ 86.2% + Branches + 25/29 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 82.35% + Lines + 14/17 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +11x +  +  +  +  +  +11x +  +11x +  +  +  +  +  +9x +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +9x +  +  +  +  +9x +  +  +  +  +  +9x +  +9x +  +  +  +  +  +  +7x +  +  +  +2x +  +  + 
/**
+ * Copyright (c) 2025 Databricks Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+import AuthenticationError from '../errors/AuthenticationError';
+import RetryError from '../errors/RetryError';
+ 
+/**
+ * Classifies exceptions as terminal (unrecoverable) vs retryable.
+ *
+ * Terminal exceptions should be flushed immediately to telemetry,
+ * while retryable exceptions are buffered until statement completion.
+ *
+ * This follows the JDBC driver pattern of smart exception flushing
+ * to optimize telemetry export efficiency while ensuring critical
+ * errors are reported immediately.
+ */
+export default class ExceptionClassifier {
+  /**
+   * Determines if an exception is terminal (non-retryable).
+   *
+   * Terminal exceptions indicate unrecoverable failures that should
+   * be reported immediately, such as authentication failures, invalid
+   * requests, or resource not found errors.
+   *
+   * @param error - The error to classify
+   * @returns true if the error is terminal, false otherwise
+   */
+  static isTerminal(error: Error): boolean {
+    // Check for AuthenticationError (terminal)
+    Iif (error instanceof AuthenticationError) {
+      return true;
+    }
+ 
+    // Check for HTTP status codes in error properties
+    // Supporting both 'statusCode' and 'status' property names for flexibility
+    const statusCode = (error as any).statusCode ?? (error as any).status;
+ 
+    if (typeof statusCode === 'number') {
+      // Terminal HTTP status codes:
+      // 400 - Bad Request (invalid request format)
+      // 401 - Unauthorized (authentication required)
+      // 403 - Forbidden (permission denied)
+      // 404 - Not Found (resource does not exist)
+      return statusCode === 400 || statusCode === 401 || statusCode === 403 || statusCode === 404;
+    }
+ 
+    // Default to false for unknown error types
+    return false;
+  }
+ 
+  /**
+   * Determines if an exception is retryable.
+   *
+   * Retryable exceptions indicate transient failures that may succeed
+   * on retry, such as rate limiting, server errors, or network timeouts.
+   *
+   * @param error - The error to classify
+   * @returns true if the error is retryable, false otherwise
+   */
+  static isRetryable(error: Error): boolean {
+    // Check for RetryError (explicitly retryable)
+    Iif (error instanceof RetryError) {
+      return true;
+    }
+ 
+    // Check for network timeout errors
+    Iif (error.name === 'TimeoutError' || error.message.includes('timeout')) {
+      return true;
+    }
+ 
+    // Check for HTTP status codes in error properties
+    // Supporting both 'statusCode' and 'status' property names for flexibility
+    const statusCode = (error as any).statusCode ?? (error as any).status;
+ 
+    if (typeof statusCode === 'number') {
+      // Retryable HTTP status codes:
+      // 429 - Too Many Requests (rate limiting)
+      // 500 - Internal Server Error
+      // 502 - Bad Gateway
+      // 503 - Service Unavailable
+      // 504 - Gateway Timeout
+      return statusCode === 429 || statusCode === 500 || statusCode === 502 || statusCode === 503 || statusCode === 504;
+    }
+ 
+    // Default to false for unknown error types
+    return false;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html b/coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html new file mode 100644 index 00000000..d123ed82 --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html @@ -0,0 +1,688 @@ + + + + + + Code coverage report for lib/telemetry/FeatureFlagCache.ts + + + + + + + + + +
+
+

All files / lib/telemetry FeatureFlagCache.ts

+
+ +
+ 0% + Statements + 0/63 +
+ + +
+ 0% + Branches + 0/33 +
+ + +
+ 0% + Functions + 0/8 +
+ + +
+ 0% + Lines + 0/62 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
/**
+ * Copyright (c) 2025 Databricks Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+import fetch from 'node-fetch';
+import IClientContext from '../contracts/IClientContext';
+import { LogLevel } from '../contracts/IDBSQLLogger';
+import driverVersion from '../version';
+ 
+/**
+ * Context holding feature flag state for a specific host.
+ */
+export interface FeatureFlagContext {
+  telemetryEnabled?: boolean;
+  lastFetched?: Date;
+  refCount: number;
+  cacheDuration: number; // 15 minutes in ms
+}
+ 
+/**
+ * Manages feature flag cache per host.
+ * Prevents rate limiting by caching feature flag responses.
+ * Instance-based, stored in DBSQLClient.
+ */
+export default class FeatureFlagCache {
+  private contexts: Map<string, FeatureFlagContext>;
+ 
+  private readonly CACHE_DURATION_MS = 15 * 60 * 1000; // 15 minutes
+ 
+  private readonly FEATURE_FLAG_NAME = 'databricks.partnerplatform.clientConfigsFeatureFlags.enableTelemetryForNodeJs';
+ 
+  constructor(private context: IClientContext) {
+    this.contexts = new Map();
+  }
+ 
+  /**
+   * Gets or creates a feature flag context for the host.
+   * Increments reference count.
+   */
+  getOrCreateContext(host: string): FeatureFlagContext {
+    let ctx = this.contexts.get(host);
+    if (!ctx) {
+      ctx = {
+        refCount: 0,
+        cacheDuration: this.CACHE_DURATION_MS,
+      };
+      this.contexts.set(host, ctx);
+    }
+    ctx.refCount += 1;
+    return ctx;
+  }
+ 
+  /**
+   * Decrements reference count for the host.
+   * Removes context when ref count reaches zero.
+   */
+  releaseContext(host: string): void {
+    const ctx = this.contexts.get(host);
+    if (ctx) {
+      ctx.refCount -= 1;
+      if (ctx.refCount <= 0) {
+        this.contexts.delete(host);
+      }
+    }
+  }
+ 
+  /**
+   * Checks if telemetry is enabled for the host.
+   * Uses cached value if available and not expired.
+   */
+  async isTelemetryEnabled(host: string): Promise<boolean> {
+    const logger = this.context.getLogger();
+    const ctx = this.contexts.get(host);
+ 
+    if (!ctx) {
+      return false;
+    }
+ 
+    const isExpired = !ctx.lastFetched || Date.now() - ctx.lastFetched.getTime() > ctx.cacheDuration;
+ 
+    if (isExpired) {
+      try {
+        // Fetch feature flag from server
+        ctx.telemetryEnabled = await this.fetchFeatureFlag(host);
+        ctx.lastFetched = new Date();
+      } catch (error: any) {
+        // Log at debug level only, never propagate exceptions
+        logger.log(LogLevel.debug, `Error fetching feature flag: ${error.message}`);
+      }
+    }
+ 
+    return ctx.telemetryEnabled ?? false;
+  }
+ 
+  /**
+   * Fetches feature flag from server using connector-service API.
+   * Calls GET /api/2.0/connector-service/feature-flags/OSS_NODEJS/{version}
+   *
+   * @param host The host to fetch feature flag for
+   * @returns true if feature flag is enabled, false otherwise
+   */
+  private async fetchFeatureFlag(host: string): Promise<boolean> {
+    const logger = this.context.getLogger();
+ 
+    try {
+      // Get driver version for endpoint
+      const version = this.getDriverVersion();
+ 
+      // Build feature flags endpoint for Node.js driver
+      const endpoint = this.buildUrl(host, `/api/2.0/connector-service/feature-flags/NODEJS/${version}`);
+ 
+      // Get authentication headers
+      const authHeaders = await this.context.getAuthHeaders();
+ 
+      logger.log(LogLevel.debug, `Fetching feature flags from ${endpoint}`);
+ 
+      // Get agent with proxy settings (same pattern as CloudFetchResultHandler and DBSQLSession)
+      const connectionProvider = await this.context.getConnectionProvider();
+      const agent = await connectionProvider.getAgent();
+ 
+      // Make HTTP GET request with authentication and proxy support
+      const response = await fetch(endpoint, {
+        method: 'GET',
+        headers: {
+          ...authHeaders,
+          'Content-Type': 'application/json',
+          'User-Agent': `databricks-sql-nodejs/${driverVersion}`,
+        },
+        agent, // Include agent for proxy support
+      });
+ 
+      if (!response.ok) {
+        logger.log(LogLevel.debug, `Feature flag fetch failed: ${response.status} ${response.statusText}`);
+        return false;
+      }
+ 
+      // Parse response JSON
+      const data: any = await response.json();
+ 
+      // Response format: { flags: [{ name: string, value: string }], ttl_seconds?: number }
+      if (data && data.flags && Array.isArray(data.flags)) {
+        // Update cache duration if TTL provided
+        const ctx = this.contexts.get(host);
+        if (ctx && data.ttl_seconds) {
+          ctx.cacheDuration = data.ttl_seconds * 1000; // Convert to milliseconds
+          logger.log(LogLevel.debug, `Updated cache duration to ${data.ttl_seconds} seconds`);
+        }
+ 
+        // Look for our specific feature flag
+        const flag = data.flags.find((f: any) => f.name === this.FEATURE_FLAG_NAME);
+ 
+        if (flag) {
+          // Parse boolean value (can be string "true"/"false")
+          const value = String(flag.value).toLowerCase();
+          const enabled = value === 'true';
+          logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME}: ${enabled}`);
+          return enabled;
+        }
+      }
+ 
+      // Feature flag not found in response, default to false
+      logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME} not found in response`);
+      return false;
+    } catch (error: any) {
+      // Log at debug level only, never propagate exceptions
+      logger.log(LogLevel.debug, `Error fetching feature flag from ${host}: ${error.message}`);
+      return false;
+    }
+  }
+ 
+  /**
+   * Build full URL from host and path, handling protocol correctly.
+   */
+  private buildUrl(host: string, path: string): string {
+    if (host.startsWith('http://') || host.startsWith('https://')) {
+      return `${host}${path}`;
+    }
+    return `https://${host}${path}`;
+  }
+ 
+  /**
+   * Gets the driver version without -oss suffix for API calls.
+   * Format: "1.12.0" from "1.12.0-oss"
+   */
+  private getDriverVersion(): string {
+    // Remove -oss suffix if present
+    return driverVersion.replace(/-oss$/, '');
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html b/coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html new file mode 100644 index 00000000..2a11f203 --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html @@ -0,0 +1,1261 @@ + + + + + + Code coverage report for lib/telemetry/MetricsAggregator.ts + + + + + + + + + +
+
+

All files / lib/telemetry MetricsAggregator.ts

+
+ +
+ 88.13% + Statements + 104/118 +
+ + +
+ 82.19% + Branches + 60/73 +
+ + +
+ 100% + Functions + 12/12 +
+ + +
+ 88.03% + Lines + 103/117 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +14x +  +14x +  +14x +  +  +  +  +  +  +  +14x +14x +14x +14x +14x +14x +  +  +14x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +21x +  +21x +  +21x +12x +12x +  +  +  +9x +2x +2x +  +  +  +7x +6x +  +  +  +  +  +  +  +  +  +  +  +12x +  +  +  +  +  +  +  +12x +  +  +  +  +  +  +2x +  +  +2x +2x +  +  +2x +  +2x +  +1x +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +1x +  +  +  +1x +1x +  +1x +1x +  +  +  +  +  +  +  +6x +  +6x +  +3x +3x +3x +  +  +1x +1x +1x +1x +1x +1x +  +  +2x +2x +2x +2x +  +2x +  +  +  +  +  +  +  +  +  +  +  +7x +  +7x +4x +  +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +5x +  +5x +5x +5x +1x +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +  +4x +1x +  +  +  +  +  +  +  +  +1x +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +18x +  +  +18x +2x +2x +2x +2x +  +  +  +18x +1x +  +  +  +  +  +  +  +  +  +12x +  +12x +12x +2x +  +  +10x +10x +  +10x +  +  +10x +  +  +  +10x +7x +  +  +  +  +  +  +  +  +  +  +  +21x +  +21x +21x +7x +  +  +21x +  +2x +  +  +  +21x +  +  +  +  +  +  +  +  +  +  +3x +  +3x +  +3x +3x +3x +  +  +  +3x +1x +  +  +  +3x +  +  +  +  +  +  + 
/**
+ * Copyright (c) 2025 Databricks Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+import IClientContext from '../contracts/IClientContext';
+import { LogLevel } from '../contracts/IDBSQLLogger';
+import { TelemetryEvent, TelemetryEventType, TelemetryMetric, DEFAULT_TELEMETRY_CONFIG } from './types';
+import DatabricksTelemetryExporter from './DatabricksTelemetryExporter';
+import ExceptionClassifier from './ExceptionClassifier';
+ 
+/**
+ * Per-statement telemetry details for aggregation
+ */
+interface StatementTelemetryDetails {
+  statementId: string;
+  sessionId: string;
+  workspaceId?: string;
+  operationType?: string;
+  startTime: number;
+  executionLatencyMs?: number;
+  resultFormat?: string;
+  chunkCount: number;
+  bytesDownloaded: number;
+  pollCount: number;
+  compressionEnabled?: boolean;
+  errors: TelemetryEvent[];
+}
+ 
+/**
+ * Aggregates telemetry events by statement_id and manages batching/flushing.
+ *
+ * Features:
+ * - Aggregates events by statement_id
+ * - Connection events emitted immediately (no aggregation)
+ * - Statement events buffered until completeStatement() called
+ * - Terminal exceptions flushed immediately
+ * - Retryable exceptions buffered until statement complete
+ * - Batch size and periodic timer trigger flushes
+ * - CRITICAL: All exceptions swallowed and logged at LogLevel.debug ONLY
+ * - CRITICAL: NO console logging
+ *
+ * Follows JDBC TelemetryCollector.java:29-30 pattern.
+ */
+export default class MetricsAggregator {
+  private statementMetrics: Map<string, StatementTelemetryDetails> = new Map();
+ 
+  private pendingMetrics: TelemetryMetric[] = [];
+ 
+  private flushTimer: NodeJS.Timeout | null = null;
+ 
+  private batchSize: number;
+ 
+  private flushIntervalMs: number;
+ 
+  private maxPendingMetrics: number;
+ 
+  constructor(private context: IClientContext, private exporter: DatabricksTelemetryExporter) {
+    try {
+      const config = context.getConfig();
+      this.batchSize = config.telemetryBatchSize ?? DEFAULT_TELEMETRY_CONFIG.batchSize;
+      this.flushIntervalMs = config.telemetryFlushIntervalMs ?? DEFAULT_TELEMETRY_CONFIG.flushIntervalMs;
+      this.maxPendingMetrics = config.telemetryMaxPendingMetrics ?? DEFAULT_TELEMETRY_CONFIG.maxPendingMetrics;
+ 
+      // Start periodic flush timer
+      this.startFlushTimer();
+    } catch (error: any) {
+      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
+      const logger = this.context.getLogger();
+      logger.log(LogLevel.debug, `MetricsAggregator constructor error: ${error.message}`);
+ 
+      // Initialize with default values
+      this.batchSize = DEFAULT_TELEMETRY_CONFIG.batchSize;
+      this.flushIntervalMs = DEFAULT_TELEMETRY_CONFIG.flushIntervalMs;
+      this.maxPendingMetrics = DEFAULT_TELEMETRY_CONFIG.maxPendingMetrics;
+    }
+  }
+ 
+  /**
+   * Process a telemetry event. Never throws.
+   *
+   * @param event - The telemetry event to process
+   */
+  processEvent(event: TelemetryEvent): void {
+    const logger = this.context.getLogger();
+ 
+    try {
+      // Connection events are emitted immediately (no aggregation)
+      if (event.eventType === TelemetryEventType.CONNECTION_OPEN) {
+        this.processConnectionEvent(event);
+        return;
+      }
+ 
+      // Error events - check if terminal or retryable
+      if (event.eventType === TelemetryEventType.ERROR) {
+        this.processErrorEvent(event);
+        return;
+      }
+ 
+      // Statement events - buffer until complete
+      if (event.statementId) {
+        this.processStatementEvent(event);
+      }
+    } catch (error: any) {
+      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
+      logger.log(LogLevel.debug, `MetricsAggregator.processEvent error: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Process connection event (emit immediately)
+   */
+  private processConnectionEvent(event: TelemetryEvent): void {
+    const metric: TelemetryMetric = {
+      metricType: 'connection',
+      timestamp: event.timestamp,
+      sessionId: event.sessionId,
+      workspaceId: event.workspaceId,
+      driverConfig: event.driverConfig,
+    };
+ 
+    this.addPendingMetric(metric);
+  }
+ 
+  /**
+   * Process error event (terminal errors flushed immediately, retryable buffered)
+   */
+  private processErrorEvent(event: TelemetryEvent): void {
+    const logger = this.context.getLogger();
+ 
+    // Create error object for classification
+    const error: any = new Error(event.errorMessage || 'Unknown error');
+    error.name = event.errorName || 'UnknownError';
+ 
+    // Check if terminal using isTerminal field or ExceptionClassifier
+    const isTerminal = event.isTerminal ?? ExceptionClassifier.isTerminal(error);
+ 
+    if (isTerminal) {
+      // Terminal error - flush immediately
+      logger.log(LogLevel.debug, `Terminal error detected - flushing immediately`);
+ 
+      // If associated with a statement, complete and flush it
+      Iif (event.statementId && this.statementMetrics.has(event.statementId)) {
+        const details = this.statementMetrics.get(event.statementId)!;
+        details.errors.push(event);
+        this.completeStatement(event.statementId);
+      } else {
+        // Standalone error - emit immediately
+        const metric: TelemetryMetric = {
+          metricType: 'error',
+          timestamp: event.timestamp,
+          sessionId: event.sessionId,
+          statementId: event.statementId,
+          workspaceId: event.workspaceId,
+          errorName: event.errorName,
+          errorMessage: event.errorMessage,
+        };
+        this.addPendingMetric(metric);
+      }
+ 
+      // Flush immediately for terminal errors
+      this.flush();
+    } else Eif (event.statementId) {
+      // Retryable error - buffer until statement complete
+      const details = this.getOrCreateStatementDetails(event);
+      details.errors.push(event);
+    }
+  }
+ 
+  /**
+   * Process statement event (buffer until complete)
+   */
+  private processStatementEvent(event: TelemetryEvent): void {
+    const details = this.getOrCreateStatementDetails(event);
+ 
+    switch (event.eventType) {
+      case TelemetryEventType.STATEMENT_START:
+        details.operationType = event.operationType;
+        details.startTime = event.timestamp;
+        break;
+ 
+      case TelemetryEventType.STATEMENT_COMPLETE:
+        details.executionLatencyMs = event.latencyMs;
+        details.resultFormat = event.resultFormat;
+        details.chunkCount = event.chunkCount ?? 0;
+        details.bytesDownloaded = event.bytesDownloaded ?? 0;
+        details.pollCount = event.pollCount ?? 0;
+        break;
+ 
+      case TelemetryEventType.CLOUDFETCH_CHUNK:
+        details.chunkCount += 1;
+        details.bytesDownloaded += event.bytes ?? 0;
+        Eif (event.compressed !== undefined) {
+          details.compressionEnabled = event.compressed;
+        }
+        break;
+ 
+      default:
+        // Unknown event type - ignore
+        break;
+    }
+  }
+ 
+  /**
+   * Get or create statement details for the given event
+   */
+  private getOrCreateStatementDetails(event: TelemetryEvent): StatementTelemetryDetails {
+    const statementId = event.statementId!;
+ 
+    if (!this.statementMetrics.has(statementId)) {
+      this.statementMetrics.set(statementId, {
+        statementId,
+        sessionId: event.sessionId!,
+        workspaceId: event.workspaceId,
+        startTime: event.timestamp,
+        chunkCount: 0,
+        bytesDownloaded: 0,
+        pollCount: 0,
+        errors: [],
+      });
+    }
+ 
+    return this.statementMetrics.get(statementId)!;
+  }
+ 
+  /**
+   * Complete a statement and prepare it for flushing. Never throws.
+   *
+   * @param statementId - The statement ID to complete
+   */
+  completeStatement(statementId: string): void {
+    const logger = this.context.getLogger();
+ 
+    try {
+      const details = this.statementMetrics.get(statementId);
+      if (!details) {
+        return;
+      }
+ 
+      // Create statement metric
+      const metric: TelemetryMetric = {
+        metricType: 'statement',
+        timestamp: details.startTime,
+        sessionId: details.sessionId,
+        statementId: details.statementId,
+        workspaceId: details.workspaceId,
+        latencyMs: details.executionLatencyMs,
+        resultFormat: details.resultFormat,
+        chunkCount: details.chunkCount,
+        bytesDownloaded: details.bytesDownloaded,
+        pollCount: details.pollCount,
+      };
+ 
+      this.addPendingMetric(metric);
+ 
+      // Add buffered error metrics
+      for (const errorEvent of details.errors) {
+        const errorMetric: TelemetryMetric = {
+          metricType: 'error',
+          timestamp: errorEvent.timestamp,
+          sessionId: details.sessionId,
+          statementId: details.statementId,
+          workspaceId: details.workspaceId,
+          errorName: errorEvent.errorName,
+          errorMessage: errorEvent.errorMessage,
+        };
+        this.addPendingMetric(errorMetric);
+      }
+ 
+      // Remove from map
+      this.statementMetrics.delete(statementId);
+    } catch (error: any) {
+      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
+      logger.log(LogLevel.debug, `MetricsAggregator.completeStatement error: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Add a metric to pending batch and flush if batch size reached.
+   * Drops oldest metrics if the buffer exceeds maxPendingMetrics to prevent
+   * unbounded growth when the circuit breaker keeps failing exports.
+   */
+  private addPendingMetric(metric: TelemetryMetric): void {
+    this.pendingMetrics.push(metric);
+ 
+    // Cap the buffer to avoid unbounded memory growth when exports keep failing
+    if (this.pendingMetrics.length > this.maxPendingMetrics) {
+      const dropped = this.pendingMetrics.length - this.maxPendingMetrics;
+      this.pendingMetrics = this.pendingMetrics.slice(dropped);
+      const logger = this.context.getLogger();
+      logger.log(LogLevel.debug, `Dropped ${dropped} oldest telemetry metrics (buffer full at ${this.maxPendingMetrics})`);
+    }
+ 
+    // Check if batch size reached
+    if (this.pendingMetrics.length >= this.batchSize) {
+      this.flush();
+    }
+  }
+ 
+  /**
+   * Flush all pending metrics to exporter. Never throws.
+   *
+   * @param resetTimer If true, resets the flush timer after flushing (default: true)
+   */
+  flush(resetTimer: boolean = true): void {
+    const logger = this.context.getLogger();
+ 
+    try {
+      if (this.pendingMetrics.length === 0) {
+        return;
+      }
+ 
+      const metricsToExport = [...this.pendingMetrics];
+      this.pendingMetrics = [];
+ 
+      logger.log(LogLevel.debug, `Flushing ${metricsToExport.length} telemetry metrics`);
+ 
+      // Export metrics (exporter.export never throws)
+      this.exporter.export(metricsToExport);
+ 
+      // Reset timer to avoid rapid successive flushes (e.g., batch flush at 25s then timer flush at 30s)
+      // This ensures consistent spacing between exports and helps avoid rate limiting
+      if (resetTimer) {
+        this.startFlushTimer();
+      }
+    } catch (error: any) {
+      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
+      logger.log(LogLevel.debug, `MetricsAggregator.flush error: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Start the periodic flush timer
+   */
+  private startFlushTimer(): void {
+    const logger = this.context.getLogger();
+ 
+    try {
+      if (this.flushTimer) {
+        clearInterval(this.flushTimer);
+      }
+ 
+      this.flushTimer = setInterval(() => {
+        // Don't reset timer when flush is triggered by the timer itself
+        this.flush(false);
+      }, this.flushIntervalMs);
+ 
+      // Prevent timer from keeping Node.js process alive
+      this.flushTimer.unref();
+    } catch (error: any) {
+      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
+      logger.log(LogLevel.debug, `MetricsAggregator.startFlushTimer error: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Close the aggregator and flush remaining metrics. Never throws.
+   */
+  close(): void {
+    const logger = this.context.getLogger();
+ 
+    try {
+      // Stop flush timer
+      Eif (this.flushTimer) {
+        clearInterval(this.flushTimer);
+        this.flushTimer = null;
+      }
+ 
+      // Complete any remaining statements
+      for (const statementId of this.statementMetrics.keys()) {
+        this.completeStatement(statementId);
+      }
+ 
+      // Final flush - don't reset timer since we're closing
+      this.flush(false);
+    } catch (error: any) {
+      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
+      logger.log(LogLevel.debug, `MetricsAggregator.close error: ${error.message}`);
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html b/coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html new file mode 100644 index 00000000..c96d6b44 --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html @@ -0,0 +1,655 @@ + + + + + + Code coverage report for lib/telemetry/TelemetryEventEmitter.ts + + + + + + + + + +
+
+

All files / lib/telemetry TelemetryEventEmitter.ts

+
+ +
+ 86.04% + Statements + 37/43 +
+ + +
+ 78.57% + Branches + 11/14 +
+ + +
+ 100% + Functions + 6/6 +
+ + +
+ 89.47% + Lines + 34/38 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +11x +11x +  +  +11x +11x +  +  +  +  +  +  +  +  +4x +  +3x +3x +3x +  +  +  +  +  +  +3x +  +  +2x +  +  +  +  +  +  +  +  +  +2x +  +1x +1x +1x +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +1x +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +2x +2x +2x +  +  +  +  +  +  +  +  +2x +  +  +  +  +  +  + 
/**
+ * Copyright (c) 2025 Databricks Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+import { EventEmitter } from 'events';
+import IClientContext from '../contracts/IClientContext';
+import { LogLevel } from '../contracts/IDBSQLLogger';
+import { TelemetryEvent, TelemetryEventType, DriverConfiguration } from './types';
+ 
+/**
+ * EventEmitter for driver telemetry.
+ * Emits events at key driver operations.
+ *
+ * CRITICAL REQUIREMENT: ALL exceptions must be caught and logged at LogLevel.debug ONLY
+ * (never warn/error) to avoid customer anxiety. NO console logging allowed - only IDBSQLLogger.
+ *
+ * All emit methods are wrapped in try-catch blocks that swallow exceptions completely.
+ * Event emission respects the telemetryEnabled flag from context config.
+ */
+export default class TelemetryEventEmitter extends EventEmitter {
+  private enabled: boolean;
+ 
+  constructor(private context: IClientContext) {
+    super();
+    // Check if telemetry is enabled from config
+    // Default to false for safe rollout
+    const config = context.getConfig() as any;
+    this.enabled = config.telemetryEnabled ?? false;
+  }
+ 
+  /**
+   * Emit a connection open event.
+   *
+   * @param data Connection event data including sessionId, workspaceId, and driverConfig
+   */
+  emitConnectionOpen(data: { sessionId: string; workspaceId: string; driverConfig: DriverConfiguration }): void {
+    if (!this.enabled) return;
+ 
+    const logger = this.context.getLogger();
+    try {
+      const event: TelemetryEvent = {
+        eventType: TelemetryEventType.CONNECTION_OPEN,
+        timestamp: Date.now(),
+        sessionId: data.sessionId,
+        workspaceId: data.workspaceId,
+        driverConfig: data.driverConfig,
+      };
+      this.emit(TelemetryEventType.CONNECTION_OPEN, event);
+    } catch (error: any) {
+      // Swallow all exceptions - log at debug level only
+      logger.log(LogLevel.debug, `Error emitting connection event: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Emit a statement start event.
+   *
+   * @param data Statement start data including statementId, sessionId, and operationType
+   */
+  emitStatementStart(data: { statementId: string; sessionId: string; operationType?: string }): void {
+    if (!this.enabled) return;
+ 
+    const logger = this.context.getLogger();
+    try {
+      const event: TelemetryEvent = {
+        eventType: TelemetryEventType.STATEMENT_START,
+        timestamp: Date.now(),
+        statementId: data.statementId,
+        sessionId: data.sessionId,
+        operationType: data.operationType,
+      };
+      this.emit(TelemetryEventType.STATEMENT_START, event);
+    } catch (error: any) {
+      // Swallow all exceptions - log at debug level only
+      logger.log(LogLevel.debug, `Error emitting statement start: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Emit a statement complete event.
+   *
+   * @param data Statement completion data including latency, result format, and metrics
+   */
+  emitStatementComplete(data: {
+    statementId: string;
+    sessionId: string;
+    latencyMs?: number;
+    resultFormat?: string;
+    chunkCount?: number;
+    bytesDownloaded?: number;
+    pollCount?: number;
+  }): void {
+    Iif (!this.enabled) return;
+ 
+    const logger = this.context.getLogger();
+    try {
+      const event: TelemetryEvent = {
+        eventType: TelemetryEventType.STATEMENT_COMPLETE,
+        timestamp: Date.now(),
+        statementId: data.statementId,
+        sessionId: data.sessionId,
+        latencyMs: data.latencyMs,
+        resultFormat: data.resultFormat,
+        chunkCount: data.chunkCount,
+        bytesDownloaded: data.bytesDownloaded,
+        pollCount: data.pollCount,
+      };
+      this.emit(TelemetryEventType.STATEMENT_COMPLETE, event);
+    } catch (error: any) {
+      // Swallow all exceptions - log at debug level only
+      logger.log(LogLevel.debug, `Error emitting statement complete: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Emit a CloudFetch chunk download event.
+   *
+   * @param data CloudFetch chunk data including chunk index, latency, bytes, and compression
+   */
+  emitCloudFetchChunk(data: {
+    statementId: string;
+    chunkIndex: number;
+    latencyMs?: number;
+    bytes: number;
+    compressed?: boolean;
+  }): void {
+    Iif (!this.enabled) return;
+ 
+    const logger = this.context.getLogger();
+    try {
+      const event: TelemetryEvent = {
+        eventType: TelemetryEventType.CLOUDFETCH_CHUNK,
+        timestamp: Date.now(),
+        statementId: data.statementId,
+        chunkIndex: data.chunkIndex,
+        latencyMs: data.latencyMs,
+        bytes: data.bytes,
+        compressed: data.compressed,
+      };
+      this.emit(TelemetryEventType.CLOUDFETCH_CHUNK, event);
+    } catch (error: any) {
+      // Swallow all exceptions - log at debug level only
+      logger.log(LogLevel.debug, `Error emitting cloudfetch chunk: ${error.message}`);
+    }
+  }
+ 
+  /**
+   * Emit an error event.
+   *
+   * @param data Error event data including error details and terminal status
+   */
+  emitError(data: {
+    statementId?: string;
+    sessionId?: string;
+    errorName: string;
+    errorMessage: string;
+    isTerminal: boolean;
+  }): void {
+    if (!this.enabled) return;
+ 
+    const logger = this.context.getLogger();
+    try {
+      const event: TelemetryEvent = {
+        eventType: TelemetryEventType.ERROR,
+        timestamp: Date.now(),
+        statementId: data.statementId,
+        sessionId: data.sessionId,
+        errorName: data.errorName,
+        errorMessage: data.errorMessage,
+        isTerminal: data.isTerminal,
+      };
+      this.emit(TelemetryEventType.ERROR, event);
+    } catch (error: any) {
+      // Swallow all exceptions - log at debug level only
+      logger.log(LogLevel.debug, `Error emitting error event: ${error.message}`);
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/index.html b/coverage/lcov-report/lib/telemetry/index.html new file mode 100644 index 00000000..7c3d2b8b --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/index.html @@ -0,0 +1,206 @@ + + + + + + Code coverage report for lib/telemetry + + + + + + + + + +
+
+

All files lib/telemetry

+
+ +
+ 75.93% + Statements + 303/399 +
+ + +
+ 68.72% + Branches + 156/227 +
+ + +
+ 85.71% + Functions + 48/56 +
+ + +
+ 76.09% + Lines + 296/389 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
CircuitBreaker.ts +
+
100%61/61100%18/18100%13/13100%61/61
DatabricksTelemetryExporter.ts +
+
88.76%79/8968.96%40/58100%14/1488.37%76/86
ExceptionClassifier.ts +
+
82.35%14/1786.2%25/29100%2/282.35%14/17
FeatureFlagCache.ts +
+
0%0/630%0/330%0/80%0/62
MetricsAggregator.ts +
+
88.13%104/11882.19%60/73100%12/1288.03%103/117
TelemetryEventEmitter.ts +
+
86.04%37/4378.57%11/14100%6/689.47%34/38
types.ts +
+
100%8/8100%2/2100%1/1100%8/8
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/types.ts.html b/coverage/lcov-report/lib/telemetry/types.ts.html new file mode 100644 index 00000000..bc0805c1 --- /dev/null +++ b/coverage/lcov-report/lib/telemetry/types.ts.html @@ -0,0 +1,916 @@ + + + + + + Code coverage report for lib/telemetry/types.ts + + + + + + + + + +
+
+

All files / lib/telemetry types.ts

+
+ +
+ 100% + Statements + 8/8 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 8/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +1x +1x +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
/**
+ * Copyright (c) 2025 Databricks Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+/**
+ * Driver name constant for telemetry
+ */
+export const DRIVER_NAME = 'nodejs-sql-driver';
+ 
+/**
+ * Event types emitted by the telemetry system
+ */
+export enum TelemetryEventType {
+  CONNECTION_OPEN = 'connection.open',
+  STATEMENT_START = 'statement.start',
+  STATEMENT_COMPLETE = 'statement.complete',
+  CLOUDFETCH_CHUNK = 'cloudfetch.chunk',
+  ERROR = 'error',
+}
+ 
+/**
+ * Configuration for telemetry components
+ */
+export interface TelemetryConfiguration {
+  /** Whether telemetry is enabled */
+  enabled?: boolean;
+ 
+  /** Maximum number of metrics to batch before flushing */
+  batchSize?: number;
+ 
+  /** Interval in milliseconds to flush metrics */
+  flushIntervalMs?: number;
+ 
+  /** Maximum retry attempts for export */
+  maxRetries?: number;
+ 
+  /** Whether to use authenticated export endpoint */
+  authenticatedExport?: boolean;
+ 
+  /** Circuit breaker failure threshold */
+  circuitBreakerThreshold?: number;
+ 
+  /** Circuit breaker timeout in milliseconds */
+  circuitBreakerTimeout?: number;
+ 
+  /** Maximum number of pending metrics buffered before dropping oldest (prevents unbounded growth when export keeps failing) */
+  maxPendingMetrics?: number;
+}
+ 
+/**
+ * Default telemetry configuration values
+ */
+export const DEFAULT_TELEMETRY_CONFIG: Required<TelemetryConfiguration> = {
+  enabled: false, // Initially disabled for safe rollout
+  batchSize: 100,
+  flushIntervalMs: 5000,
+  maxRetries: 3,
+  authenticatedExport: true,
+  circuitBreakerThreshold: 5,
+  circuitBreakerTimeout: 60000, // 1 minute
+  maxPendingMetrics: 500,
+};
+ 
+/**
+ * Runtime telemetry event emitted by the driver
+ */
+export interface TelemetryEvent {
+  /** Type of the event */
+  eventType: TelemetryEventType | string;
+ 
+  /** Timestamp when the event occurred (milliseconds since epoch) */
+  timestamp: number;
+ 
+  /** Session ID for correlation */
+  sessionId?: string;
+ 
+  /** Statement ID for correlation */
+  statementId?: string;
+ 
+  // Connection-specific fields
+  /** Workspace ID */
+  workspaceId?: string;
+ 
+  /** Driver configuration */
+  driverConfig?: DriverConfiguration;
+ 
+  // Statement-specific fields
+  /** Type of operation (SELECT, INSERT, etc.) */
+  operationType?: string;
+ 
+  /** Execution latency in milliseconds */
+  latencyMs?: number;
+ 
+  /** Result format (inline, cloudfetch, arrow) */
+  resultFormat?: string;
+ 
+  /** Number of result chunks */
+  chunkCount?: number;
+ 
+  /** Total bytes downloaded */
+  bytesDownloaded?: number;
+ 
+  /** Number of poll operations */
+  pollCount?: number;
+ 
+  // CloudFetch-specific fields
+  /** Chunk index in the result set */
+  chunkIndex?: number;
+ 
+  /** Number of bytes in this chunk */
+  bytes?: number;
+ 
+  /** Whether compression was used */
+  compressed?: boolean;
+ 
+  // Error-specific fields
+  /** Error name/type */
+  errorName?: string;
+ 
+  /** Error message */
+  errorMessage?: string;
+ 
+  /** Whether the error is terminal (non-retryable) */
+  isTerminal?: boolean;
+}
+ 
+/**
+ * Aggregated telemetry metric for export to Databricks
+ */
+export interface TelemetryMetric {
+  /** Type of metric */
+  metricType: 'connection' | 'statement' | 'error';
+ 
+  /** Timestamp when the metric was created (milliseconds since epoch) */
+  timestamp: number;
+ 
+  /** Session ID for correlation */
+  sessionId?: string;
+ 
+  /** Statement ID for correlation */
+  statementId?: string;
+ 
+  /** Workspace ID */
+  workspaceId?: string;
+ 
+  /** Driver configuration (for connection metrics) */
+  driverConfig?: DriverConfiguration;
+ 
+  /** Execution latency in milliseconds */
+  latencyMs?: number;
+ 
+  /** Result format (inline, cloudfetch, arrow) */
+  resultFormat?: string;
+ 
+  /** Number of result chunks */
+  chunkCount?: number;
+ 
+  /** Total bytes downloaded */
+  bytesDownloaded?: number;
+ 
+  /** Number of poll operations */
+  pollCount?: number;
+ 
+  /** Error name/type */
+  errorName?: string;
+ 
+  /** Error message */
+  errorMessage?: string;
+}
+ 
+/**
+ * Driver configuration metadata collected once per connection
+ */
+export interface DriverConfiguration {
+  /** Driver version */
+  driverVersion: string;
+ 
+  /** Driver name */
+  driverName: string;
+ 
+  /** Node.js version */
+  nodeVersion: string;
+ 
+  /** Platform (linux, darwin, win32) */
+  platform: string;
+ 
+  /** OS version */
+  osVersion: string;
+ 
+  /** OS architecture (x64, arm64, etc.) */
+  osArch: string;
+ 
+  /** Runtime vendor (Node.js Foundation) */
+  runtimeVendor: string;
+ 
+  /** Locale name (e.g., en_US) */
+  localeName: string;
+ 
+  /** Character set encoding (e.g., UTF-8) */
+  charSetEncoding: string;
+ 
+  /** Process name */
+  processName: string;
+ 
+  // Feature flags
+  /** Whether CloudFetch is enabled */
+  cloudFetchEnabled: boolean;
+ 
+  /** Whether LZ4 compression is enabled */
+  lz4Enabled: boolean;
+ 
+  /** Whether Arrow format is enabled */
+  arrowEnabled: boolean;
+ 
+  /** Whether direct results are enabled */
+  directResultsEnabled: boolean;
+ 
+  // Configuration values
+  /** Socket timeout in milliseconds */
+  socketTimeout: number;
+ 
+  /** Maximum retry attempts */
+  retryMaxAttempts: number;
+ 
+  /** Number of concurrent CloudFetch downloads */
+  cloudFetchConcurrentDownloads: number;
+}
+ 
+/**
+ * Per-statement metrics aggregated from multiple events
+ */
+export interface StatementMetrics {
+  /** Statement ID */
+  statementId: string;
+ 
+  /** Session ID */
+  sessionId: string;
+ 
+  /** Type of operation */
+  operationType?: string;
+ 
+  /** Start timestamp (milliseconds since epoch) */
+  startTime: number;
+ 
+  /** Total execution latency in milliseconds */
+  executionLatencyMs?: number;
+ 
+  /** Number of poll operations */
+  pollCount: number;
+ 
+  /** Total poll latency in milliseconds */
+  pollLatencyMs: number;
+ 
+  /** Result format (inline, cloudfetch, arrow) */
+  resultFormat?: string;
+ 
+  /** Number of CloudFetch chunks downloaded */
+  chunkCount: number;
+ 
+  /** Total bytes downloaded */
+  totalBytesDownloaded: number;
+ 
+  /** Whether compression was used */
+  compressionEnabled?: boolean;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/CloseableCollection.ts.html b/coverage/lcov-report/lib/utils/CloseableCollection.ts.html new file mode 100644 index 00000000..85bf1fa7 --- /dev/null +++ b/coverage/lcov-report/lib/utils/CloseableCollection.ts.html @@ -0,0 +1,181 @@ + + + + + + Code coverage report for lib/utils/CloseableCollection.ts + + + + + + + + + +
+
+

All files / lib/utils CloseableCollection.ts

+
+ +
+ 7.69% + Statements + 1/13 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 7.69% + Lines + 1/13 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
export interface ICloseable {
+  onClose?: () => void;
+ 
+  close(): Promise<unknown>;
+}
+ 
+export default class CloseableCollection<T extends ICloseable> {
+  private items = new Set<T>();
+ 
+  public add(item: T) {
+    item.onClose = () => {
+      this.delete(item);
+    };
+    this.items.add(item);
+  }
+ 
+  public delete(item: T) {
+    if (this.items.has(item)) {
+      item.onClose = undefined;
+    }
+    this.items.delete(item);
+  }
+ 
+  public async closeAll() {
+    const items = [...this.items];
+    for (const item of items) {
+      await item.close(); // eslint-disable-line no-await-in-loop
+      item.onClose = undefined;
+      this.items.delete(item);
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/OperationIterator.ts.html b/coverage/lcov-report/lib/utils/OperationIterator.ts.html new file mode 100644 index 00000000..55a147a2 --- /dev/null +++ b/coverage/lcov-report/lib/utils/OperationIterator.ts.html @@ -0,0 +1,340 @@ + + + + + + Code coverage report for lib/utils/OperationIterator.ts + + + + + + + + + +
+
+

All files / lib/utils OperationIterator.ts

+
+ +
+ 6.66% + Statements + 2/30 +
+ + +
+ 0% + Branches + 0/20 +
+ + +
+ 0% + Functions + 0/7 +
+ + +
+ 6.66% + Lines + 2/30 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import IOperation, { IOperationChunksIterator, IOperationRowsIterator, IteratorOptions } from '../contracts/IOperation';
+ 
+abstract class OperationIterator<R> implements AsyncIterableIterator<R> {
+  public readonly operation: IOperation;
+ 
+  protected readonly options?: IteratorOptions;
+ 
+  constructor(operation: IOperation, options?: IteratorOptions) {
+    this.operation = operation;
+    this.options = options;
+  }
+ 
+  protected abstract getNext(): Promise<IteratorResult<R>>;
+ 
+  public [Symbol.asyncIterator]() {
+    return this;
+  }
+ 
+  public async next() {
+    const result = await this.getNext();
+ 
+    if (result.done && this.options?.autoClose) {
+      await this.operation.close();
+    }
+ 
+    return result;
+  }
+ 
+  // This method is intended for a cleanup when the caller does not intend to make any more
+  // reads from iterator (e.g. when using `break` in a `for ... of` loop)
+  public async return(value?: any) {
+    if (this.options?.autoClose) {
+      await this.operation.close();
+    }
+ 
+    return { done: true, value };
+  }
+}
+ 
+export class OperationChunksIterator extends OperationIterator<Array<object>> implements IOperationChunksIterator {
+  protected async getNext(): Promise<IteratorResult<Array<object>>> {
+    const hasMoreRows = await this.operation.hasMoreRows();
+    if (hasMoreRows) {
+      const value = await this.operation.fetchChunk(this.options);
+      return { done: false, value };
+    }
+ 
+    return { done: true, value: undefined };
+  }
+}
+ 
+export class OperationRowsIterator extends OperationIterator<object> implements IOperationRowsIterator {
+  private chunk: Array<object> = [];
+ 
+  private index: number = 0;
+ 
+  constructor(operation: IOperation, options?: IteratorOptions) {
+    super(operation, {
+      ...options,
+      // Tell slicer to return raw chunks. We're going to process rows one by one anyway,
+      // so no need to additionally buffer and slice chunks returned by server
+      disableBuffering: true,
+    });
+  }
+ 
+  protected async getNext(): Promise<IteratorResult<object>> {
+    if (this.index < this.chunk.length) {
+      const value = this.chunk[this.index];
+      this.index += 1;
+      return { done: false, value };
+    }
+ 
+    const hasMoreRows = await this.operation.hasMoreRows();
+    if (hasMoreRows) {
+      this.chunk = await this.operation.fetchChunk(this.options);
+      this.index = 0;
+      // Note: this call is not really a recursion. Since this method is
+      // async - the call will be actually scheduled for processing on
+      // the next event loop cycle
+      return this.getNext();
+    }
+ 
+    return { done: true, value: undefined };
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/buildUserAgentString.ts.html b/coverage/lcov-report/lib/utils/buildUserAgentString.ts.html new file mode 100644 index 00000000..dd0474ef --- /dev/null +++ b/coverage/lcov-report/lib/utils/buildUserAgentString.ts.html @@ -0,0 +1,178 @@ + + + + + + Code coverage report for lib/utils/buildUserAgentString.ts + + + + + + + + + +
+
+

All files / lib/utils buildUserAgentString.ts

+
+ +
+ 26.66% + Statements + 4/15 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 26.66% + Lines + 4/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +321x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  + 
import os from 'os';
+import packageVersion from '../version';
+ 
+const productName = 'NodejsDatabricksSqlConnector';
+ 
+function getNodeVersion(): string {
+  return `Node.js ${process.versions.node}`;
+}
+ 
+function getOperatingSystemVersion(): string {
+  return `${os.type()} ${os.release()}`;
+}
+ 
+function redactInternalToken(userAgentEntry: string): string {
+  const internalTokenPrefixes = ['dkea', 'dskea', 'dapi', 'dsapi', 'dose'];
+  for (const prefix of internalTokenPrefixes) {
+    if (userAgentEntry.startsWith(prefix)) {
+      return '<REDACTED>';
+    }
+  }
+  return userAgentEntry;
+}
+ 
+export default function buildUserAgentString(userAgentEntry?: string): string {
+  if (userAgentEntry) {
+    userAgentEntry = redactInternalToken(userAgentEntry);
+  }
+ 
+  const extra = [userAgentEntry, getNodeVersion(), getOperatingSystemVersion()].filter(Boolean);
+  return `${productName}/${packageVersion} (${extra.join('; ')})`;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/definedOrError.ts.html b/coverage/lcov-report/lib/utils/definedOrError.ts.html new file mode 100644 index 00000000..fd231774 --- /dev/null +++ b/coverage/lcov-report/lib/utils/definedOrError.ts.html @@ -0,0 +1,103 @@ + + + + + + Code coverage report for lib/utils/definedOrError.ts + + + + + + + + + +
+
+

All files / lib/utils definedOrError.ts

+
+ +
+ 25% + Statements + 1/4 +
+ + +
+ 0% + Branches + 0/2 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 25% + Lines + 1/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +71x +  +  +  +  +  + 
export default function definedOrError<T>(value: T | undefined): T {
+  if (value === undefined) {
+    throw new TypeError('Value is undefined');
+  }
+  return value;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/formatProgress.ts.html b/coverage/lcov-report/lib/utils/formatProgress.ts.html new file mode 100644 index 00000000..d9754f4f --- /dev/null +++ b/coverage/lcov-report/lib/utils/formatProgress.ts.html @@ -0,0 +1,166 @@ + + + + + + Code coverage report for lib/utils/formatProgress.ts + + + + + + + + + +
+
+

All files / lib/utils formatProgress.ts

+
+ +
+ 16.66% + Statements + 2/12 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/6 +
+ + +
+ 20% + Lines + 2/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  + 
import { TProgressUpdateResp } from '../../thrift/TCLIService_types';
+ 
+export class ProgressUpdateTransformer {
+  private progressUpdate: TProgressUpdateResp;
+ 
+  private rowWidth: number = 10;
+ 
+  constructor(progressUpdate: TProgressUpdateResp) {
+    this.progressUpdate = progressUpdate;
+  }
+ 
+  formatRow(row: Array<string>): string {
+    return row.map((cell) => cell.padEnd(this.rowWidth, ' ')).join('|');
+  }
+ 
+  toString() {
+    const header = this.formatRow(this.progressUpdate.headerNames);
+    const footer = this.progressUpdate.footerSummary;
+    const rows = this.progressUpdate.rows.map((row: Array<string>) => this.formatRow(row));
+ 
+    return [header, ...rows, footer].join('\n');
+  }
+}
+ 
+export default function formatProgress(progressUpdate: TProgressUpdateResp): string {
+  return String(new ProgressUpdateTransformer(progressUpdate));
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/index.html b/coverage/lcov-report/lib/utils/index.html new file mode 100644 index 00000000..9dc2afec --- /dev/null +++ b/coverage/lcov-report/lib/utils/index.html @@ -0,0 +1,221 @@ + + + + + + Code coverage report for lib/utils + + + + + + + + + +
+
+

All files lib/utils

+
+ +
+ 25.83% + Statements + 31/120 +
+ + +
+ 0% + Branches + 0/48 +
+ + +
+ 0% + Functions + 0/34 +
+ + +
+ 23% + Lines + 26/113 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
CloseableCollection.ts +
+
7.69%1/130%0/20%0/57.69%1/13
OperationIterator.ts +
+
6.66%2/300%0/200%0/76.66%2/30
buildUserAgentString.ts +
+
26.66%4/150%0/40%0/426.66%4/15
definedOrError.ts +
+
25%1/40%0/20%0/125%1/4
formatProgress.ts +
+
16.66%2/12100%0/00%0/620%2/10
index.ts +
+
100%11/11100%0/00%0/1100%6/6
lz4.ts +
+
6.25%1/160%0/160%0/26.25%1/16
protocolVersion.ts +
+
47.36%9/190%0/40%0/847.36%9/19
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/index.ts.html b/coverage/lcov-report/lib/utils/index.ts.html new file mode 100644 index 00000000..e0e80f29 --- /dev/null +++ b/coverage/lcov-report/lib/utils/index.ts.html @@ -0,0 +1,106 @@ + + + + + + Code coverage report for lib/utils/index.ts + + + + + + + + + +
+
+

All files / lib/utils index.ts

+
+ +
+ 100% + Statements + 11/11 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 100% + Lines + 6/6 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +81x +1x +1x +1x +1x +  +1x + 
import definedOrError from './definedOrError';
+import buildUserAgentString from './buildUserAgentString';
+import formatProgress, { ProgressUpdateTransformer } from './formatProgress';
+import LZ4 from './lz4';
+import * as ProtocolVersion from './protocolVersion';
+ 
+export { definedOrError, buildUserAgentString, formatProgress, ProgressUpdateTransformer, LZ4, ProtocolVersion };
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/lz4.ts.html b/coverage/lcov-report/lib/utils/lz4.ts.html new file mode 100644 index 00000000..5cf41703 --- /dev/null +++ b/coverage/lcov-report/lib/utils/lz4.ts.html @@ -0,0 +1,211 @@ + + + + + + Code coverage report for lib/utils/lz4.ts + + + + + + + + + +
+
+

All files / lib/utils lz4.ts

+
+ +
+ 6.25% + Statements + 1/16 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 6.25% + Lines + 1/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
import type LZ4Namespace from 'lz4';
+ 
+type LZ4Module = typeof LZ4Namespace;
+ 
+function tryLoadLZ4Module(): LZ4Module | undefined {
+  try {
+    return require('lz4'); // eslint-disable-line global-require
+  } catch (err) {
+    if (!(err instanceof Error) || !('code' in err)) {
+      // eslint-disable-next-line no-console
+      console.warn('Unexpected error loading LZ4 module: Invalid error object', err);
+      return undefined;
+    }
+ 
+    if (err.code === 'MODULE_NOT_FOUND') {
+      return undefined;
+    }
+ 
+    if (err.code === 'ERR_DLOPEN_FAILED') {
+      // eslint-disable-next-line no-console
+      console.warn('LZ4 native module failed to load: Architecture or version mismatch', err);
+      return undefined;
+    }
+ 
+    // If it's not a known error, return undefined
+    // eslint-disable-next-line no-console
+    console.warn('Unknown error loading LZ4 module: Unhandled error code', err);
+    return undefined;
+  }
+}
+ 
+// The null is already tried resolving that failed
+let resolvedModule: LZ4Module | null | undefined;
+ 
+function getResolvedModule() {
+  if (resolvedModule === undefined) {
+    resolvedModule = tryLoadLZ4Module() ?? null;
+  }
+  return resolvedModule === null ? undefined : resolvedModule;
+}
+ 
+export default getResolvedModule;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/protocolVersion.ts.html b/coverage/lcov-report/lib/utils/protocolVersion.ts.html new file mode 100644 index 00000000..fc11d771 --- /dev/null +++ b/coverage/lcov-report/lib/utils/protocolVersion.ts.html @@ -0,0 +1,370 @@ + + + + + + Code coverage report for lib/utils/protocolVersion.ts + + + + + + + + + +
+
+

All files / lib/utils protocolVersion.ts

+
+ +
+ 47.36% + Statements + 9/19 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 0% + Functions + 0/8 +
+ + +
+ 47.36% + Lines + 9/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +961x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  + 
import { TProtocolVersion } from '../../thrift/TCLIService_types';
+ 
+/**
+ * Protocol version information from Thrift TCLIService
+ * Each version adds certain features to the Spark/Hive API
+ *
+ * Databricks only supports SPARK_CLI_SERVICE_PROTOCOL_V1 (0xA501) or higher
+ */
+ 
+/**
+ * Check if the current protocol version supports a specific feature
+ * @param serverProtocolVersion The protocol version received from server in TOpenSessionResp
+ * @param requiredVersion The minimum protocol version required for a feature
+ * @returns boolean indicating if the feature is supported
+ */
+export function isFeatureSupported(
+  serverProtocolVersion: TProtocolVersion | undefined | null,
+  requiredVersion: TProtocolVersion,
+): boolean {
+  if (serverProtocolVersion === undefined || serverProtocolVersion === null) {
+    return false;
+  }
+ 
+  return serverProtocolVersion >= requiredVersion;
+}
+ 
+/**
+ * Check if parameterized queries are supported
+ * (Requires SPARK_CLI_SERVICE_PROTOCOL_V8 or higher)
+ * @param serverProtocolVersion The protocol version from server
+ * @returns boolean indicating if parameterized queries are supported
+ */
+export function supportsParameterizedQueries(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
+  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V8);
+}
+ 
+/**
+ * Check if async metadata operations are supported
+ * (Requires SPARK_CLI_SERVICE_PROTOCOL_V6 or higher)
+ * @param serverProtocolVersion The protocol version from server
+ * @returns boolean indicating if async metadata operations are supported
+ */
+export function supportsAsyncMetadataOperations(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
+  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V6);
+}
+ 
+/**
+ * Check if result persistence mode is supported
+ * (Requires SPARK_CLI_SERVICE_PROTOCOL_V7 or higher)
+ * @param serverProtocolVersion The protocol version from server
+ * @returns boolean indicating if result persistence mode is supported
+ */
+export function supportsResultPersistenceMode(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
+  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V7);
+}
+ 
+/**
+ * Check if Arrow compression is supported
+ * (Requires SPARK_CLI_SERVICE_PROTOCOL_V6 or higher)
+ * @param serverProtocolVersion The protocol version from server
+ * @returns boolean indicating if compressed Arrow batches are supported
+ */
+export function supportsArrowCompression(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
+  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V6);
+}
+ 
+/**
+ * Check if Arrow metadata is supported
+ * (Requires SPARK_CLI_SERVICE_PROTOCOL_V5 or higher)
+ * @param serverProtocolVersion The protocol version from server
+ * @returns boolean indicating if Arrow metadata is supported
+ */
+export function supportsArrowMetadata(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
+  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V5);
+}
+ 
+/**
+ * Check if multiple catalogs are supported
+ * (Requires SPARK_CLI_SERVICE_PROTOCOL_V4 or higher)
+ * @param serverProtocolVersion The protocol version from server
+ * @returns boolean indicating if multiple catalogs are supported
+ */
+export function supportsMultipleCatalogs(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
+  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V4);
+}
+ 
+/**
+ * Check if cloud object storage fetching is supported
+ * (Requires SPARK_CLI_SERVICE_PROTOCOL_V3 or higher)
+ * @param serverProtocolVersion The protocol version from server
+ * @returns boolean indicating if cloud fetching is supported
+ */
+export function supportsCloudFetch(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
+  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V3);
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/lib/version.ts.html b/coverage/lcov-report/lib/version.ts.html new file mode 100644 index 00000000..fcb6f96d --- /dev/null +++ b/coverage/lcov-report/lib/version.ts.html @@ -0,0 +1,88 @@ + + + + + + Code coverage report for lib/version.ts + + + + + + + + + +
+
+

All files / lib version.ts

+
+ +
+ 100% + Statements + 1/1 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 1/1 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +21x + 
export default '1.10.0';
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/prettify.css b/coverage/lcov-report/prettify.css new file mode 100644 index 00000000..b317a7cd --- /dev/null +++ b/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/coverage/lcov-report/prettify.js b/coverage/lcov-report/prettify.js new file mode 100644 index 00000000..b3225238 --- /dev/null +++ b/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/coverage/lcov-report/sort-arrow-sprite.png b/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed68316eb3f65dec9063332d2f69bf3093bbfab GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc literal 0 HcmV?d00001 diff --git a/coverage/lcov-report/sorter.js b/coverage/lcov-report/sorter.js new file mode 100644 index 00000000..2bb296a8 --- /dev/null +++ b/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/coverage/lcov.info b/coverage/lcov.info new file mode 100644 index 00000000..6f55ac90 --- /dev/null +++ b/coverage/lcov.info @@ -0,0 +1,5240 @@ +TN: +SF:lib/DBSQLClient.ts +FN:36,prependSlash +FN:43,getInitialNamespaceOptions +FN:79,(anonymous_9) +FN:86,(anonymous_10) +FN:108,(anonymous_11) +FN:115,(anonymous_12) +FN:129,(anonymous_13) +FN:193,(anonymous_14) +FN:212,(anonymous_15) +FN:225,(anonymous_16) +FN:248,(anonymous_17) +FN:260,(anonymous_18) +FN:265,(anonymous_19) +FN:270,(anonymous_20) +FN:287,(anonymous_21) +FN:313,(anonymous_22) +FN:321,(anonymous_23) +FN:325,(anonymous_24) +FN:329,(anonymous_25) +FN:337,(anonymous_26) +FN:353,(anonymous_27) +FN:357,(anonymous_28) +FNF:22 +FNH:1 +FNDA:0,prependSlash +FNDA:0,getInitialNamespaceOptions +FNDA:0,(anonymous_9) +FNDA:55,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:0,(anonymous_27) +FNDA:0,(anonymous_28) +DA:1,1 +DA:2,1 +DA:4,1 +DA:6,1 +DA:7,1 +DA:12,1 +DA:13,1 +DA:16,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:37,0 +DA:38,0 +DA:40,0 +DA:44,0 +DA:45,0 +DA:48,0 +DA:58,1 +DA:69,0 +DA:75,0 +DA:77,0 +DA:80,0 +DA:81,0 +DA:83,0 +DA:87,55 +DA:109,0 +DA:110,0 +DA:111,0 +DA:112,0 +DA:116,0 +DA:130,0 +DA:131,0 +DA:134,0 +DA:137,0 +DA:143,0 +DA:154,0 +DA:156,0 +DA:166,0 +DA:176,0 +DA:200,0 +DA:203,0 +DA:204,0 +DA:209,0 +DA:213,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:232,0 +DA:233,0 +DA:238,0 +DA:239,0 +DA:242,0 +DA:244,0 +DA:246,0 +DA:248,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:270,0 +DA:271,0 +DA:272,0 +DA:275,0 +DA:289,0 +DA:292,0 +DA:293,0 +DA:296,0 +DA:303,0 +DA:304,0 +DA:309,0 +DA:310,0 +DA:314,0 +DA:316,0 +DA:317,0 +DA:318,0 +DA:322,0 +DA:326,0 +DA:330,0 +DA:331,0 +DA:334,0 +DA:338,0 +DA:340,0 +DA:341,0 +DA:342,0 +DA:345,0 +DA:346,0 +DA:347,0 +DA:350,0 +DA:354,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:362,0 +DA:363,0 +DA:366,0 +LF:106 +LH:19 +BRDA:37,0,0,0 +BRDA:37,0,1,0 +BRDA:37,1,0,0 +BRDA:37,1,1,0 +BRDA:44,2,0,0 +BRDA:44,2,1,0 +BRDA:44,3,0,0 +BRDA:44,3,1,0 +BRDA:80,4,0,0 +BRDA:80,4,1,0 +BRDA:111,5,0,0 +BRDA:111,5,1,0 +BRDA:111,6,0,0 +BRDA:111,6,1,0 +BRDA:111,7,0,0 +BRDA:111,7,1,0 +BRDA:111,8,0,0 +BRDA:111,8,1,0 +BRDA:118,9,0,0 +BRDA:118,9,1,0 +BRDA:130,10,0,0 +BRDA:130,10,1,0 +BRDA:134,11,0,0 +BRDA:134,11,1,0 +BRDA:134,11,2,0 +BRDA:134,11,3,0 +BRDA:134,11,4,0 +BRDA:134,11,5,0 +BRDA:134,11,6,0 +BRDA:144,12,0,0 +BRDA:144,12,1,0 +BRDA:203,13,0,0 +BRDA:203,13,1,0 +BRDA:227,14,0,0 +BRDA:227,14,1,0 +BRDA:232,15,0,0 +BRDA:232,15,1,0 +BRDA:238,16,0,0 +BRDA:238,16,1,0 +BRDA:251,17,0,0 +BRDA:251,17,1,0 +BRDA:287,18,0,0 +BRDA:289,19,0,0 +BRDA:289,19,1,0 +BRDA:292,20,0,0 +BRDA:292,20,1,0 +BRDA:330,21,0,0 +BRDA:330,21,1,0 +BRDA:340,22,0,0 +BRDA:340,22,1,0 +BRDA:345,23,0,0 +BRDA:345,23,1,0 +BRDA:358,24,0,0 +BRDA:358,24,1,0 +BRF:54 +BRH:0 +end_of_record +TN: +SF:lib/DBSQLLogger.ts +FN:12,(anonymous_1) +FN:25,(anonymous_2) +FN:29,(anonymous_3) +FNF:3 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:1,1 +DA:2,1 +DA:4,1 +DA:13,0 +DA:16,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:26,0 +DA:30,0 +DA:31,0 +DA:32,0 +LF:12 +LH:3 +BRDA:12,0,0,0 +BRDA:12,1,0,0 +BRDA:19,2,0,0 +BRDA:19,2,1,0 +BRDA:31,3,0,0 +BRDA:31,3,1,0 +BRF:6 +BRH:0 +end_of_record +TN: +SF:lib/DBSQLOperation.ts +FN:44,delay +FN:45,(anonymous_8) +FN:46,(anonymous_9) +FN:79,(anonymous_10) +FN:100,(anonymous_11) +FN:104,(anonymous_12) +FN:108,(anonymous_13) +FN:125,(anonymous_14) +FN:139,(anonymous_15) +FN:168,(anonymous_16) +FN:194,(anonymous_17) +FN:220,(anonymous_18) +FN:241,(anonymous_19) +FN:265,(anonymous_20) +FN:286,(anonymous_21) +FN:291,(anonymous_22) +FN:308,(anonymous_23) +FN:322,(anonymous_24) +FN:328,(anonymous_25) +FN:337,(anonymous_26) +FN:393,(anonymous_27) +FN:405,(anonymous_28) +FN:423,(anonymous_29) +FN:463,(anonymous_30) +FNF:24 +FNH:0 +FNDA:0,delay +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:0,(anonymous_27) +FNDA:0,(anonymous_28) +FNDA:0,(anonymous_29) +FNDA:0,(anonymous_30) +DA:1,1 +DA:2,1 +DA:13,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:45,0 +DA:46,0 +DA:47,0 +DA:52,1 +DA:63,0 +DA:65,0 +DA:71,0 +DA:80,0 +DA:81,0 +DA:83,0 +DA:85,0 +DA:86,0 +DA:89,0 +DA:90,0 +DA:96,0 +DA:97,0 +DA:101,0 +DA:105,0 +DA:111,0 +DA:113,0 +DA:114,0 +DA:116,0 +DA:117,0 +DA:119,0 +DA:122,0 +DA:126,0 +DA:127,0 +DA:140,0 +DA:142,0 +DA:149,0 +DA:151,0 +DA:152,0 +DA:154,0 +DA:156,0 +DA:169,0 +DA:171,0 +DA:172,0 +DA:175,0 +DA:177,0 +DA:178,0 +DA:194,0 +DA:195,0 +DA:198,0 +DA:200,0 +DA:204,0 +DA:206,0 +DA:212,0 +DA:221,0 +DA:222,0 +DA:224,0 +DA:225,0 +DA:228,0 +DA:229,0 +DA:234,0 +DA:242,0 +DA:243,0 +DA:246,0 +DA:248,0 +DA:249,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:257,0 +DA:258,0 +DA:266,0 +DA:267,0 +DA:270,0 +DA:272,0 +DA:274,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:282,0 +DA:283,0 +DA:287,0 +DA:288,0 +DA:293,0 +DA:294,0 +DA:299,0 +DA:300,0 +DA:304,0 +DA:305,0 +DA:309,0 +DA:311,0 +DA:312,0 +DA:315,0 +DA:317,0 +DA:318,0 +DA:319,0 +DA:323,0 +DA:324,0 +DA:325,0 +DA:329,0 +DA:330,0 +DA:332,0 +DA:333,0 +DA:338,0 +DA:339,0 +DA:342,0 +DA:344,0 +DA:346,0 +DA:348,0 +DA:350,0 +DA:353,0 +DA:358,0 +DA:362,0 +DA:363,0 +DA:367,0 +DA:368,0 +DA:372,0 +DA:373,0 +DA:377,0 +DA:379,0 +DA:382,0 +DA:386,0 +DA:388,0 +DA:395,0 +DA:396,0 +DA:400,0 +DA:401,0 +DA:405,0 +DA:406,0 +DA:407,0 +DA:410,0 +DA:411,0 +DA:412,0 +DA:415,0 +DA:416,0 +DA:419,0 +DA:424,0 +DA:425,0 +DA:427,0 +DA:430,0 +DA:432,0 +DA:433,0 +DA:435,0 +DA:440,0 +DA:442,0 +DA:447,0 +DA:451,0 +DA:452,0 +DA:456,0 +DA:457,0 +DA:460,0 +DA:464,0 +DA:466,0 +DA:468,0 +DA:469,0 +DA:472,0 +DA:478,0 +DA:479,0 +DA:482,0 +LF:167 +LH:16 +BRDA:83,0,0,0 +BRDA:83,0,1,0 +BRDA:83,1,0,0 +BRDA:83,1,1,0 +BRDA:85,2,0,0 +BRDA:85,2,1,0 +BRDA:85,3,0,0 +BRDA:85,3,1,0 +BRDA:85,4,0,0 +BRDA:85,4,1,0 +BRDA:89,5,0,0 +BRDA:89,5,1,0 +BRDA:89,6,0,0 +BRDA:89,6,1,0 +BRDA:93,7,0,0 +BRDA:93,7,1,0 +BRDA:93,8,0,0 +BRDA:93,8,1,0 +BRDA:96,9,0,0 +BRDA:96,9,1,0 +BRDA:96,10,0,0 +BRDA:96,10,1,0 +BRDA:111,11,0,0 +BRDA:111,11,1,0 +BRDA:111,11,2,0 +BRDA:111,12,0,0 +BRDA:111,12,1,0 +BRDA:111,13,0,0 +BRDA:111,13,1,0 +BRDA:111,14,0,0 +BRDA:111,14,1,0 +BRDA:111,15,0,0 +BRDA:111,15,1,0 +BRDA:113,16,0,0 +BRDA:113,16,1,0 +BRDA:113,17,0,0 +BRDA:113,17,1,0 +BRDA:116,18,0,0 +BRDA:116,18,1,0 +BRDA:116,19,0,0 +BRDA:116,19,1,0 +BRDA:119,20,0,0 +BRDA:119,20,1,0 +BRDA:119,21,0,0 +BRDA:119,21,1,0 +BRDA:122,22,0,0 +BRDA:122,22,1,0 +BRDA:122,23,0,0 +BRDA:122,23,1,0 +BRDA:126,24,0,0 +BRDA:126,24,1,0 +BRDA:126,25,0,0 +BRDA:126,25,1,0 +BRDA:126,26,0,0 +BRDA:126,26,1,0 +BRDA:126,27,0,0 +BRDA:126,27,1,0 +BRDA:127,28,0,0 +BRDA:127,28,1,0 +BRDA:171,29,0,0 +BRDA:171,29,1,0 +BRDA:201,30,0,0 +BRDA:201,30,1,0 +BRDA:201,31,0,0 +BRDA:201,31,1,0 +BRDA:201,32,0,0 +BRDA:201,32,1,0 +BRDA:201,33,0,0 +BRDA:201,33,1,0 +BRDA:202,34,0,0 +BRDA:202,34,1,0 +BRDA:202,35,0,0 +BRDA:202,35,1,0 +BRDA:210,36,0,0 +BRDA:210,36,1,0 +BRDA:210,37,0,0 +BRDA:210,37,1,0 +BRDA:210,38,0,0 +BRDA:210,38,1,0 +BRDA:210,39,0,0 +BRDA:210,39,1,0 +BRDA:220,40,0,0 +BRDA:224,41,0,0 +BRDA:224,41,1,0 +BRDA:242,42,0,0 +BRDA:242,42,1,0 +BRDA:242,43,0,0 +BRDA:242,43,1,0 +BRDA:257,44,0,0 +BRDA:257,44,1,0 +BRDA:257,45,0,0 +BRDA:257,45,1,0 +BRDA:266,46,0,0 +BRDA:266,46,1,0 +BRDA:266,47,0,0 +BRDA:266,47,1,0 +BRDA:274,48,0,0 +BRDA:274,48,1,0 +BRDA:274,49,0,0 +BRDA:274,49,1,0 +BRDA:282,50,0,0 +BRDA:282,50,1,0 +BRDA:282,51,0,0 +BRDA:282,51,1,0 +BRDA:293,52,0,0 +BRDA:293,52,1,0 +BRDA:293,53,0,0 +BRDA:293,53,1,0 +BRDA:299,54,0,0 +BRDA:299,54,1,0 +BRDA:311,55,0,0 +BRDA:311,55,1,0 +BRDA:319,56,0,0 +BRDA:319,56,1,0 +BRDA:319,57,0,0 +BRDA:319,57,1,0 +BRDA:329,58,0,0 +BRDA:329,58,1,0 +BRDA:332,59,0,0 +BRDA:332,59,1,0 +BRDA:338,60,0,0 +BRDA:338,60,1,0 +BRDA:346,61,0,0 +BRDA:346,61,1,0 +BRDA:346,62,0,0 +BRDA:346,62,1,0 +BRDA:348,63,0,0 +BRDA:348,63,1,0 +BRDA:348,64,0,0 +BRDA:348,64,1,0 +BRDA:348,65,0,0 +BRDA:348,65,1,0 +BRDA:353,66,0,0 +BRDA:353,66,1,0 +BRDA:353,66,2,0 +BRDA:353,66,3,0 +BRDA:353,66,4,0 +BRDA:353,66,5,0 +BRDA:353,66,6,0 +BRDA:353,66,7,0 +BRDA:353,66,8,0 +BRDA:353,66,9,0 +BRDA:386,67,0,0 +BRDA:386,67,1,0 +BRDA:395,68,0,0 +BRDA:395,68,1,0 +BRDA:400,69,0,0 +BRDA:400,69,1,0 +BRDA:427,70,0,0 +BRDA:427,70,1,0 +BRDA:430,71,0,0 +BRDA:430,71,1,0 +BRDA:430,71,2,0 +BRDA:451,72,0,0 +BRDA:451,72,1,0 +BRDA:456,73,0,0 +BRDA:456,73,1,0 +BRDA:466,74,0,0 +BRDA:466,74,1,0 +BRDA:466,75,0,0 +BRDA:466,75,1,0 +BRDA:468,76,0,0 +BRDA:468,76,1,0 +BRDA:478,77,0,0 +BRDA:478,77,1,0 +BRF:165 +BRH:0 +end_of_record +TN: +SF:lib/DBSQLParameter.ts +FN:6,(anonymous_1) +FN:37,(anonymous_2) +FN:42,(anonymous_3) +FNF:3 +FNH:1 +FNDA:1,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:1,1 +DA:2,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:32,1 +DA:38,0 +DA:39,0 +DA:44,0 +DA:45,0 +DA:49,0 +DA:50,0 +DA:53,0 +DA:54,0 +DA:63,0 +DA:64,0 +DA:73,0 +DA:74,0 +DA:83,0 +DA:84,0 +DA:93,0 +LF:33 +LH:18 +BRDA:6,0,0,1 +BRDA:6,0,1,1 +BRDA:42,1,0,0 +BRDA:44,2,0,0 +BRDA:44,2,1,0 +BRDA:49,3,0,0 +BRDA:49,3,1,0 +BRDA:49,4,0,0 +BRDA:49,4,1,0 +BRDA:53,5,0,0 +BRDA:53,5,1,0 +BRDA:56,6,0,0 +BRDA:56,6,1,0 +BRDA:56,7,0,0 +BRDA:56,7,1,0 +BRDA:58,8,0,0 +BRDA:58,8,1,0 +BRDA:63,9,0,0 +BRDA:63,9,1,0 +BRDA:66,10,0,0 +BRDA:66,10,1,0 +BRDA:66,11,0,0 +BRDA:66,11,1,0 +BRDA:66,12,0,0 +BRDA:66,12,1,0 +BRDA:73,13,0,0 +BRDA:73,13,1,0 +BRDA:73,14,0,0 +BRDA:73,14,1,0 +BRDA:76,15,0,0 +BRDA:76,15,1,0 +BRDA:76,16,0,0 +BRDA:76,16,1,0 +BRDA:83,17,0,0 +BRDA:83,17,1,0 +BRDA:86,18,0,0 +BRDA:86,18,1,0 +BRDA:86,19,0,0 +BRDA:86,19,1,0 +BRDA:95,20,0,0 +BRDA:95,20,1,0 +BRDA:95,21,0,0 +BRDA:95,21,1,0 +BRF:43 +BRH:2 +end_of_record +TN: +SF:lib/DBSQLSession.ts +FN:52,numberToInt64 +FN:67,getDirectResultsOptions +FN:79,getArrowOptions +FN:106,getQueryParameters +FN:165,(anonymous_11) +FN:169,(anonymous_12) +FN:178,(anonymous_13) +FN:191,(anonymous_14) +FN:212,(anonymous_15) +FN:261,(anonymous_16) +FN:308,(anonymous_17) +FN:330,(anonymous_18) +FN:347,(anonymous_19) +FN:383,(anonymous_20) +FN:403,(anonymous_21) +FN:423,(anonymous_22) +FN:445,(anonymous_23) +FN:469,(anonymous_24) +FN:489,(anonymous_25) +FN:513,(anonymous_26) +FN:530,(anonymous_27) +FN:553,(anonymous_28) +FN:578,(anonymous_29) +FN:601,(anonymous_30) +FN:615,(anonymous_31) +FN:621,(anonymous_32) +FNF:26 +FNH:0 +FNDA:0,numberToInt64 +FNDA:0,getDirectResultsOptions +FNDA:0,getArrowOptions +FNDA:0,getQueryParameters +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:0,(anonymous_27) +FNDA:0,(anonymous_28) +FNDA:0,(anonymous_29) +FNDA:0,(anonymous_30) +FNDA:0,(anonymous_31) +FNDA:0,(anonymous_32) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:44,1 +DA:52,1 +DA:53,0 +DA:54,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:64,0 +DA:68,0 +DA:69,0 +DA:72,0 +DA:86,0 +DA:88,0 +DA:89,0 +DA:94,0 +DA:110,0 +DA:111,0 +DA:113,0 +DA:114,0 +DA:117,0 +DA:118,0 +DA:121,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:127,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:138,0 +DA:147,1 +DA:152,0 +DA:158,0 +DA:166,0 +DA:170,0 +DA:171,0 +DA:173,0 +DA:174,0 +DA:175,0 +DA:179,0 +DA:180,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:217,0 +DA:226,0 +DA:227,0 +DA:230,0 +DA:231,0 +DA:234,0 +DA:235,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:255,0 +DA:258,0 +DA:268,0 +DA:269,0 +DA:270,0 +DA:272,0 +DA:275,0 +DA:276,0 +DA:278,0 +DA:279,0 +DA:281,0 +DA:282,0 +DA:286,0 +DA:287,0 +DA:291,0 +DA:293,0 +DA:295,0 +DA:296,0 +DA:298,0 +DA:299,0 +DA:301,0 +DA:302,0 +DA:304,0 +DA:313,0 +DA:314,0 +DA:317,0 +DA:318,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:325,0 +DA:327,0 +DA:331,0 +DA:332,0 +DA:334,0 +DA:342,0 +DA:343,0 +DA:352,0 +DA:353,0 +DA:356,0 +DA:357,0 +DA:359,0 +DA:360,0 +DA:362,0 +DA:372,0 +DA:373,0 +DA:384,0 +DA:385,0 +DA:386,0 +DA:388,0 +DA:393,0 +DA:394,0 +DA:404,0 +DA:405,0 +DA:406,0 +DA:408,0 +DA:413,0 +DA:414,0 +DA:424,0 +DA:425,0 +DA:426,0 +DA:428,0 +DA:435,0 +DA:436,0 +DA:446,0 +DA:447,0 +DA:448,0 +DA:450,0 +DA:459,0 +DA:460,0 +DA:470,0 +DA:471,0 +DA:472,0 +DA:474,0 +DA:479,0 +DA:480,0 +DA:490,0 +DA:491,0 +DA:492,0 +DA:494,0 +DA:503,0 +DA:504,0 +DA:514,0 +DA:515,0 +DA:516,0 +DA:518,0 +DA:526,0 +DA:527,0 +DA:531,0 +DA:532,0 +DA:533,0 +DA:535,0 +DA:543,0 +DA:544,0 +DA:554,0 +DA:555,0 +DA:556,0 +DA:558,0 +DA:569,0 +DA:570,0 +DA:579,0 +DA:580,0 +DA:584,0 +DA:586,0 +DA:587,0 +DA:591,0 +DA:594,0 +DA:595,0 +DA:597,0 +DA:598,0 +DA:602,0 +DA:603,0 +DA:604,0 +DA:610,0 +DA:612,0 +DA:616,0 +DA:617,0 +DA:625,0 +DA:626,0 +DA:627,0 +LF:206 +LH:21 +BRDA:53,0,0,0 +BRDA:53,0,1,0 +BRDA:57,1,0,0 +BRDA:57,1,1,0 +BRDA:68,2,0,0 +BRDA:68,2,1,0 +BRDA:74,3,0,0 +BRDA:74,3,1,0 +BRDA:74,4,0,0 +BRDA:74,4,1,0 +BRDA:86,5,0,0 +BRDA:86,6,0,0 +BRDA:88,7,0,0 +BRDA:88,7,1,0 +BRDA:88,8,0,0 +BRDA:88,8,1,0 +BRDA:110,9,0,0 +BRDA:110,9,1,0 +BRDA:111,10,0,0 +BRDA:111,10,1,0 +BRDA:113,11,0,0 +BRDA:113,11,1,0 +BRDA:113,12,0,0 +BRDA:113,12,1,0 +BRDA:117,13,0,0 +BRDA:117,13,1,0 +BRDA:117,14,0,0 +BRDA:117,14,1,0 +BRDA:123,15,0,0 +BRDA:123,15,1,0 +BRDA:126,16,0,0 +BRDA:126,16,1,0 +BRDA:131,17,0,0 +BRDA:131,17,1,0 +BRDA:133,18,0,0 +BRDA:133,18,1,0 +BRDA:166,19,0,0 +BRDA:166,19,1,0 +BRDA:179,20,0,0 +BRDA:179,20,1,0 +BRDA:179,21,0,0 +BRDA:179,21,1,0 +BRDA:179,22,0,0 +BRDA:179,22,1,0 +BRDA:179,23,0,0 +BRDA:179,23,1,0 +BRDA:180,24,0,0 +BRDA:180,24,1,0 +BRDA:212,25,0,0 +BRDA:220,26,0,0 +BRDA:220,26,1,0 +BRDA:226,27,0,0 +BRDA:226,27,1,0 +BRDA:230,28,0,0 +BRDA:230,28,1,0 +BRDA:231,29,0,0 +BRDA:231,29,1,0 +BRDA:231,30,0,0 +BRDA:231,30,1,0 +BRDA:234,31,0,0 +BRDA:234,31,1,0 +BRDA:234,32,0,0 +BRDA:234,32,1,0 +BRDA:235,33,0,0 +BRDA:235,33,1,0 +BRDA:235,34,0,0 +BRDA:235,34,1,0 +BRDA:235,35,0,0 +BRDA:235,35,1,0 +BRDA:249,36,0,0 +BRDA:249,36,1,0 +BRDA:251,37,0,0 +BRDA:251,37,1,0 +BRDA:252,38,0,0 +BRDA:252,38,1,0 +BRDA:269,39,0,0 +BRDA:269,39,1,0 +BRDA:275,40,0,0 +BRDA:275,40,1,0 +BRDA:281,41,0,0 +BRDA:281,41,1,0 +BRDA:281,42,0,0 +BRDA:281,42,1,0 +BRDA:286,43,0,0 +BRDA:286,43,1,0 +BRDA:293,44,0,0 +BRDA:293,44,1,0 +BRDA:293,44,2,0 +BRDA:293,44,3,0 +BRDA:313,45,0,0 +BRDA:313,45,1,0 +BRDA:321,46,0,0 +BRDA:321,46,1,0 +BRDA:342,47,0,0 +BRDA:342,47,1,0 +BRDA:342,48,0,0 +BRDA:342,48,1,0 +BRDA:352,49,0,0 +BRDA:352,49,1,0 +BRDA:372,50,0,0 +BRDA:372,50,1,0 +BRDA:383,51,0,0 +BRDA:403,52,0,0 +BRDA:423,53,0,0 +BRDA:445,54,0,0 +BRDA:469,55,0,0 +BRDA:489,56,0,0 +BRDA:579,57,0,0 +BRDA:579,57,1,0 +BRDA:594,58,0,0 +BRDA:594,58,1,0 +BRDA:594,59,0,0 +BRDA:594,59,1,0 +BRDA:616,60,0,0 +BRDA:616,60,1,0 +BRF:115 +BRH:0 +end_of_record +TN: +SF:lib/polyfills.ts +FN:8,toIntegerOrInfinity +FN:20,toLength +FN:26,at +FN:48,(anonymous_3) +FNF:4 +FNH:0 +FNDA:0,toIntegerOrInfinity +FNDA:0,toLength +FNDA:0,at +FNDA:0,(anonymous_3) +DA:9,0 +DA:12,0 +DA:13,0 +DA:16,0 +DA:21,0 +DA:22,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:33,0 +DA:48,0 +DA:49,0 +DA:50,0 +LF:15 +LH:0 +BRDA:12,0,0,0 +BRDA:12,0,1,0 +BRDA:13,1,0,0 +BRDA:13,1,1,0 +BRDA:22,2,0,0 +BRDA:22,2,1,0 +BRDA:29,3,0,0 +BRDA:29,3,1,0 +BRDA:30,4,0,0 +BRDA:30,4,1,0 +BRDA:30,5,0,0 +BRDA:30,5,1,0 +BRDA:49,6,0,0 +BRDA:49,6,1,0 +BRF:14 +BRH:0 +end_of_record +TN: +SF:lib/version.ts +FNF:0 +FNH:0 +DA:1,1 +LF:1 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/PlainHttpAuthentication.ts +FN:21,(anonymous_0) +FN:28,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:12,1 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:29,0 +LF:6 +LH:1 +BRDA:23,0,0,0 +BRDA:23,0,1,0 +BRDA:23,1,0,0 +BRDA:23,1,1,0 +BRDA:23,2,0,0 +BRDA:23,2,1,0 +BRDA:24,3,0,0 +BRDA:24,3,1,0 +BRDA:24,4,0,0 +BRDA:24,4,1,0 +BRDA:24,5,0,0 +BRDA:24,5,1,0 +BRDA:24,6,0,0 +BRDA:24,6,1,0 +BRDA:25,7,0,0 +BRDA:25,7,1,0 +BRDA:25,8,0,0 +BRDA:25,8,1,0 +BRDA:25,9,0,0 +BRDA:25,9,1,0 +BRF:20 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts +FN:20,defaultOpenAuthUrl +FN:39,(anonymous_2) +FN:45,(anonymous_3) +FN:52,(anonymous_4) +FN:91,(anonymous_5) +FN:100,(anonymous_6) +FN:109,(anonymous_7) +FN:110,(anonymous_8) +FN:125,(anonymous_9) +FN:129,(anonymous_10) +FN:136,(anonymous_11) +FN:137,(anonymous_12) +FN:143,(anonymous_13) +FN:150,(anonymous_14) +FN:155,(anonymous_15) +FN:156,(anonymous_16) +FN:162,(anonymous_17) +FN:169,(anonymous_18) +FNF:18 +FNH:0 +FNDA:0,defaultOpenAuthUrl +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:7,1 +DA:21,0 +DA:30,1 +DA:35,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:60,0 +DA:61,0 +DA:65,0 +DA:66,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:85,0 +DA:88,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:105,0 +DA:109,0 +DA:110,0 +DA:114,0 +DA:115,0 +DA:117,0 +DA:122,0 +DA:126,0 +DA:134,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:151,0 +DA:152,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:170,0 +DA:172,0 +LF:72 +LH:7 +BRDA:54,0,0,0 +BRDA:54,0,1,0 +BRDA:76,1,0,0 +BRDA:76,1,1,0 +BRDA:76,2,0,0 +BRDA:76,2,1,0 +BRDA:80,3,0,0 +BRDA:80,3,1,0 +BRDA:80,4,0,0 +BRDA:80,4,1,0 +BRDA:81,5,0,0 +BRDA:81,5,1,0 +BRDA:81,6,0,0 +BRDA:81,6,1,0 +BRDA:81,7,0,0 +BRDA:81,7,1,0 +BRDA:114,8,0,0 +BRDA:114,8,1,0 +BRDA:114,9,0,0 +BRDA:114,9,1,0 +BRDA:114,9,2,0 +BRDA:151,10,0,0 +BRDA:151,10,1,0 +BRF:23 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/DatabricksOAuth/OAuthManager.ts +FN:10,(anonymous_1) +FN:26,getDatabricksOIDCUrl +FN:43,(anonymous_3) +FN:58,(anonymous_4) +FN:64,(anonymous_5) +FN:103,(anonymous_6) +FN:123,(anonymous_7) +FN:127,(anonymous_8) +FN:147,(anonymous_9) +FN:173,(anonymous_10) +FN:193,(anonymous_11) +FN:203,(anonymous_12) +FN:208,(anonymous_13) +FN:215,(anonymous_14) +FN:223,(anonymous_15) +FN:230,(anonymous_16) +FN:247,(anonymous_17) +FN:251,(anonymous_18) +FN:255,(anonymous_19) +FN:259,(anonymous_20) +FN:263,(anonymous_21) +FN:279,(anonymous_22) +FN:283,(anonymous_23) +FN:287,(anonymous_24) +FN:291,(anonymous_25) +FN:295,(anonymous_26) +FNF:26 +FNH:1 +FNDA:1,(anonymous_1) +FNDA:0,getDatabricksOIDCUrl +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:27,0 +DA:28,0 +DA:29,0 +DA:32,1 +DA:44,0 +DA:45,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:68,0 +DA:75,0 +DA:78,0 +DA:82,0 +DA:87,0 +DA:90,0 +DA:91,0 +DA:97,0 +DA:100,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:111,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:120,0 +DA:124,0 +DA:128,0 +DA:129,0 +DA:131,0 +DA:134,0 +DA:135,0 +DA:138,0 +DA:140,0 +DA:142,0 +DA:148,0 +DA:150,0 +DA:156,0 +DA:158,0 +DA:160,0 +DA:167,0 +DA:168,0 +DA:170,0 +DA:174,0 +DA:176,0 +DA:182,0 +DA:187,0 +DA:188,0 +DA:190,0 +DA:194,0 +DA:196,0 +DA:198,0 +DA:205,0 +DA:207,0 +DA:208,0 +DA:209,0 +DA:211,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:218,0 +DA:221,0 +DA:222,0 +DA:223,0 +DA:224,0 +DA:226,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:233,0 +DA:237,0 +DA:242,1 +DA:243,1 +DA:245,1 +DA:248,0 +DA:252,0 +DA:256,0 +DA:260,0 +DA:264,0 +DA:266,0 +DA:268,0 +DA:272,1 +DA:273,1 +DA:275,1 +DA:277,1 +DA:280,0 +DA:284,0 +DA:288,0 +DA:292,0 +DA:297,0 +DA:299,0 +DA:301,0 +DA:303,0 +DA:304,0 +DA:306,0 +DA:307,0 +DA:311,0 +DA:312,0 +DA:315,0 +LF:110 +LH:17 +BRDA:10,0,0,1 +BRDA:10,0,1,1 +BRDA:27,1,0,0 +BRDA:27,1,1,0 +BRDA:28,2,0,0 +BRDA:28,2,1,0 +BRDA:68,3,0,0 +BRDA:68,3,1,0 +BRDA:90,4,0,0 +BRDA:90,4,1,0 +BRDA:94,5,0,0 +BRDA:94,5,1,0 +BRDA:104,6,0,0 +BRDA:104,6,1,0 +BRDA:117,7,0,0 +BRDA:117,7,1,0 +BRDA:117,8,0,0 +BRDA:117,8,1,0 +BRDA:124,9,0,0 +BRDA:124,9,1,0 +BRDA:124,10,0,0 +BRDA:124,10,1,0 +BRDA:129,11,0,0 +BRDA:129,11,1,0 +BRDA:138,12,0,0 +BRDA:138,12,1,0 +BRDA:167,13,0,0 +BRDA:167,13,1,0 +BRDA:187,14,0,0 +BRDA:187,14,1,0 +BRDA:194,15,0,0 +BRDA:194,15,1,0 +BRDA:209,16,0,0 +BRDA:209,16,1,0 +BRDA:216,17,0,0 +BRDA:216,17,1,0 +BRDA:221,18,0,0 +BRDA:221,18,1,0 +BRDA:224,19,0,0 +BRDA:224,19,1,0 +BRDA:231,20,0,0 +BRDA:231,20,1,0 +BRDA:256,21,0,0 +BRDA:256,21,1,0 +BRDA:256,22,0,0 +BRDA:256,22,1,0 +BRDA:260,23,0,0 +BRDA:260,23,1,0 +BRDA:260,24,0,0 +BRDA:260,24,1,0 +BRDA:264,25,0,0 +BRDA:264,25,1,0 +BRDA:288,26,0,0 +BRDA:288,26,1,0 +BRDA:288,27,0,0 +BRDA:288,27,1,0 +BRDA:292,28,0,0 +BRDA:292,28,1,0 +BRDA:292,29,0,0 +BRDA:292,29,1,0 +BRDA:297,30,0,0 +BRDA:297,30,1,0 +BRDA:297,31,0,0 +BRDA:297,31,1,0 +BRDA:301,32,0,0 +BRDA:301,32,1,0 +BRDA:311,33,0,0 +BRDA:311,33,1,0 +BRF:68 +BRH:2 +end_of_record +TN: +SF:lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts +FN:9,(anonymous_0) +FN:12,(anonymous_1) +FN:16,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:9,1 +DA:10,0 +DA:13,0 +DA:17,0 +LF:4 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/DatabricksOAuth/OAuthScope.ts +FN:1,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:9,1 +DA:11,1 +LF:6 +LH:6 +BRDA:1,0,0,1 +BRDA:1,0,1,1 +BRF:2 +BRH:2 +end_of_record +TN: +SF:lib/connection/auth/DatabricksOAuth/OAuthToken.ts +FN:12,(anonymous_0) +FN:18,(anonymous_1) +FN:22,(anonymous_2) +FN:26,(anonymous_3) +FN:30,(anonymous_4) +FN:43,(anonymous_5) +FNF:6 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +DA:3,1 +DA:13,0 +DA:14,0 +DA:15,0 +DA:19,0 +DA:23,0 +DA:27,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:40,0 +DA:44,0 +DA:45,0 +LF:14 +LH:1 +BRDA:35,0,0,0 +BRDA:35,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/DatabricksOAuth/index.ts +FN:8,(anonymous_6) +FN:25,(anonymous_7) +FN:30,(anonymous_8) +FN:49,(anonymous_9) +FNF:4 +FNH:0 +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +DA:3,1 +DA:4,1 +DA:5,1 +DA:8,1 +DA:16,1 +DA:23,0 +DA:26,0 +DA:27,0 +DA:31,0 +DA:33,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:40,0 +DA:41,0 +DA:43,0 +DA:50,0 +DA:51,0 +DA:53,0 +LF:19 +LH:5 +BRDA:33,0,0,0 +BRDA:33,0,1,0 +BRDA:33,1,0,0 +BRDA:33,1,1,0 +BRDA:36,2,0,0 +BRDA:36,2,1,0 +BRDA:37,3,0,0 +BRDA:37,3,1,0 +BRDA:37,4,0,0 +BRDA:37,4,1,0 +BRDA:50,5,0,0 +BRDA:50,5,1,0 +BRF:12 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/CachedTokenProvider.ts +FN:29,(anonymous_0) +FN:39,(anonymous_1) +FN:61,(anonymous_2) +FN:68,(anonymous_3) +FN:77,(anonymous_4) +FN:93,(anonymous_5) +FNF:6 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +DA:8,1 +DA:14,1 +DA:19,0 +DA:21,0 +DA:35,0 +DA:36,0 +DA:41,0 +DA:42,0 +DA:46,0 +DA:47,0 +DA:51,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:57,0 +DA:62,0 +DA:69,0 +DA:79,0 +DA:80,0 +DA:83,0 +DA:84,0 +DA:85,0 +DA:87,0 +DA:94,0 +DA:95,0 +DA:96,0 +LF:26 +LH:2 +BRDA:36,0,0,0 +BRDA:36,0,1,0 +BRDA:36,1,0,0 +BRDA:36,1,1,0 +BRDA:36,2,0,0 +BRDA:36,2,1,0 +BRDA:36,3,0,0 +BRDA:36,3,1,0 +BRDA:41,4,0,0 +BRDA:41,4,1,0 +BRDA:41,5,0,0 +BRDA:41,5,1,0 +BRDA:46,6,0,0 +BRDA:46,6,1,0 +BRDA:79,7,0,0 +BRDA:79,7,1,0 +BRF:16 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/ExternalTokenProvider.ts +FN:27,(anonymous_1) +FN:39,(anonymous_2) +FN:49,(anonymous_3) +FNF:3 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:2,1 +DA:13,1 +DA:34,0 +DA:35,0 +DA:36,0 +DA:40,0 +DA:42,0 +DA:43,0 +DA:46,0 +DA:50,0 +LF:10 +LH:2 +BRDA:35,0,0,0 +BRDA:35,0,1,0 +BRDA:35,1,0,0 +BRDA:35,1,1,0 +BRDA:35,2,0,0 +BRDA:35,2,1,0 +BRDA:35,3,0,0 +BRDA:35,3,1,0 +BRDA:36,4,0,0 +BRDA:36,4,1,0 +BRDA:36,5,0,0 +BRDA:36,5,1,0 +BRDA:36,6,0,0 +BRDA:36,6,1,0 +BRDA:36,7,0,0 +BRDA:36,7,1,0 +BRDA:42,8,0,0 +BRDA:42,8,1,0 +BRF:18 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/FederationProvider.ts +FN:52,(anonymous_1) +FN:81,(anonymous_2) +FN:95,(anonymous_3) +FN:115,(anonymous_4) +FN:124,(anonymous_5) +FN:146,(anonymous_6) +FN:154,(anonymous_7) +FN:157,(anonymous_8) +FN:206,(anonymous_9) +FN:229,(anonymous_10) +FN:240,(anonymous_11) +FN:253,(anonymous_12) +FNF:12 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +DA:1,1 +DA:3,1 +DA:4,1 +DA:9,1 +DA:14,1 +DA:19,1 +DA:24,1 +DA:29,1 +DA:34,1 +DA:39,1 +DA:44,1 +DA:53,0 +DA:54,0 +DA:55,0 +DA:64,1 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:96,0 +DA:99,0 +DA:100,0 +DA:104,0 +DA:105,0 +DA:107,0 +DA:109,0 +DA:111,0 +DA:116,0 +DA:125,0 +DA:128,0 +DA:129,0 +DA:133,0 +DA:134,0 +DA:137,0 +DA:147,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:159,0 +DA:160,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:175,0 +DA:178,0 +DA:184,0 +DA:185,0 +DA:190,0 +DA:191,0 +DA:194,0 +DA:199,0 +DA:207,0 +DA:214,0 +DA:215,0 +DA:218,0 +DA:219,0 +DA:221,0 +DA:223,0 +DA:224,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:233,0 +DA:241,0 +DA:242,0 +DA:244,0 +DA:245,0 +DA:247,0 +DA:254,0 +DA:257,0 +DA:258,0 +DA:262,0 +DA:263,0 +DA:266,0 +LF:74 +LH:12 +BRDA:91,0,0,0 +BRDA:91,0,1,0 +BRDA:91,1,0,0 +BRDA:91,1,1,0 +BRDA:92,2,0,0 +BRDA:92,2,1,0 +BRDA:92,3,0,0 +BRDA:92,3,1,0 +BRDA:92,4,0,0 +BRDA:92,4,1,0 +BRDA:92,5,0,0 +BRDA:92,5,1,0 +BRDA:99,6,0,0 +BRDA:99,6,1,0 +BRDA:107,7,0,0 +BRDA:107,7,1,0 +BRDA:128,8,0,0 +BRDA:128,8,1,0 +BRDA:133,9,0,0 +BRDA:133,9,1,0 +BRDA:169,10,0,0 +BRDA:169,10,1,0 +BRDA:184,11,0,0 +BRDA:184,11,1,0 +BRDA:190,12,0,0 +BRDA:190,12,1,0 +BRDA:195,13,0,0 +BRDA:195,13,1,0 +BRDA:195,14,0,0 +BRDA:195,14,1,0 +BRDA:214,15,0,0 +BRDA:214,15,1,0 +BRDA:221,16,0,0 +BRDA:221,16,1,0 +BRDA:223,17,0,0 +BRDA:223,17,1,0 +BRDA:241,18,0,0 +BRDA:241,18,1,0 +BRDA:244,19,0,0 +BRDA:244,19,1,0 +BRDA:245,20,0,0 +BRDA:245,20,1,0 +BRDA:257,21,0,0 +BRDA:257,21,1,0 +BRDA:262,22,0,0 +BRDA:262,22,1,0 +BRF:46 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/StaticTokenProvider.ts +FN:16,(anonymous_1) +FN:26,(anonymous_2) +FN:36,(anonymous_3) +FN:40,(anonymous_4) +FNF:4 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +DA:2,1 +DA:8,1 +DA:17,0 +DA:27,0 +DA:28,0 +DA:37,0 +DA:41,0 +LF:7 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/Token.ts +FN:43,(anonymous_0) +FN:54,(anonymous_1) +FN:61,(anonymous_2) +FN:68,(anonymous_3) +FN:75,(anonymous_4) +FN:82,(anonymous_5) +FN:90,(anonymous_6) +FN:104,(anonymous_7) +FN:120,(anonymous_8) +FN:148,(anonymous_9) +FNF:10 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +DA:7,1 +DA:32,1 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:55,0 +DA:62,0 +DA:69,0 +DA:76,0 +DA:83,0 +DA:91,0 +DA:92,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:105,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:137,0 +DA:149,0 +LF:27 +LH:2 +BRDA:45,0,0,0 +BRDA:45,0,1,0 +BRDA:45,1,0,0 +BRDA:45,1,1,0 +BRDA:45,2,0,0 +BRDA:45,2,1,0 +BRDA:45,3,0,0 +BRDA:45,3,1,0 +BRDA:46,4,0,0 +BRDA:46,4,1,0 +BRDA:46,5,0,0 +BRDA:46,5,1,0 +BRDA:47,6,0,0 +BRDA:47,6,1,0 +BRDA:47,7,0,0 +BRDA:47,7,1,0 +BRDA:48,8,0,0 +BRDA:48,8,1,0 +BRDA:48,9,0,0 +BRDA:48,9,1,0 +BRDA:91,10,0,0 +BRDA:91,10,1,0 +BRDA:125,11,0,0 +BRDA:125,11,1,0 +BRDA:128,12,0,0 +BRDA:128,12,1,0 +BRDA:138,13,0,0 +BRDA:138,13,1,0 +BRDA:138,14,0,0 +BRDA:138,14,1,0 +BRDA:140,15,0,0 +BRDA:140,15,1,0 +BRDA:140,16,0,0 +BRDA:140,16,1,0 +BRDA:141,17,0,0 +BRDA:141,17,1,0 +BRDA:141,18,0,0 +BRDA:141,18,1,0 +BRDA:152,19,0,0 +BRDA:152,19,1,0 +BRDA:152,20,0,0 +BRDA:152,20,1,0 +BRF:42 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts +FN:24,(anonymous_0) +FN:30,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:5,1 +DA:11,1 +DA:25,0 +DA:26,0 +DA:27,0 +DA:31,0 +DA:32,0 +DA:34,0 +DA:36,0 +DA:38,0 +DA:39,0 +DA:44,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:53,0 +LF:17 +LH:2 +BRDA:27,0,0,0 +BRDA:27,0,1,0 +BRDA:27,1,0,0 +BRDA:27,1,1,0 +BRDA:38,2,0,0 +BRDA:38,2,1,0 +BRDA:46,3,0,0 +BRDA:46,3,1,0 +BRF:8 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/index.ts +FN:2,(anonymous_1) +FN:3,(anonymous_2) +FN:4,(anonymous_3) +FN:5,(anonymous_4) +FN:6,(anonymous_5) +FN:7,(anonymous_6) +FN:8,(anonymous_7) +FN:8,(anonymous_8) +FN:8,(anonymous_9) +FNF:9 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +LF:7 +LH:7 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/connection/auth/tokenProvider/utils.ts +FN:8,decodeJWT +FN:27,getJWTIssuer +FN:42,extractHostname +FN:67,isSameHost +FNF:4 +FNH:0 +FNDA:0,decodeJWT +FNDA:0,getJWTIssuer +FNDA:0,extractHostname +FNDA:0,isSameHost +DA:8,1 +DA:9,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:14,0 +DA:15,0 +DA:17,0 +DA:27,1 +DA:28,0 +DA:29,0 +DA:30,0 +DA:32,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:56,0 +DA:67,1 +DA:68,0 +DA:69,0 +DA:70,0 +DA:72,0 +DA:73,0 +DA:75,0 +DA:77,0 +LF:28 +LH:3 +BRDA:11,0,0,0 +BRDA:11,0,1,0 +BRDA:29,1,0,0 +BRDA:29,1,1,0 +BRDA:29,2,0,0 +BRDA:29,2,1,0 +BRDA:44,3,0,0 +BRDA:44,3,1,0 +BRDA:51,4,0,0 +BRDA:51,4,1,0 +BRDA:72,5,0,0 +BRDA:72,5,1,0 +BRDA:72,6,0,0 +BRDA:72,6,1,0 +BRF:14 +BRH:0 +end_of_record +TN: +SF:lib/connection/connections/HttpConnection.ts +FN:26,(anonymous_1) +FN:31,(anonymous_2) +FN:39,(anonymous_3) +FN:51,(anonymous_4) +FN:62,(anonymous_5) +FN:67,(anonymous_6) +FN:79,(anonymous_7) +FN:87,(anonymous_8) +FN:93,(anonymous_9) +FN:106,(anonymous_10) +FN:122,(anonymous_11) +FNF:11 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +DA:1,1 +DA:2,1 +DA:3,1 +DA:5,1 +DA:11,1 +DA:13,1 +DA:15,1 +DA:20,0 +DA:27,0 +DA:28,0 +DA:32,0 +DA:33,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:44,0 +DA:48,0 +DA:52,0 +DA:54,0 +DA:63,0 +DA:64,0 +DA:68,0 +DA:76,0 +DA:80,0 +DA:83,0 +DA:85,0 +DA:87,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:99,0 +DA:106,0 +DA:119,0 +DA:123,0 +LF:35 +LH:7 +BRDA:33,0,0,0 +BRDA:33,0,1,0 +BRDA:33,1,0,0 +BRDA:33,1,1,0 +BRDA:40,2,0,0 +BRDA:40,2,1,0 +BRDA:41,3,0,0 +BRDA:41,3,1,0 +BRDA:44,4,0,0 +BRDA:44,4,1,0 +BRDA:58,5,0,0 +BRDA:58,5,1,0 +BRDA:58,6,0,0 +BRDA:58,6,1,0 +BRDA:80,7,0,0 +BRDA:80,7,1,0 +BRDA:80,8,0,0 +BRDA:80,8,1,0 +BRDA:80,9,0,0 +BRDA:80,9,1,0 +BRDA:81,10,0,0 +BRDA:81,10,1,0 +BRDA:81,11,0,0 +BRDA:81,11,1,0 +BRDA:81,12,0,0 +BRDA:81,12,1,0 +BRDA:81,13,0,0 +BRDA:81,13,1,0 +BRDA:94,14,0,0 +BRDA:94,14,1,0 +BRDA:101,15,0,0 +BRDA:101,15,1,0 +BRDA:102,16,0,0 +BRDA:102,16,1,0 +BRDA:102,17,0,0 +BRDA:102,17,1,0 +BRDA:110,18,0,0 +BRDA:110,18,1,0 +BRDA:110,19,0,0 +BRDA:110,19,1,0 +BRF:40 +BRH:0 +end_of_record +TN: +SF:lib/connection/connections/HttpRetryPolicy.ts +FN:6,delay +FN:7,(anonymous_7) +FN:8,(anonymous_8) +FN:19,(anonymous_9) +FN:25,(anonymous_10) +FN:57,(anonymous_11) +FN:68,(anonymous_12) +FN:82,(anonymous_13) +FN:98,(anonymous_14) +FNF:9 +FNH:0 +FNDA:0,delay +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +DA:4,1 +DA:7,0 +DA:8,0 +DA:12,1 +DA:20,0 +DA:21,0 +DA:22,0 +DA:26,0 +DA:27,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:44,0 +DA:45,0 +DA:51,0 +DA:54,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:69,0 +DA:73,0 +DA:79,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:95,0 +DA:99,0 +DA:100,0 +LF:37 +LH:2 +BRDA:26,0,0,0 +BRDA:26,0,1,0 +BRDA:31,1,0,0 +BRDA:31,1,1,0 +BRDA:39,2,0,0 +BRDA:39,2,1,0 +BRDA:47,3,0,0 +BRDA:47,3,1,0 +BRDA:47,4,0,0 +BRDA:47,4,1,0 +BRDA:61,5,0,0 +BRDA:61,5,1,0 +BRDA:73,6,0,0 +BRDA:73,6,1,0 +BRDA:73,6,2,0 +BRDA:73,6,3,0 +BRDA:88,7,0,0 +BRDA:88,7,1,0 +BRDA:89,8,0,0 +BRDA:89,8,1,0 +BRDA:91,9,0,0 +BRDA:91,9,1,0 +BRDA:91,10,0,0 +BRDA:91,10,1,0 +BRF:24 +BRH:0 +end_of_record +TN: +SF:lib/connection/connections/NullRetryPolicy.ts +FN:5,(anonymous_0) +FN:9,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:3,1 +DA:6,0 +DA:11,0 +LF:3 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/connection/connections/ThriftHttpConnection.ts +FN:21,(anonymous_7) +FN:86,(anonymous_8) +FN:95,(anonymous_9) +FN:104,(anonymous_10) +FN:111,(anonymous_11) +FN:125,(anonymous_12) +FN:126,(anonymous_13) +FN:127,(anonymous_14) +FN:129,(anonymous_15) +FN:133,(anonymous_16) +FN:140,(anonymous_17) +FN:141,(anonymous_18) +FN:143,(anonymous_19) +FN:153,(anonymous_20) +FN:167,(anonymous_21) +FN:168,(anonymous_22) +FN:170,(anonymous_23) +FN:184,(anonymous_24) +FN:198,(anonymous_25) +FN:203,(anonymous_26) +FNF:20 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +DA:7,1 +DA:8,1 +DA:9,1 +DA:11,1 +DA:14,1 +DA:16,1 +DA:22,0 +DA:26,0 +DA:27,0 +DA:52,1 +DA:70,1 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:97,0 +DA:98,0 +DA:101,0 +DA:105,0 +DA:112,0 +DA:124,0 +DA:125,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:131,0 +DA:134,0 +DA:135,0 +DA:138,0 +DA:141,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:153,0 +DA:154,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:162,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:171,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:177,0 +DA:179,0 +DA:185,0 +DA:186,0 +DA:189,0 +DA:190,0 +DA:191,0 +DA:193,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:209,0 +DA:210,0 +DA:212,0 +DA:213,0 +DA:220,0 +DA:221,0 +DA:223,0 +LF:74 +LH:8 +BRDA:86,0,0,0 +BRDA:91,1,0,0 +BRDA:91,1,1,0 +BRDA:91,2,0,0 +BRDA:91,2,1,0 +BRDA:92,3,0,0 +BRDA:92,3,1,0 +BRDA:92,4,0,0 +BRDA:92,4,1,0 +BRDA:97,5,0,0 +BRDA:97,5,1,0 +BRDA:97,6,0,0 +BRDA:97,6,1,0 +BRDA:134,7,0,0 +BRDA:134,7,1,0 +BRDA:144,8,0,0 +BRDA:144,8,1,0 +BRDA:145,9,0,0 +BRDA:145,9,1,0 +BRDA:157,10,0,0 +BRDA:157,10,1,0 +BRDA:158,11,0,0 +BRDA:158,11,1,0 +BRDA:158,12,0,0 +BRDA:158,12,1,0 +BRDA:185,13,0,0 +BRDA:185,13,1,0 +BRDA:202,14,0,0 +BRDA:202,14,1,0 +BRDA:209,15,0,0 +BRDA:209,15,1,0 +BRDA:220,16,0,0 +BRDA:220,16,1,0 +BRF:33 +BRH:0 +end_of_record +TN: +SF:lib/contracts/IDBSQLLogger.ts +FN:10,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:14,1 +LF:5 +LH:5 +BRDA:10,0,0,1 +BRDA:10,0,1,1 +BRF:2 +BRH:2 +end_of_record +TN: +SF:lib/dto/InfoValue.ts +FN:9,(anonymous_0) +FN:13,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:6,1 +DA:10,0 +DA:14,0 +DA:16,0 +DA:17,0 +DA:19,0 +DA:20,0 +DA:22,0 +DA:23,0 +DA:25,0 +DA:26,0 +DA:28,0 +DA:29,0 +DA:31,0 +LF:14 +LH:1 +BRDA:16,0,0,0 +BRDA:16,0,1,0 +BRDA:19,1,0,0 +BRDA:19,1,1,0 +BRDA:22,2,0,0 +BRDA:22,2,1,0 +BRDA:25,3,0,0 +BRDA:25,3,1,0 +BRDA:28,4,0,0 +BRDA:28,4,1,0 +BRF:10 +BRH:0 +end_of_record +TN: +SF:lib/dto/Status.ts +FN:7,(anonymous_1) +FN:11,(anonymous_2) +FN:16,(anonymous_3) +FN:21,(anonymous_4) +FN:26,(anonymous_5) +FN:30,(anonymous_6) +FN:37,(anonymous_7) +FNF:7 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +DA:1,1 +DA:2,1 +DA:4,1 +DA:8,0 +DA:12,0 +DA:13,0 +DA:17,0 +DA:18,0 +DA:22,0 +DA:23,0 +DA:27,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:38,0 +LF:15 +LH:3 +BRDA:13,0,0,0 +BRDA:13,0,1,0 +BRDA:23,1,0,0 +BRDA:23,1,1,0 +BRDA:27,2,0,0 +BRDA:27,2,1,0 +BRDA:32,3,0,0 +BRDA:32,3,1,0 +BRDA:37,4,0,0 +BRDA:39,5,0,0 +BRDA:39,5,1,0 +BRF:11 +BRH:0 +end_of_record +TN: +SF:lib/errors/AuthenticationError.ts +FNF:0 +FNH:0 +DA:1,1 +DA:3,1 +LF:2 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/errors/HiveDriverError.ts +FNF:0 +FNH:0 +DA:1,1 +LF:1 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/errors/OperationStateError.ts +FN:4,(anonymous_1) +FN:25,(anonymous_2) +FNF:2 +FNH:1 +FNDA:1,(anonymous_1) +FNDA:0,(anonymous_2) +DA:1,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:12,1 +DA:20,1 +DA:26,0 +DA:28,0 +DA:29,0 +LF:12 +LH:9 +BRDA:4,0,0,1 +BRDA:4,0,1,1 +BRDA:26,1,0,0 +BRDA:26,1,1,0 +BRDA:26,2,0,0 +BRDA:26,2,1,0 +BRDA:26,3,0,0 +BRDA:26,3,1,0 +BRDA:26,4,0,0 +BRDA:26,4,1,0 +BRF:10 +BRH:2 +end_of_record +TN: +SF:lib/errors/ParameterError.ts +FNF:0 +FNH:0 +DA:1,1 +LF:1 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/errors/RetryError.ts +FN:1,(anonymous_0) +FN:16,(anonymous_1) +FNF:2 +FNH:1 +FNDA:1,(anonymous_0) +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:3,1 +DA:6,1 +DA:11,1 +DA:17,0 +DA:18,0 +DA:19,0 +LF:8 +LH:5 +BRDA:1,0,0,1 +BRDA:1,0,1,1 +BRF:2 +BRH:2 +end_of_record +TN: +SF:lib/errors/StagingError.ts +FNF:0 +FNH:0 +DA:1,1 +LF:1 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/errors/StatusError.ts +FN:12,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:3,1 +DA:13,0 +DA:14,0 +DA:15,0 +DA:17,0 +DA:18,0 +LF:6 +LH:1 +BRDA:14,0,0,0 +BRDA:14,0,1,0 +BRDA:15,1,0,0 +BRDA:15,1,1,0 +BRDA:17,2,0,0 +BRDA:17,2,1,0 +BRF:6 +BRH:0 +end_of_record +TN: +SF:lib/hive/HiveDriver.ts +FN:55,(anonymous_1) +FN:59,(anonymous_2) +FN:65,(anonymous_3) +FN:71,(anonymous_4) +FN:77,(anonymous_5) +FN:83,(anonymous_6) +FN:89,(anonymous_7) +FN:95,(anonymous_8) +FN:101,(anonymous_9) +FN:107,(anonymous_10) +FN:113,(anonymous_11) +FN:119,(anonymous_12) +FN:125,(anonymous_13) +FN:131,(anonymous_14) +FN:137,(anonymous_15) +FN:143,(anonymous_16) +FN:149,(anonymous_17) +FN:155,(anonymous_18) +FN:161,(anonymous_19) +FN:167,(anonymous_20) +FN:173,(anonymous_21) +FN:179,(anonymous_22) +FNF:22 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:52,1 +DA:56,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:102,0 +DA:103,0 +DA:104,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:138,0 +DA:139,0 +DA:140,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:150,0 +DA:151,0 +DA:152,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:174,0 +DA:175,0 +DA:176,0 +DA:180,0 +DA:181,0 +DA:182,0 +LF:86 +LH:22 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/BaseCommand.ts +FN:11,(anonymous_7) +FN:16,(anonymous_8) +FN:49,(anonymous_9) +FN:56,(anonymous_10) +FN:58,(anonymous_11) +FNF:5 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +DA:1,1 +DA:2,1 +DA:3,1 +DA:6,1 +DA:12,0 +DA:13,0 +DA:17,0 +DA:18,0 +DA:20,0 +DA:22,0 +DA:28,0 +DA:31,0 +DA:33,0 +DA:37,0 +DA:45,0 +DA:50,0 +DA:51,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:62,0 +DA:66,0 +LF:24 +LH:4 +BRDA:20,0,0,0 +BRDA:20,0,1,0 +BRDA:22,1,0,0 +BRDA:22,1,1,0 +BRDA:23,2,0,0 +BRDA:23,2,1,0 +BRDA:23,2,2,0 +BRDA:23,2,3,0 +BRDA:31,3,0,0 +BRDA:31,3,1,0 +BRDA:34,4,0,0 +BRDA:34,4,1,0 +BRDA:34,5,0,0 +BRDA:34,5,1,0 +BRDA:38,6,0,0 +BRDA:38,6,1,0 +BRDA:38,7,0,0 +BRDA:38,7,1,0 +BRDA:50,8,0,0 +BRDA:50,8,1,0 +BRDA:59,9,0,0 +BRDA:59,9,1,0 +BRF:22 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/CancelDelegationTokenCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/CancelOperationCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/CloseOperationCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/CloseSessionCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/ExecuteStatementCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/FetchResultsCommand.ts +FN:11,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:10,1 +DA:12,0 +DA:14,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetCatalogsCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetColumnsCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetCrossReferenceCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetDelegationTokenCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetFunctionsCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetInfoCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetOperationStatusCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetPrimaryKeysCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetResultSetMetadataCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetSchemasCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetTableTypesCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetTablesCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/GetTypeInfoCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/OpenSessionCommand.ts +FN:17,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:16,1 +DA:18,0 +DA:20,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/hive/Commands/RenewDelegationTokenCommand.ts +FN:8,(anonymous_1) +FNF:1 +FNH:0 +FNDA:0,(anonymous_1) +DA:1,1 +DA:2,1 +DA:7,1 +DA:9,0 +DA:11,0 +LF:5 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/result/ArrowResultConverter.ts +FN:45,(anonymous_0) +FN:51,(anonymous_1) +FN:61,(anonymous_2) +FN:105,(anonymous_3) +FN:142,(anonymous_4) +FN:143,(anonymous_5) +FN:151,(anonymous_6) +FN:173,(anonymous_7) +FN:185,(anonymous_8) +FN:211,(anonymous_9) +FN:214,(anonymous_10) +FNF:11 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +DA:1,1 +DA:2,1 +DA:19,1 +DA:21,1 +DA:26,1 +DA:36,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:52,0 +DA:53,0 +DA:55,0 +DA:56,0 +DA:58,0 +DA:62,0 +DA:63,0 +DA:74,0 +DA:76,0 +DA:78,0 +DA:79,0 +DA:81,0 +DA:82,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:93,0 +DA:95,0 +DA:98,0 +DA:108,0 +DA:111,0 +DA:112,0 +DA:113,0 +DA:114,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:129,0 +DA:130,0 +DA:131,0 +DA:135,0 +DA:136,0 +DA:143,0 +DA:145,0 +DA:147,0 +DA:152,0 +DA:153,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:168,0 +DA:170,0 +DA:171,0 +DA:173,0 +DA:174,0 +DA:175,0 +DA:177,0 +DA:181,0 +DA:182,0 +DA:184,0 +DA:185,0 +DA:188,0 +DA:189,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:199,0 +DA:203,0 +DA:204,0 +DA:208,0 +DA:212,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:221,0 +LF:84 +LH:5 +BRDA:52,0,0,0 +BRDA:52,0,1,0 +BRDA:55,1,0,0 +BRDA:55,1,1,0 +BRDA:62,2,0,0 +BRDA:62,2,1,0 +BRDA:76,3,0,0 +BRDA:76,3,1,0 +BRDA:88,4,0,0 +BRDA:88,4,1,0 +BRDA:111,5,0,0 +BRDA:111,5,1,0 +BRDA:113,6,0,0 +BRDA:113,6,1,0 +BRDA:118,7,0,0 +BRDA:118,7,1,0 +BRDA:118,8,0,0 +BRDA:118,8,1,0 +BRDA:129,9,0,0 +BRDA:129,9,1,0 +BRDA:129,10,0,0 +BRDA:129,10,1,0 +BRDA:129,11,0,0 +BRDA:129,11,1,0 +BRDA:129,12,0,0 +BRDA:129,12,1,0 +BRDA:130,13,0,0 +BRDA:130,13,1,0 +BRDA:130,14,0,0 +BRDA:130,14,1,0 +BRDA:135,15,0,0 +BRDA:135,15,1,0 +BRDA:151,16,0,0 +BRDA:152,17,0,0 +BRDA:152,17,1,0 +BRDA:162,18,0,0 +BRDA:162,18,1,0 +BRDA:166,19,0,0 +BRDA:166,19,1,0 +BRDA:166,20,0,0 +BRDA:166,20,1,0 +BRDA:166,21,0,0 +BRDA:166,21,1,0 +BRDA:166,22,0,0 +BRDA:166,22,1,0 +BRDA:166,23,0,0 +BRDA:166,23,1,0 +BRDA:170,24,0,0 +BRDA:170,24,1,0 +BRDA:173,25,0,0 +BRDA:173,25,1,0 +BRDA:173,26,0,0 +BRDA:173,26,1,0 +BRDA:175,27,0,0 +BRDA:175,27,1,0 +BRDA:175,28,0,0 +BRDA:175,28,1,0 +BRDA:175,29,0,0 +BRDA:175,29,1,0 +BRDA:175,30,0,0 +BRDA:175,30,1,0 +BRDA:175,31,0,0 +BRDA:175,31,1,0 +BRDA:181,32,0,0 +BRDA:181,32,1,0 +BRDA:185,33,0,0 +BRDA:185,33,1,0 +BRDA:185,34,0,0 +BRDA:185,34,1,0 +BRDA:185,35,0,0 +BRDA:185,35,1,0 +BRDA:185,36,0,0 +BRDA:185,36,1,0 +BRDA:185,37,0,0 +BRDA:185,37,1,0 +BRDA:188,38,0,0 +BRDA:188,38,1,0 +BRDA:194,39,0,0 +BRDA:194,39,1,0 +BRDA:194,40,0,0 +BRDA:194,40,1,0 +BRDA:196,41,0,0 +BRDA:196,41,1,0 +BRDA:203,42,0,0 +BRDA:203,42,1,0 +BRDA:208,43,0,0 +BRDA:208,43,1,0 +BRDA:215,44,0,0 +BRDA:215,44,1,0 +BRDA:215,45,0,0 +BRDA:215,45,1,0 +BRDA:218,46,0,0 +BRDA:218,46,1,0 +BRF:93 +BRH:0 +end_of_record +TN: +SF:lib/result/ArrowResultHandler.ts +FN:17,(anonymous_1) +FN:34,(anonymous_2) +FN:41,(anonymous_3) +FN:53,(anonymous_4) +FNF:4 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +DA:2,1 +DA:5,1 +DA:6,1 +DA:8,1 +DA:22,0 +DA:23,0 +DA:26,0 +DA:27,0 +DA:29,0 +DA:30,0 +DA:35,0 +DA:36,0 +DA:38,0 +DA:42,0 +DA:43,0 +DA:49,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:60,0 +DA:61,0 +DA:67,0 +LF:25 +LH:4 +BRDA:26,0,0,0 +BRDA:26,0,1,0 +BRDA:26,1,0,0 +BRDA:26,1,1,0 +BRDA:27,2,0,0 +BRDA:27,2,1,0 +BRDA:27,3,0,0 +BRDA:27,3,1,0 +BRDA:29,4,0,0 +BRDA:29,4,1,0 +BRDA:29,5,0,0 +BRDA:29,5,1,0 +BRDA:35,6,0,0 +BRDA:35,6,1,0 +BRDA:42,7,0,0 +BRDA:42,7,1,0 +BRDA:53,8,0,0 +BRDA:53,8,1,0 +BRDA:53,9,0,0 +BRDA:53,9,1,0 +BRDA:53,10,0,0 +BRDA:53,10,1,0 +BRDA:53,11,0,0 +BRDA:53,11,1,0 +BRDA:54,12,0,0 +BRDA:54,12,1,0 +BRDA:55,13,0,0 +BRDA:55,13,1,0 +BRDA:60,14,0,0 +BRDA:60,14,1,0 +BRF:30 +BRH:0 +end_of_record +TN: +SF:lib/result/CloudFetchResultHandler.ts +FN:21,(anonymous_7) +FN:35,(anonymous_8) +FN:42,(anonymous_9) +FN:45,(anonymous_10) +FN:54,(anonymous_11) +FN:67,(anonymous_12) +FN:72,(anonymous_13) +FN:93,(anonymous_14) +FN:115,(anonymous_15) +FN:121,(anonymous_16) +FN:123,(anonymous_17) +FNF:11 +FNH:0 +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +DA:1,1 +DA:3,1 +DA:7,1 +DA:8,1 +DA:10,1 +DA:17,0 +DA:19,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:30,0 +DA:31,0 +DA:36,0 +DA:37,0 +DA:39,0 +DA:43,0 +DA:45,0 +DA:46,0 +DA:49,0 +DA:50,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:66,0 +DA:67,0 +DA:69,0 +DA:73,0 +DA:74,0 +DA:76,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:94,0 +DA:95,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:104,0 +DA:105,0 +DA:107,0 +DA:109,0 +DA:116,0 +DA:117,0 +DA:118,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:125,0 +LF:54 +LH:5 +BRDA:28,0,0,0 +BRDA:28,0,1,0 +BRDA:28,1,0,0 +BRDA:28,1,1,0 +BRDA:30,2,0,0 +BRDA:30,2,1,0 +BRDA:30,3,0,0 +BRDA:30,3,1,0 +BRDA:36,4,0,0 +BRDA:36,4,1,0 +BRDA:36,5,0,0 +BRDA:36,5,1,0 +BRDA:45,6,0,0 +BRDA:45,6,1,0 +BRDA:45,7,0,0 +BRDA:45,7,1,0 +BRDA:45,8,0,0 +BRDA:45,8,1,0 +BRDA:45,9,0,0 +BRDA:45,9,1,0 +BRDA:52,10,0,0 +BRDA:52,10,1,0 +BRDA:59,11,0,0 +BRDA:59,11,1,0 +BRDA:66,12,0,0 +BRDA:66,12,1,0 +BRDA:81,13,0,0 +BRDA:81,13,1,0 +BRDA:94,14,0,0 +BRDA:94,14,1,0 +BRDA:100,15,0,0 +BRDA:100,15,1,0 +BRF:32 +BRH:0 +end_of_record +TN: +SF:lib/result/JsonResultHandler.ts +FN:13,(anonymous_0) +FN:23,(anonymous_1) +FN:27,(anonymous_2) +FN:41,(anonymous_3) +FN:43,(anonymous_4) +FN:44,(anonymous_5) +FN:59,(anonymous_6) +FN:67,(anonymous_7) +FN:75,(anonymous_8) +FNF:9 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +DA:4,1 +DA:6,1 +DA:18,0 +DA:19,0 +DA:20,0 +DA:24,0 +DA:28,0 +DA:29,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:37,0 +DA:38,0 +DA:42,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:49,0 +DA:51,0 +DA:53,0 +DA:60,0 +DA:61,0 +DA:63,0 +DA:64,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:71,0 +DA:76,0 +DA:77,0 +DA:79,0 +LF:31 +LH:2 +BRDA:28,0,0,0 +BRDA:28,0,1,0 +BRDA:33,1,0,0 +BRDA:33,1,1,0 +BRDA:37,2,0,0 +BRDA:37,2,1,0 +BRDA:45,3,0,0 +BRDA:45,3,1,0 +BRDA:60,4,0,0 +BRDA:60,4,1,0 +BRDA:60,5,0,0 +BRDA:60,5,1,0 +BRDA:63,6,0,0 +BRDA:63,6,1,0 +BRDA:68,7,0,0 +BRDA:68,7,1,0 +BRDA:68,8,0,0 +BRDA:68,8,1,0 +BRF:18 +BRH:0 +end_of_record +TN: +SF:lib/result/ResultSlicer.ts +FN:17,(anonymous_0) +FN:22,(anonymous_1) +FN:29,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:10,1 +DA:15,0 +DA:18,0 +DA:19,0 +DA:23,0 +DA:24,0 +DA:26,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:39,0 +DA:42,0 +DA:43,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:53,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:74,0 +LF:32 +LH:1 +BRDA:23,0,0,0 +BRDA:23,0,1,0 +BRDA:32,1,0,0 +BRDA:32,1,1,0 +BRDA:33,2,0,0 +BRDA:33,2,1,0 +BRDA:46,3,0,0 +BRDA:46,3,1,0 +BRDA:56,4,0,0 +BRDA:56,4,1,0 +BRDA:67,5,0,0 +BRDA:67,5,1,0 +BRDA:68,6,0,0 +BRDA:68,6,1,0 +BRDA:68,7,0,0 +BRDA:68,7,1,0 +BRF:16 +BRH:0 +end_of_record +TN: +SF:lib/result/RowSetProvider.ts +FN:8,(anonymous_1) +FN:13,checkIfOperationHasMoreRows +FN:37,(anonymous_3) +FN:46,(anonymous_4) +FN:54,(anonymous_5) +FN:62,(anonymous_6) +FN:69,(anonymous_7) +FN:96,(anonymous_8) +FNF:8 +FNH:1 +FNDA:1,(anonymous_1) +FNDA:0,checkIfOperationHasMoreRows +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +DA:1,1 +DA:2,1 +DA:3,1 +DA:6,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:14,0 +DA:15,0 +DA:18,0 +DA:20,0 +DA:21,0 +DA:24,1 +DA:29,0 +DA:31,0 +DA:35,0 +DA:43,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:56,0 +DA:59,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:66,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:76,0 +DA:77,0 +DA:81,0 +DA:82,0 +DA:85,0 +DA:86,0 +DA:93,0 +DA:99,0 +DA:100,0 +DA:103,0 +DA:104,0 +DA:107,0 +LF:42 +LH:8 +BRDA:8,0,0,1 +BRDA:8,0,1,1 +BRDA:14,1,0,0 +BRDA:14,1,1,0 +BRDA:18,2,0,0 +BRDA:18,2,1,0 +BRDA:18,3,0,0 +BRDA:18,3,1,0 +BRDA:18,4,0,0 +BRDA:18,4,1,0 +BRDA:21,5,0,0 +BRDA:21,5,1,0 +BRDA:21,6,0,0 +BRDA:21,6,1,0 +BRDA:21,7,0,0 +BRDA:21,7,1,0 +BRDA:21,8,0,0 +BRDA:21,8,1,0 +BRDA:21,9,0,0 +BRDA:21,9,1,0 +BRDA:21,10,0,0 +BRDA:21,10,1,0 +BRDA:43,11,0,0 +BRDA:43,11,1,0 +BRDA:43,12,0,0 +BRDA:43,12,1,0 +BRDA:55,13,0,0 +BRDA:55,13,1,0 +BRDA:71,14,0,0 +BRDA:71,14,1,0 +BRDA:76,15,0,0 +BRDA:76,15,1,0 +BRDA:81,16,0,0 +BRDA:81,16,1,0 +BRDA:99,17,0,0 +BRDA:99,17,1,0 +BRDA:103,18,0,0 +BRDA:103,18,1,0 +BRF:38 +BRH:2 +end_of_record +TN: +SF:lib/result/utils.ts +FN:27,getSchemaColumns +FN:32,(anonymous_2) +FN:35,isString +FN:39,convertJSON +FN:51,convertBigInt +FN:61,convertThriftValue +FN:126,hiveSchemaToArrowSchema +FN:133,(anonymous_8) +FN:149,getColumnValue +FNF:9 +FNH:0 +FNDA:0,getSchemaColumns +FNDA:0,(anonymous_2) +FNDA:0,isString +FNDA:0,convertJSON +FNDA:0,convertBigInt +FNDA:0,convertThriftValue +FNDA:0,hiveSchemaToArrowSchema +FNDA:0,(anonymous_8) +FNDA:0,getColumnValue +DA:1,1 +DA:2,1 +DA:19,1 +DA:20,1 +DA:27,1 +DA:28,0 +DA:29,0 +DA:32,0 +DA:36,0 +DA:40,0 +DA:41,0 +DA:44,0 +DA:45,0 +DA:47,0 +DA:52,0 +DA:53,0 +DA:55,0 +DA:56,0 +DA:58,0 +DA:61,1 +DA:62,0 +DA:63,0 +DA:66,0 +DA:69,0 +DA:72,0 +DA:74,0 +DA:77,0 +DA:79,0 +DA:81,0 +DA:96,0 +DA:101,1 +DA:126,1 +DA:127,0 +DA:128,0 +DA:131,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:139,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:149,1 +DA:150,0 +DA:151,0 +DA:153,0 +LF:50 +LH:9 +BRDA:28,0,0,0 +BRDA:28,0,1,0 +BRDA:36,1,0,0 +BRDA:36,1,1,0 +BRDA:40,2,0,0 +BRDA:40,2,1,0 +BRDA:52,3,0,0 +BRDA:52,3,1,0 +BRDA:55,4,0,0 +BRDA:55,4,1,0 +BRDA:62,5,0,0 +BRDA:62,5,1,0 +BRDA:66,6,0,0 +BRDA:66,6,1,0 +BRDA:66,6,2,0 +BRDA:66,6,3,0 +BRDA:66,6,4,0 +BRDA:66,6,5,0 +BRDA:66,6,6,0 +BRDA:66,6,7,0 +BRDA:66,6,8,0 +BRDA:66,6,9,0 +BRDA:66,6,10,0 +BRDA:66,6,11,0 +BRDA:66,6,12,0 +BRDA:66,6,13,0 +BRDA:66,6,14,0 +BRDA:66,6,15,0 +BRDA:66,6,16,0 +BRDA:66,6,17,0 +BRDA:66,6,18,0 +BRDA:66,6,19,0 +BRDA:66,6,20,0 +BRDA:66,6,21,0 +BRDA:66,6,22,0 +BRDA:127,7,0,0 +BRDA:127,7,1,0 +BRDA:134,8,0,0 +BRDA:134,8,1,0 +BRDA:134,9,0,0 +BRDA:134,9,1,0 +BRDA:134,10,0,0 +BRDA:134,10,1,0 +BRDA:134,11,0,0 +BRDA:134,11,1,0 +BRDA:135,12,0,0 +BRDA:135,12,1,0 +BRDA:136,13,0,0 +BRDA:136,13,1,0 +BRDA:137,14,0,0 +BRDA:137,14,1,0 +BRDA:150,15,0,0 +BRDA:150,15,1,0 +BRDA:154,16,0,0 +BRDA:154,16,1,0 +BRDA:154,17,0,0 +BRDA:154,17,1,0 +BRDA:154,18,0,0 +BRDA:154,18,1,0 +BRDA:154,19,0,0 +BRDA:154,19,1,0 +BRDA:154,20,0,0 +BRDA:154,20,1,0 +BRDA:154,21,0,0 +BRDA:154,21,1,0 +BRDA:154,22,0,0 +BRDA:154,22,1,0 +BRDA:154,23,0,0 +BRDA:154,23,1,0 +BRDA:154,24,0,0 +BRDA:154,24,1,0 +BRDA:154,25,0,0 +BRDA:154,25,1,0 +BRDA:154,26,0,0 +BRDA:154,26,1,0 +BRDA:154,27,0,0 +BRDA:154,27,1,0 +BRDA:154,28,0,0 +BRDA:154,28,1,0 +BRDA:154,29,0,0 +BRDA:154,29,1,0 +BRF:81 +BRH:0 +end_of_record +TN: +SF:lib/telemetry/CircuitBreaker.ts +FN:23,(anonymous_0) +FN:73,(anonymous_1) +FN:87,(anonymous_2) +FN:114,(anonymous_3) +FN:121,(anonymous_4) +FN:128,(anonymous_5) +FN:135,(anonymous_6) +FN:161,(anonymous_7) +FN:187,(anonymous_8) +FN:198,(anonymous_9) +FN:213,(anonymous_10) +FN:223,(anonymous_11) +FN:233,(anonymous_12) +FNF:13 +FNH:13 +FNDA:1,(anonymous_0) +FNDA:50,(anonymous_1) +FNDA:110,(anonymous_2) +FNDA:27,(anonymous_3) +FNDA:9,(anonymous_4) +FNDA:5,(anonymous_5) +FNDA:20,(anonymous_6) +FNDA:85,(anonymous_7) +FNDA:27,(anonymous_8) +FNDA:34,(anonymous_9) +FNDA:6,(anonymous_10) +FNDA:3,(anonymous_11) +FNDA:1,(anonymous_12) +DA:18,1 +DA:23,1 +DA:25,1 +DA:27,1 +DA:29,1 +DA:47,1 +DA:62,1 +DA:63,50 +DA:65,50 +DA:67,50 +DA:73,50 +DA:74,50 +DA:88,110 +DA:91,110 +DA:92,13 +DA:93,5 +DA:96,8 +DA:97,8 +DA:98,8 +DA:101,105 +DA:102,105 +DA:103,20 +DA:104,20 +DA:106,85 +DA:107,85 +DA:115,27 +DA:122,9 +DA:129,5 +DA:136,20 +DA:139,20 +DA:141,20 +DA:142,11 +DA:143,11 +DA:148,11 +DA:150,3 +DA:151,3 +DA:152,3 +DA:153,3 +DA:162,85 +DA:164,85 +DA:165,85 +DA:167,85 +DA:171,85 +DA:172,18 +DA:173,18 +DA:174,18 +DA:184,1 +DA:187,27 +DA:188,27 +DA:199,34 +DA:200,34 +DA:201,31 +DA:202,31 +DA:203,31 +DA:204,31 +DA:206,34 +DA:214,6 +DA:224,3 +DA:225,3 +DA:226,3 +DA:234,1 +LF:61 +LH:61 +BRDA:23,0,0,1 +BRDA:23,0,1,1 +BRDA:91,1,0,13 +BRDA:91,1,1,97 +BRDA:92,2,0,5 +BRDA:92,2,1,8 +BRDA:92,3,0,13 +BRDA:92,3,1,13 +BRDA:141,4,0,11 +BRDA:141,4,1,9 +BRDA:148,5,0,3 +BRDA:148,5,1,8 +BRDA:171,6,0,18 +BRDA:171,6,1,67 +BRDA:171,7,0,85 +BRDA:171,7,1,83 +BRDA:200,8,0,31 +BRDA:200,8,1,3 +BRF:18 +BRH:18 +end_of_record +TN: +SF:lib/telemetry/DatabricksTelemetryExporter.ts +FN:105,(anonymous_1) +FN:123,(anonymous_2) +FN:131,(anonymous_3) +FN:148,(anonymous_4) +FN:205,(anonymous_5) +FN:216,(anonymous_6) +FN:217,(anonymous_7) +FN:263,(anonymous_8) +FN:324,(anonymous_9) +FN:334,(anonymous_10) +FN:335,(anonymous_11) +FN:345,(anonymous_12) +FN:357,(anonymous_13) +FN:358,(anonymous_14) +FNF:14 +FNH:14 +FNDA:14,(anonymous_1) +FNDA:16,(anonymous_2) +FNDA:13,(anonymous_3) +FNDA:13,(anonymous_4) +FNDA:17,(anonymous_5) +FNDA:18,(anonymous_6) +FNDA:18,(anonymous_7) +FNDA:18,(anonymous_8) +FNDA:17,(anonymous_9) +FNDA:18,(anonymous_10) +FNDA:558,(anonymous_11) +FNDA:14,(anonymous_12) +FNDA:4,(anonymous_13) +FNDA:4,(anonymous_14) +DA:17,1 +DA:19,1 +DA:20,1 +DA:22,1 +DA:98,1 +DA:106,14 +DA:107,14 +DA:108,14 +DA:111,14 +DA:112,14 +DA:115,14 +DA:124,16 +DA:125,1 +DA:128,15 +DA:130,15 +DA:131,15 +DA:132,13 +DA:136,9 +DA:137,2 +DA:139,7 +DA:149,13 +DA:150,13 +DA:151,13 +DA:153,13 +DA:156,13 +DA:157,17 +DA:158,17 +DA:159,6 +DA:161,11 +DA:164,11 +DA:165,2 +DA:166,2 +DA:170,9 +DA:171,2 +DA:172,2 +DA:176,7 +DA:177,3 +DA:178,3 +DA:182,4 +DA:183,4 +DA:184,4 +DA:186,4 +DA:191,4 +DA:197,0 +DA:198,0 +DA:206,17 +DA:207,17 +DA:210,17 +DA:211,17 +DA:216,18 +DA:217,18 +DA:219,17 +DA:225,17 +DA:233,17 +DA:236,17 +DA:237,17 +DA:240,17 +DA:251,15 +DA:252,9 +DA:253,9 +DA:254,9 +DA:257,6 +DA:264,18 +DA:281,18 +DA:283,0 +DA:296,18 +DA:297,0 +DA:299,0 +DA:300,0 +DA:304,0 +DA:305,0 +DA:311,18 +DA:312,0 +DA:318,18 +DA:325,17 +DA:326,1 +DA:328,16 +DA:335,18 +DA:336,558 +DA:337,558 +DA:338,558 +DA:346,14 +DA:348,14 +DA:350,0 +DA:358,4 +DA:359,4 +LF:86 +LH:76 +BRDA:112,0,0,14 +BRDA:112,0,1,0 +BRDA:124,1,0,1 +BRDA:124,1,1,15 +BRDA:124,2,0,16 +BRDA:124,2,1,16 +BRDA:136,3,0,2 +BRDA:136,3,1,7 +BRDA:151,4,0,6 +BRDA:151,4,1,7 +BRDA:151,5,0,13 +BRDA:151,5,1,13 +BRDA:164,6,0,2 +BRDA:164,6,1,9 +BRDA:170,7,0,2 +BRDA:170,7,1,7 +BRDA:176,8,0,3 +BRDA:176,8,1,4 +BRDA:197,9,0,0 +BRDA:197,9,1,0 +BRDA:210,10,0,3 +BRDA:210,10,1,14 +BRDA:210,11,0,17 +BRDA:210,11,1,17 +BRDA:211,12,0,16 +BRDA:211,12,1,1 +BRDA:228,13,0,16 +BRDA:228,13,1,1 +BRDA:233,14,0,16 +BRDA:233,14,1,1 +BRDA:251,15,0,9 +BRDA:251,15,1,6 +BRDA:281,16,0,0 +BRDA:281,16,1,18 +BRDA:281,17,0,18 +BRDA:281,17,1,18 +BRDA:296,18,0,0 +BRDA:296,18,1,18 +BRDA:299,19,0,0 +BRDA:299,19,1,0 +BRDA:299,20,0,0 +BRDA:299,20,1,0 +BRDA:304,21,0,0 +BRDA:304,21,1,0 +BRDA:304,22,0,0 +BRDA:304,22,1,0 +BRDA:311,23,0,0 +BRDA:311,23,1,18 +BRDA:313,24,0,0 +BRDA:313,24,1,0 +BRDA:314,25,0,0 +BRDA:314,25,1,0 +BRDA:325,26,0,1 +BRDA:325,26,1,16 +BRDA:325,27,0,17 +BRDA:325,27,1,17 +BRDA:337,28,0,540 +BRDA:337,28,1,18 +BRF:58 +BRH:40 +end_of_record +TN: +SF:lib/telemetry/ExceptionClassifier.ts +FN:41,(anonymous_1) +FN:73,(anonymous_2) +FNF:2 +FNH:2 +FNDA:11,(anonymous_1) +FNDA:9,(anonymous_2) +DA:17,1 +DA:18,1 +DA:30,1 +DA:43,11 +DA:44,0 +DA:49,11 +DA:51,11 +DA:57,9 +DA:61,2 +DA:75,9 +DA:76,0 +DA:80,9 +DA:81,0 +DA:86,9 +DA:88,9 +DA:95,7 +DA:99,2 +LF:17 +LH:14 +BRDA:43,0,0,0 +BRDA:43,0,1,11 +BRDA:49,1,0,9 +BRDA:49,1,1,2 +BRDA:49,2,0,11 +BRDA:49,2,1,11 +BRDA:51,3,0,9 +BRDA:51,3,1,2 +BRDA:57,4,0,9 +BRDA:57,4,1,8 +BRDA:57,4,2,7 +BRDA:57,4,3,7 +BRDA:75,5,0,0 +BRDA:75,5,1,9 +BRDA:80,6,0,0 +BRDA:80,6,1,9 +BRDA:80,7,0,9 +BRDA:80,7,1,9 +BRDA:86,8,0,7 +BRDA:86,8,1,2 +BRDA:86,9,0,9 +BRDA:86,9,1,9 +BRDA:88,10,0,7 +BRDA:88,10,1,2 +BRDA:95,11,0,7 +BRDA:95,11,1,7 +BRDA:95,11,2,7 +BRDA:95,11,3,7 +BRDA:95,11,4,0 +BRF:29 +BRH:25 +end_of_record +TN: +SF:lib/telemetry/FeatureFlagCache.ts +FN:44,(anonymous_1) +FN:52,(anonymous_2) +FN:69,(anonymous_3) +FN:83,(anonymous_4) +FN:114,(anonymous_5) +FN:162,(anonymous_6) +FN:186,(anonymous_7) +FN:197,(anonymous_8) +FNF:8 +FNH:0 +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +DA:17,0 +DA:19,0 +DA:20,0 +DA:37,0 +DA:40,0 +DA:42,0 +DA:44,0 +DA:45,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:59,0 +DA:61,0 +DA:62,0 +DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:84,0 +DA:85,0 +DA:87,0 +DA:88,0 +DA:91,0 +DA:93,0 +DA:94,0 +DA:96,0 +DA:97,0 +DA:100,0 +DA:104,0 +DA:115,0 +DA:117,0 +DA:119,0 +DA:122,0 +DA:125,0 +DA:127,0 +DA:130,0 +DA:131,0 +DA:134,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:150,0 +DA:153,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:162,0 +DA:164,0 +DA:166,0 +DA:167,0 +DA:168,0 +DA:169,0 +DA:174,0 +DA:175,0 +DA:178,0 +DA:179,0 +DA:187,0 +DA:188,0 +DA:190,0 +DA:199,0 +LF:62 +LH:0 +BRDA:54,0,0,0 +BRDA:54,0,1,0 +BRDA:71,1,0,0 +BRDA:71,1,1,0 +BRDA:73,2,0,0 +BRDA:73,2,1,0 +BRDA:87,3,0,0 +BRDA:87,3,1,0 +BRDA:91,4,0,0 +BRDA:91,4,1,0 +BRDA:93,5,0,0 +BRDA:93,5,1,0 +BRDA:104,6,0,0 +BRDA:104,6,1,0 +BRDA:104,7,0,0 +BRDA:104,7,1,0 +BRDA:144,8,0,0 +BRDA:144,8,1,0 +BRDA:153,9,0,0 +BRDA:153,9,1,0 +BRDA:153,10,0,0 +BRDA:153,10,1,0 +BRDA:153,10,2,0 +BRDA:156,11,0,0 +BRDA:156,11,1,0 +BRDA:156,12,0,0 +BRDA:156,12,1,0 +BRDA:164,13,0,0 +BRDA:164,13,1,0 +BRDA:187,14,0,0 +BRDA:187,14,1,0 +BRDA:187,15,0,0 +BRDA:187,15,1,0 +BRF:33 +BRH:0 +end_of_record +TN: +SF:lib/telemetry/MetricsAggregator.ts +FN:69,(anonymous_1) +FN:95,(anonymous_2) +FN:124,(anonymous_3) +FN:139,(anonymous_4) +FN:184,(anonymous_5) +FN:218,(anonymous_6) +FN:242,(anonymous_7) +FN:294,(anonymous_8) +FN:316,(anonymous_9) +FN:346,(anonymous_10) +FN:354,(anonymous_11) +FN:370,(anonymous_12) +FNF:12 +FNH:12 +FNDA:14,(anonymous_1) +FNDA:21,(anonymous_2) +FNDA:12,(anonymous_3) +FNDA:2,(anonymous_4) +FNDA:6,(anonymous_5) +FNDA:7,(anonymous_6) +FNDA:5,(anonymous_7) +FNDA:18,(anonymous_8) +FNDA:12,(anonymous_9) +FNDA:21,(anonymous_10) +FNDA:2,(anonymous_11) +FNDA:3,(anonymous_12) +DA:18,1 +DA:19,1 +DA:21,1 +DA:56,1 +DA:57,14 +DA:59,14 +DA:61,14 +DA:69,14 +DA:70,14 +DA:71,14 +DA:72,14 +DA:73,14 +DA:74,14 +DA:77,14 +DA:80,0 +DA:81,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:96,21 +DA:98,21 +DA:100,21 +DA:101,12 +DA:102,12 +DA:106,9 +DA:107,2 +DA:108,2 +DA:112,7 +DA:113,6 +DA:117,0 +DA:125,12 +DA:133,12 +DA:140,2 +DA:143,2 +DA:144,2 +DA:147,2 +DA:149,2 +DA:151,1 +DA:154,1 +DA:155,0 +DA:156,0 +DA:157,0 +DA:160,1 +DA:169,1 +DA:173,1 +DA:174,1 +DA:176,1 +DA:177,1 +DA:185,6 +DA:187,6 +DA:189,3 +DA:190,3 +DA:191,3 +DA:194,1 +DA:195,1 +DA:196,1 +DA:197,1 +DA:198,1 +DA:199,1 +DA:202,2 +DA:203,2 +DA:204,2 +DA:205,2 +DA:207,2 +DA:211,0 +DA:219,7 +DA:221,7 +DA:222,4 +DA:234,7 +DA:243,5 +DA:245,5 +DA:246,5 +DA:247,5 +DA:248,1 +DA:252,4 +DA:265,4 +DA:268,4 +DA:269,1 +DA:278,1 +DA:282,4 +DA:285,0 +DA:295,18 +DA:298,18 +DA:299,2 +DA:300,2 +DA:301,2 +DA:302,2 +DA:306,18 +DA:307,1 +DA:317,12 +DA:319,12 +DA:320,12 +DA:321,2 +DA:324,10 +DA:325,10 +DA:327,10 +DA:330,10 +DA:334,10 +DA:335,7 +DA:339,0 +DA:347,21 +DA:349,21 +DA:350,21 +DA:351,7 +DA:354,21 +DA:356,2 +DA:360,21 +DA:363,0 +DA:371,3 +DA:373,3 +DA:375,3 +DA:376,3 +DA:377,3 +DA:381,3 +DA:382,1 +DA:386,3 +DA:389,0 +LF:117 +LH:103 +BRDA:72,0,0,2 +BRDA:72,0,1,12 +BRDA:72,1,0,14 +BRDA:72,1,1,14 +BRDA:73,2,0,3 +BRDA:73,2,1,11 +BRDA:73,3,0,14 +BRDA:73,3,1,14 +BRDA:74,4,0,1 +BRDA:74,4,1,13 +BRDA:74,5,0,14 +BRDA:74,5,1,14 +BRDA:100,6,0,12 +BRDA:100,6,1,9 +BRDA:106,7,0,2 +BRDA:106,7,1,7 +BRDA:112,8,0,6 +BRDA:112,8,1,1 +BRDA:143,9,0,2 +BRDA:143,9,1,0 +BRDA:144,10,0,2 +BRDA:144,10,1,0 +BRDA:147,11,0,2 +BRDA:147,11,1,0 +BRDA:147,12,0,2 +BRDA:147,12,1,2 +BRDA:149,13,0,1 +BRDA:149,13,1,1 +BRDA:154,14,0,0 +BRDA:154,14,1,1 +BRDA:154,15,0,1 +BRDA:154,15,1,0 +BRDA:174,16,0,1 +BRDA:174,16,1,0 +BRDA:187,17,0,3 +BRDA:187,17,1,1 +BRDA:187,17,2,2 +BRDA:187,17,3,0 +BRDA:196,18,0,0 +BRDA:196,18,1,1 +BRDA:196,19,0,1 +BRDA:196,19,1,1 +BRDA:197,20,0,0 +BRDA:197,20,1,1 +BRDA:197,21,0,1 +BRDA:197,21,1,1 +BRDA:198,22,0,0 +BRDA:198,22,1,1 +BRDA:198,23,0,1 +BRDA:198,23,1,1 +BRDA:203,24,0,2 +BRDA:203,24,1,0 +BRDA:203,25,0,2 +BRDA:203,25,1,2 +BRDA:204,26,0,2 +BRDA:204,26,1,0 +BRDA:221,27,0,4 +BRDA:221,27,1,3 +BRDA:247,28,0,1 +BRDA:247,28,1,4 +BRDA:298,29,0,2 +BRDA:298,29,1,16 +BRDA:306,30,0,1 +BRDA:306,30,1,17 +BRDA:316,31,0,7 +BRDA:320,32,0,2 +BRDA:320,32,1,10 +BRDA:334,33,0,7 +BRDA:334,33,1,3 +BRDA:350,34,0,7 +BRDA:350,34,1,14 +BRDA:375,35,0,3 +BRDA:375,35,1,0 +BRF:73 +BRH:60 +end_of_record +TN: +SF:lib/telemetry/TelemetryEventEmitter.ts +FN:35,(anonymous_0) +FN:48,(anonymous_1) +FN:72,(anonymous_2) +FN:96,(anonymous_3) +FN:132,(anonymous_4) +FN:164,(anonymous_5) +FNF:6 +FNH:6 +FNDA:11,(anonymous_0) +FNDA:4,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +FNDA:3,(anonymous_5) +DA:17,1 +DA:19,1 +DA:20,1 +DA:32,1 +DA:35,11 +DA:36,11 +DA:39,11 +DA:40,11 +DA:49,4 +DA:51,3 +DA:52,3 +DA:53,3 +DA:60,3 +DA:63,2 +DA:73,2 +DA:75,1 +DA:76,1 +DA:77,1 +DA:84,1 +DA:87,0 +DA:105,1 +DA:107,1 +DA:108,1 +DA:109,1 +DA:120,1 +DA:123,0 +DA:139,1 +DA:141,1 +DA:142,1 +DA:143,1 +DA:152,1 +DA:155,0 +DA:171,3 +DA:173,2 +DA:174,2 +DA:175,2 +DA:184,2 +DA:187,0 +LF:38 +LH:34 +BRDA:40,0,0,11 +BRDA:40,0,1,0 +BRDA:40,1,0,11 +BRDA:40,1,1,11 +BRDA:49,2,0,1 +BRDA:49,2,1,3 +BRDA:73,3,0,1 +BRDA:73,3,1,1 +BRDA:105,4,0,0 +BRDA:105,4,1,1 +BRDA:139,5,0,0 +BRDA:139,5,1,1 +BRDA:171,6,0,1 +BRDA:171,6,1,2 +BRF:14 +BRH:11 +end_of_record +TN: +SF:lib/telemetry/types.ts +FN:25,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:20,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,1 +DA:65,1 +LF:8 +LH:8 +BRDA:25,0,0,1 +BRDA:25,0,1,1 +BRF:2 +BRH:2 +end_of_record +TN: +SF:lib/utils/CloseableCollection.ts +FN:7,(anonymous_0) +FN:10,(anonymous_1) +FN:11,(anonymous_2) +FN:17,(anonymous_3) +FN:24,(anonymous_4) +FNF:5 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +DA:7,1 +DA:8,0 +DA:11,0 +DA:12,0 +DA:14,0 +DA:18,0 +DA:19,0 +DA:21,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +LF:13 +LH:1 +BRDA:18,0,0,0 +BRDA:18,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:lib/utils/OperationIterator.ts +FN:8,(anonymous_0) +FN:15,(anonymous_1) +FN:19,(anonymous_2) +FN:31,(anonymous_3) +FN:41,(anonymous_4) +FN:57,(anonymous_5) +FN:66,(anonymous_6) +FNF:7 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +DA:9,0 +DA:10,0 +DA:16,0 +DA:20,0 +DA:22,0 +DA:23,0 +DA:26,0 +DA:32,0 +DA:33,0 +DA:36,0 +DA:40,1 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:48,0 +DA:52,1 +DA:53,0 +DA:55,0 +DA:58,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:80,0 +DA:83,0 +LF:30 +LH:2 +BRDA:22,0,0,0 +BRDA:22,0,1,0 +BRDA:22,1,0,0 +BRDA:22,1,1,0 +BRDA:22,2,0,0 +BRDA:22,2,1,0 +BRDA:22,3,0,0 +BRDA:22,3,1,0 +BRDA:32,4,0,0 +BRDA:32,4,1,0 +BRDA:32,5,0,0 +BRDA:32,5,1,0 +BRDA:32,6,0,0 +BRDA:32,6,1,0 +BRDA:43,7,0,0 +BRDA:43,7,1,0 +BRDA:67,8,0,0 +BRDA:67,8,1,0 +BRDA:74,9,0,0 +BRDA:74,9,1,0 +BRF:20 +BRH:0 +end_of_record +TN: +SF:lib/utils/buildUserAgentString.ts +FN:6,getNodeVersion +FN:10,getOperatingSystemVersion +FN:14,redactInternalToken +FN:24,buildUserAgentString +FNF:4 +FNH:0 +FNDA:0,getNodeVersion +FNDA:0,getOperatingSystemVersion +FNDA:0,redactInternalToken +FNDA:0,buildUserAgentString +DA:1,1 +DA:2,1 +DA:4,1 +DA:7,0 +DA:11,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:21,0 +DA:24,1 +DA:25,0 +DA:26,0 +DA:29,0 +DA:30,0 +LF:15 +LH:4 +BRDA:17,0,0,0 +BRDA:17,0,1,0 +BRDA:25,1,0,0 +BRDA:25,1,1,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:lib/utils/definedOrError.ts +FN:1,definedOrError +FNF:1 +FNH:0 +FNDA:0,definedOrError +DA:1,1 +DA:2,0 +DA:3,0 +DA:5,0 +LF:4 +LH:1 +BRDA:2,0,0,0 +BRDA:2,0,1,0 +BRF:2 +BRH:0 +end_of_record +TN: +SF:lib/utils/formatProgress.ts +FN:8,(anonymous_0) +FN:12,(anonymous_1) +FN:13,(anonymous_2) +FN:16,(anonymous_3) +FN:19,(anonymous_4) +FN:25,formatProgress +FNF:6 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,formatProgress +DA:3,1 +DA:6,0 +DA:9,0 +DA:13,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:21,0 +DA:25,1 +DA:26,0 +LF:10 +LH:2 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/utils/index.ts +FN:7,(anonymous_7) +FNF:1 +FNH:0 +FNDA:0,(anonymous_7) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:7,1 +LF:6 +LH:6 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/utils/lz4.ts +FN:5,tryLoadLZ4Module +FN:35,getResolvedModule +FNF:2 +FNH:0 +FNDA:0,tryLoadLZ4Module +FNDA:0,getResolvedModule +DA:6,0 +DA:7,0 +DA:9,0 +DA:11,0 +DA:12,0 +DA:15,0 +DA:16,0 +DA:19,0 +DA:21,0 +DA:22,0 +DA:27,0 +DA:28,0 +DA:36,0 +DA:37,0 +DA:39,0 +DA:42,1 +LF:16 +LH:1 +BRDA:9,0,0,0 +BRDA:9,0,1,0 +BRDA:9,1,0,0 +BRDA:9,1,1,0 +BRDA:15,2,0,0 +BRDA:15,2,1,0 +BRDA:19,3,0,0 +BRDA:19,3,1,0 +BRDA:36,4,0,0 +BRDA:36,4,1,0 +BRDA:37,5,0,0 +BRDA:37,5,1,0 +BRDA:37,6,0,0 +BRDA:37,6,1,0 +BRDA:39,7,0,0 +BRDA:39,7,1,0 +BRF:16 +BRH:0 +end_of_record +TN: +SF:lib/utils/protocolVersion.ts +FN:16,isFeatureSupported +FN:33,supportsParameterizedQueries +FN:43,supportsAsyncMetadataOperations +FN:53,supportsResultPersistenceMode +FN:63,supportsArrowCompression +FN:73,supportsArrowMetadata +FN:83,supportsMultipleCatalogs +FN:93,supportsCloudFetch +FNF:8 +FNH:0 +FNDA:0,isFeatureSupported +FNDA:0,supportsParameterizedQueries +FNDA:0,supportsAsyncMetadataOperations +FNDA:0,supportsResultPersistenceMode +FNDA:0,supportsArrowCompression +FNDA:0,supportsArrowMetadata +FNDA:0,supportsMultipleCatalogs +FNDA:0,supportsCloudFetch +DA:1,1 +DA:16,1 +DA:20,0 +DA:21,0 +DA:24,0 +DA:33,1 +DA:34,0 +DA:43,1 +DA:44,0 +DA:53,1 +DA:54,0 +DA:63,1 +DA:64,0 +DA:73,1 +DA:74,0 +DA:83,1 +DA:84,0 +DA:93,1 +DA:94,0 +LF:19 +LH:9 +BRDA:20,0,0,0 +BRDA:20,0,1,0 +BRDA:20,1,0,0 +BRDA:20,1,1,0 +BRF:4 +BRH:0 +end_of_record diff --git a/lib/DBSQLClient.ts b/lib/DBSQLClient.ts index ee53e790..b8656cf8 100644 --- a/lib/DBSQLClient.ts +++ b/lib/DBSQLClient.ts @@ -2,6 +2,7 @@ import thrift from 'thrift'; import Int64 from 'node-int64'; import { EventEmitter } from 'events'; +import { HeadersInit } from 'node-fetch'; import TCLIService from '../thrift/TCLIService'; import { TProtocolVersion } from '../thrift/TCLIService_types'; import IDBSQLClient, { ClientOptions, ConnectionOptions, OpenSessionRequest } from './contracts/IDBSQLClient'; @@ -238,12 +239,6 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I this.config.enableMetricViewMetadata = options.enableMetricViewMetadata; } - // Persist userAgentEntry so telemetry and feature-flag call sites reuse - // the same value as the primary Thrift connection's User-Agent. - if (options.userAgentEntry !== undefined) { - this.config.userAgentEntry = options.userAgentEntry; - } - this.authProvider = this.createAuthProvider(options, authProvider); this.connectionProvider = this.createConnectionProvider(options); @@ -359,14 +354,15 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I return this.driver; } - /** - * Returns the authentication provider associated with this client, if any. - * Intended for internal telemetry/feature-flag call sites that need to - * obtain auth headers directly without routing through `IClientContext`. - * - * @internal Not part of the public API. May change without notice. - */ - public getAuthProvider(): IAuthentication | undefined { - return this.authProvider; + public async getAuthHeaders(): Promise { + if (this.authProvider) { + try { + return await this.authProvider.authenticate(); + } catch (error) { + this.logger.log(LogLevel.debug, `Error getting auth headers: ${error}`); + return {}; + } + } + return {}; } } diff --git a/lib/contracts/IClientContext.ts b/lib/contracts/IClientContext.ts index c7274a1b..051dcf3f 100644 --- a/lib/contracts/IClientContext.ts +++ b/lib/contracts/IClientContext.ts @@ -1,3 +1,4 @@ +import { HeadersInit } from 'node-fetch'; import IDBSQLLogger from './IDBSQLLogger'; import IDriver from './IDriver'; import IConnectionProvider from '../connection/contracts/IConnectionProvider'; @@ -28,16 +29,10 @@ export interface ClientConfig { telemetryBatchSize?: number; telemetryFlushIntervalMs?: number; telemetryMaxRetries?: number; - telemetryBackoffBaseMs?: number; - telemetryBackoffMaxMs?: number; - telemetryBackoffJitterMs?: number; telemetryAuthenticatedExport?: boolean; telemetryCircuitBreakerThreshold?: number; telemetryCircuitBreakerTimeout?: number; telemetryMaxPendingMetrics?: number; - telemetryMaxErrorsPerStatement?: number; - telemetryStatementTtlMs?: number; - userAgentEntry?: string; } export default interface IClientContext { @@ -50,4 +45,11 @@ export default interface IClientContext { getClient(): Promise; getDriver(): Promise; + + /** + * Gets authentication headers for HTTP requests. + * Used by telemetry and feature flag fetching to authenticate REST API calls. + * @returns Promise resolving to headers object with authentication, or empty object if no auth + */ + getAuthHeaders(): Promise; } diff --git a/lib/index.ts b/lib/index.ts index 81e3aaae..adf14f36 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -23,11 +23,6 @@ import { LogLevel } from './contracts/IDBSQLLogger'; // Re-export types for TypeScript users export type { default as ITokenProvider } from './connection/auth/tokenProvider/ITokenProvider'; -// Re-export telemetry error classes so consumers can instanceof-check rather -// than string-matching error messages. -export { CircuitBreakerOpenError, CIRCUIT_BREAKER_OPEN_CODE } from './telemetry/CircuitBreaker'; -export { TelemetryTerminalError } from './telemetry/DatabricksTelemetryExporter'; - export const auth = { PlainHttpAuthentication, // Token provider classes for custom authentication diff --git a/lib/telemetry/CircuitBreaker.ts b/lib/telemetry/CircuitBreaker.ts index 33b1fb5b..b8dbd296 100644 --- a/lib/telemetry/CircuitBreaker.ts +++ b/lib/telemetry/CircuitBreaker.ts @@ -17,41 +17,48 @@ import IClientContext from '../contracts/IClientContext'; import { LogLevel } from '../contracts/IDBSQLLogger'; +/** + * States of the circuit breaker. + */ export enum CircuitBreakerState { + /** Normal operation, requests pass through */ CLOSED = 'CLOSED', + /** After threshold failures, all requests rejected immediately */ OPEN = 'OPEN', + /** After timeout, allows test requests to check if endpoint recovered */ HALF_OPEN = 'HALF_OPEN', } +/** + * Configuration for circuit breaker behavior. + */ export interface CircuitBreakerConfig { + /** Number of consecutive failures before opening the circuit */ failureThreshold: number; + /** Time in milliseconds to wait before attempting recovery */ timeout: number; + /** Number of consecutive successes in HALF_OPEN state to close the circuit */ successThreshold: number; } -export const DEFAULT_CIRCUIT_BREAKER_CONFIG: Readonly = Object.freeze({ +/** + * Default circuit breaker configuration. + */ +export const DEFAULT_CIRCUIT_BREAKER_CONFIG: CircuitBreakerConfig = { failureThreshold: 5, - timeout: 60000, + timeout: 60000, // 1 minute successThreshold: 2, -}); - -export const CIRCUIT_BREAKER_OPEN_CODE = 'CIRCUIT_BREAKER_OPEN' as const; +}; /** - * Thrown when execute() is called while the breaker is OPEN or a HALF_OPEN - * probe is already in flight. Callers identify the condition via - * `instanceof CircuitBreakerOpenError` or `err.code === CIRCUIT_BREAKER_OPEN_CODE` - * rather than string-matching the message. + * Circuit breaker for telemetry exporter. + * Protects against failing telemetry endpoint with automatic recovery. + * + * States: + * - CLOSED: Normal operation, requests pass through + * - OPEN: After threshold failures, all requests rejected immediately + * - HALF_OPEN: After timeout, allows test requests to check if endpoint recovered */ -export class CircuitBreakerOpenError extends Error { - readonly code = CIRCUIT_BREAKER_OPEN_CODE; - - constructor(message = 'Circuit breaker OPEN') { - super(message); - this.name = 'CircuitBreakerOpenError'; - } -} - export class CircuitBreaker { private state: CircuitBreakerState = CircuitBreakerState.CLOSED; @@ -61,6 +68,7 @@ export class CircuitBreaker { private nextAttempt?: Date; + /** Number of in-flight requests in HALF_OPEN state (limits to 1 probe) */ private halfOpenInflight = 0; private readonly config: CircuitBreakerConfig; @@ -72,76 +80,79 @@ export class CircuitBreaker { }; } - async execute(operation: () => Promise): Promise { - const admitted = this.tryAdmit(); - if (!admitted) { - throw new CircuitBreakerOpenError(); - } - - const { wasHalfOpenProbe } = admitted; - - try { - const result = await operation(); - this.onSuccess(); - return result; - } catch (error) { - this.onFailure(); - throw error; - } finally { - if (wasHalfOpenProbe && this.halfOpenInflight > 0) { - this.halfOpenInflight -= 1; - } - } - } - /** - * Synchronous admission check. Returning `null` means "reject". Returning - * an object means the caller is admitted; `wasHalfOpenProbe` indicates - * whether this admission consumed the single HALF_OPEN probe slot so the - * caller can decrement it in `finally`. + * Executes an operation with circuit breaker protection. * - * Running this as a single synchronous block is what prevents the - * concurrent-probe race that existed in the previous implementation. + * @param operation The operation to execute + * @returns Promise resolving to the operation result + * @throws Error if circuit is OPEN or operation fails */ - private tryAdmit(): { wasHalfOpenProbe: boolean } | null { + async execute(operation: () => Promise): Promise { const logger = this.context.getLogger(); + // Check if circuit is open if (this.state === CircuitBreakerState.OPEN) { if (this.nextAttempt && Date.now() < this.nextAttempt.getTime()) { - return null; + throw new Error('Circuit breaker OPEN'); } + // Timeout expired, transition to HALF_OPEN this.state = CircuitBreakerState.HALF_OPEN; this.successCount = 0; this.halfOpenInflight = 0; logger.log(LogLevel.debug, 'Circuit breaker transitioned to HALF_OPEN'); } + // In HALF_OPEN state, allow only one probe request at a time + if (this.state === CircuitBreakerState.HALF_OPEN && this.halfOpenInflight > 0) { + throw new Error('Circuit breaker OPEN'); + } + if (this.state === CircuitBreakerState.HALF_OPEN) { - if (this.halfOpenInflight > 0) { - return null; - } this.halfOpenInflight += 1; - return { wasHalfOpenProbe: true }; } - return { wasHalfOpenProbe: false }; + try { + const result = await operation(); + this.onSuccess(); + return result; + } catch (error) { + this.onFailure(); + throw error; + } finally { + if (this.halfOpenInflight > 0) { + this.halfOpenInflight -= 1; + } + } } + /** + * Gets the current state of the circuit breaker. + */ getState(): CircuitBreakerState { return this.state; } + /** + * Gets the current failure count. + */ getFailureCount(): number { return this.failureCount; } + /** + * Gets the current success count (relevant in HALF_OPEN state). + */ getSuccessCount(): number { return this.successCount; } + /** + * Handles successful operation execution. + */ private onSuccess(): void { const logger = this.context.getLogger(); + // Reset failure count on any success this.failureCount = 0; if (this.state === CircuitBreakerState.HALF_OPEN) { @@ -152,6 +163,7 @@ export class CircuitBreaker { ); if (this.successCount >= this.config.successThreshold) { + // Transition to CLOSED this.state = CircuitBreakerState.CLOSED; this.successCount = 0; this.nextAttempt = undefined; @@ -160,17 +172,23 @@ export class CircuitBreaker { } } + /** + * Handles failed operation execution. + */ private onFailure(): void { const logger = this.context.getLogger(); this.failureCount += 1; - this.successCount = 0; + this.successCount = 0; // Reset success count on failure logger.log(LogLevel.debug, `Circuit breaker failure (${this.failureCount}/${this.config.failureThreshold})`); + // In HALF_OPEN state, any failure immediately reopens the circuit. + // In CLOSED state, reopen only after failureThreshold consecutive failures. if (this.state === CircuitBreakerState.HALF_OPEN || this.failureCount >= this.config.failureThreshold) { this.state = CircuitBreakerState.OPEN; this.nextAttempt = new Date(Date.now() + this.config.timeout); + // Log at warn level for OPEN transitions — meaningful operational signal logger.log( LogLevel.warn, `Telemetry circuit breaker OPEN after ${this.failureCount} failures (will retry after ${this.config.timeout}ms)`, @@ -179,6 +197,11 @@ export class CircuitBreaker { } } +/** + * Manages circuit breakers per host. + * Ensures each host has its own isolated circuit breaker to prevent + * failures on one host from affecting telemetry to other hosts. + */ export class CircuitBreakerRegistry { private breakers: Map; @@ -186,6 +209,13 @@ export class CircuitBreakerRegistry { this.breakers = new Map(); } + /** + * Gets or creates a circuit breaker for the specified host. + * + * @param host The host identifier (e.g., "workspace.cloud.databricks.com") + * @param config Optional configuration overrides + * @returns Circuit breaker for the host + */ getCircuitBreaker(host: string, config?: Partial): CircuitBreaker { let breaker = this.breakers.get(host); if (!breaker) { @@ -200,16 +230,30 @@ export class CircuitBreakerRegistry { return breaker; } + /** + * Gets all registered circuit breakers. + * Useful for testing and diagnostics. + */ getAllBreakers(): Map { return new Map(this.breakers); } + /** + * Removes a circuit breaker for the specified host. + * Useful for cleanup when a host is no longer in use. + * + * @param host The host identifier + */ removeCircuitBreaker(host: string): void { this.breakers.delete(host); const logger = this.context.getLogger(); logger.log(LogLevel.debug, `Removed circuit breaker for host: ${host}`); } + /** + * Clears all circuit breakers. + * Useful for testing. + */ clear(): void { this.breakers.clear(); } diff --git a/lib/telemetry/DatabricksTelemetryExporter.ts b/lib/telemetry/DatabricksTelemetryExporter.ts index 37cf1c70..c85f1ba6 100644 --- a/lib/telemetry/DatabricksTelemetryExporter.ts +++ b/lib/telemetry/DatabricksTelemetryExporter.ts @@ -15,24 +15,17 @@ */ import { v4 as uuidv4 } from 'uuid'; -import fetch, { RequestInit, Response } from 'node-fetch'; +import fetch, { Response, RequestInit, Request } from 'node-fetch'; import IClientContext from '../contracts/IClientContext'; import { LogLevel } from '../contracts/IDBSQLLogger'; -import IAuthentication from '../connection/contracts/IAuthentication'; -import AuthenticationError from '../errors/AuthenticationError'; -import HiveDriverError from '../errors/HiveDriverError'; import { TelemetryMetric, DEFAULT_TELEMETRY_CONFIG } from './types'; -import { CircuitBreaker, CircuitBreakerOpenError, CircuitBreakerRegistry } from './CircuitBreaker'; -import ExceptionClassifier from './ExceptionClassifier'; -import { - buildTelemetryUrl, - hasAuthorization, - normalizeHeaders, - redactSensitive, - sanitizeProcessName, -} from './telemetryUtils'; -import buildUserAgentString from '../utils/buildUserAgentString'; +import { CircuitBreaker, CircuitBreakerRegistry } from './CircuitBreaker'; +import buildTelemetryUrl from './telemetryUtils'; +import driverVersion from '../version'; +/** + * Databricks telemetry log format for export. + */ interface DatabricksTelemetryLog { workspace_id?: string; frontend_log_event_id: string; @@ -60,6 +53,7 @@ interface DatabricksTelemetryLog { char_set_encoding?: string; process_name?: string; }; + driver_connection_params?: any; operation_latency_ms?: number; sql_operation?: { execution_result?: string; @@ -80,60 +74,50 @@ interface DatabricksTelemetryLog { } /** - * Thrown for non-credential terminal telemetry failures (e.g. refusal to - * export to an invalid host). Separate from `AuthenticationError` so the - * classifier can keep the "short-circuit, don't retry, count as breaker - * failure" contract without muddying the auth taxonomy used by the rest of - * the driver. + * Payload format for Databricks telemetry export. + * Matches JDBC TelemetryRequest format with protoLogs. */ -export class TelemetryTerminalError extends HiveDriverError { - readonly terminal = true as const; +interface DatabricksTelemetryPayload { + uploadTime: number; + items: string[]; // Always empty - required field + protoLogs: string[]; // JSON-stringified DatabricksTelemetryLog objects } /** - * Exports telemetry metrics to the Databricks telemetry service. + * Exports telemetry metrics to Databricks telemetry service. * - * CRITICAL: export() never throws — all errors are swallowed and logged at - * LogLevel.debug (the one exception is a single warn on the first observed - * auth-missing, re-armed on recovery). + * Endpoints: + * - Authenticated: /telemetry-ext + * - Unauthenticated: /telemetry-unauth + * + * Features: + * - Circuit breaker integration for endpoint protection + * - Retry logic with exponential backoff for retryable errors + * - Terminal error detection (no retry on 400, 401, 403, 404) + * - CRITICAL: export() method NEVER throws - all exceptions swallowed + * - CRITICAL: All logging at LogLevel.debug ONLY */ export default class DatabricksTelemetryExporter { private readonly circuitBreaker: CircuitBreaker; - private readonly authenticatedUserAgent: string; - - /** User-Agent used for the unauthenticated endpoint; strips any - * caller-supplied `userAgentEntry` that could identify the customer. */ - private readonly unauthenticatedUserAgent: string; - - private authMissingWarned = false; + private readonly userAgent: string; constructor( private context: IClientContext, private host: string, private circuitBreakerRegistry: CircuitBreakerRegistry, - private authProvider?: IAuthentication, ) { this.circuitBreaker = circuitBreakerRegistry.getCircuitBreaker(host); - const config = this.context.getConfig(); - this.authenticatedUserAgent = buildUserAgentString(config.userAgentEntry); - this.unauthenticatedUserAgent = buildUserAgentString(undefined); + + // Get driver version for user agent + this.userAgent = `databricks-sql-nodejs/${this.getDriverVersion()}`; } /** - * Release the per-host circuit breaker. Intended for the owning client's - * close() path. + * Export metrics to Databricks service. Never throws. * - * NOTE: `CircuitBreakerRegistry` currently shares one breaker per host - * across consumers; calling this while another consumer is active will - * reset their failure-count memory. The owning-client is expected to be - * the last consumer on its host; multi-consumer refcounting on the - * registry will land in the consumer-wiring PR. + * @param metrics - Array of telemetry metrics to export */ - dispose(): void { - this.circuitBreakerRegistry.removeCircuitBreaker(this.host); - } - async export(metrics: TelemetryMetric[]): Promise { if (!metrics || metrics.length === 0) { return; @@ -142,123 +126,41 @@ export default class DatabricksTelemetryExporter { const logger = this.context.getLogger(); try { - await this.circuitBreaker.execute(() => this.exportWithRetry(metrics)); + await this.circuitBreaker.execute(async () => { + await this.exportInternal(metrics); + }); } catch (error: any) { - if (error instanceof CircuitBreakerOpenError) { + // CRITICAL: All exceptions swallowed and logged at debug level ONLY + if (error.message === 'Circuit breaker OPEN') { logger.log(LogLevel.debug, 'Circuit breaker OPEN - dropping telemetry'); - } else if (error instanceof AuthenticationError) { - logger.log(LogLevel.debug, `Telemetry export auth failure: ${error.message}`); - } else if (error instanceof TelemetryTerminalError) { - logger.log(LogLevel.debug, `Telemetry export refused: ${error.message}`); } else { - logger.log(LogLevel.debug, `Telemetry export error: ${error?.message ?? error}`); + logger.log(LogLevel.debug, `Telemetry export error: ${error.message}`); } } } /** - * Retry wrapper shaped after HttpRetryPolicy: retries only on errors - * classified as retryable by ExceptionClassifier, stops on terminal ones, - * surfaces the last error to the circuit breaker. - * - * `maxRetries` is the number of retries *after* the first attempt (i.e. - * attempts = maxRetries + 1), matching HttpRetryPolicy's semantics. + * Internal export implementation that makes the HTTP call. */ - private async exportWithRetry(metrics: TelemetryMetric[]): Promise { - const config = this.context.getConfig(); - const logger = this.context.getLogger(); - - const rawMaxRetries = config.telemetryMaxRetries ?? DEFAULT_TELEMETRY_CONFIG.maxRetries; - const maxRetries = - Number.isFinite(rawMaxRetries) && rawMaxRetries >= 0 ? rawMaxRetries : DEFAULT_TELEMETRY_CONFIG.maxRetries; - const baseMs = config.telemetryBackoffBaseMs ?? DEFAULT_TELEMETRY_CONFIG.backoffBaseMs; - const maxMs = config.telemetryBackoffMaxMs ?? DEFAULT_TELEMETRY_CONFIG.backoffMaxMs; - const jitterMs = config.telemetryBackoffJitterMs ?? DEFAULT_TELEMETRY_CONFIG.backoffJitterMs; - - const totalAttempts = maxRetries + 1; - - let lastError: Error | null = null; - - /* eslint-disable no-await-in-loop */ - for (let attempt = 0; attempt < totalAttempts; attempt += 1) { - try { - await this.exportInternal(metrics); - return; - } catch (error: any) { - lastError = error; - - if ( - error instanceof AuthenticationError || - error instanceof TelemetryTerminalError || - ExceptionClassifier.isTerminal(error) - ) { - throw error; - } - if (!ExceptionClassifier.isRetryable(error)) { - throw error; - } - if (attempt >= totalAttempts - 1) { - throw error; - } - - const base = Math.min(baseMs * 2 ** attempt, maxMs); - const jitter = Math.random() * jitterMs; - const delay = Math.min(base + jitter, maxMs); - - // Include the failing error so ops can see what's being retried, - // not just the cadence. - logger.log( - LogLevel.debug, - `Retrying telemetry export (attempt ${attempt + 1}/${totalAttempts}) after ${Math.round(delay)}ms: ${ - error?.statusCode ?? '' - } ${redactSensitive(error?.message ?? '')}`, - ); - - await this.sleep(delay); - } - } - /* eslint-enable no-await-in-loop */ - - if (lastError) { - throw lastError; - } - } - private async exportInternal(metrics: TelemetryMetric[]): Promise { const config = this.context.getConfig(); const logger = this.context.getLogger(); + // Determine endpoint based on authentication mode const authenticatedExport = config.telemetryAuthenticatedExport ?? DEFAULT_TELEMETRY_CONFIG.authenticatedExport; - const endpoint = buildTelemetryUrl(this.host, authenticatedExport ? '/telemetry-ext' : '/telemetry-unauth'); - if (!endpoint) { - // Malformed / deny-listed host — drop the batch rather than letting - // it target an attacker-controlled destination. - throw new TelemetryTerminalError('Refusing telemetry export: host failed validation'); - } + const endpoint = authenticatedExport + ? buildTelemetryUrl(this.host, '/telemetry-ext') + : buildTelemetryUrl(this.host, '/telemetry-unauth'); - const userAgent = authenticatedExport ? this.authenticatedUserAgent : this.unauthenticatedUserAgent; - let headers: Record = { - 'Content-Type': 'application/json', - 'User-Agent': userAgent, - }; - - if (authenticatedExport) { - headers = { ...headers, ...(await this.getAuthHeaders()) }; - if (!hasAuthorization(headers)) { - if (!this.authMissingWarned) { - this.authMissingWarned = true; - logger.log(LogLevel.warn, 'Telemetry: Authorization header missing — metrics will be dropped'); - } - throw new AuthenticationError('Telemetry export: missing Authorization header'); - } - } + // Format payload - each log is JSON-stringified to match JDBC format + const telemetryLogs = metrics.map((m) => this.toTelemetryLog(m)); + const protoLogs = telemetryLogs.map((log) => JSON.stringify(log)); - const protoLogs = metrics.map((m) => this.toTelemetryLog(m, authenticatedExport, userAgent)); - const body = JSON.stringify({ + const payload: DatabricksTelemetryPayload = { uploadTime: Date.now(), - items: [], - protoLogs: protoLogs.map((log) => JSON.stringify(log)), - }); + items: [], // Required but unused + protoLogs, + }; logger.log( LogLevel.debug, @@ -267,76 +169,82 @@ export default class DatabricksTelemetryExporter { } endpoint`, ); - const response = await this.sendRequest(endpoint, { + // Get authentication headers if using authenticated endpoint + const authHeaders = authenticatedExport ? await this.context.getAuthHeaders() : {}; + + // Skip export if authenticated mode is requested but no auth headers available. + // Note: all auth providers in this codebase return plain objects (Record). + const headersObj = authHeaders as Record; + if (authenticatedExport && (!headersObj || !headersObj.Authorization)) { + logger.log(LogLevel.debug, 'Skipping telemetry export: authenticated mode but no Authorization header'); + return; + } + + // Make HTTP POST request with authentication and proxy support + const response: Response = await this.sendRequest(endpoint, { method: 'POST', - headers, - body, - timeout: 10000, + headers: { + ...authHeaders, + 'Content-Type': 'application/json', + 'User-Agent': this.userAgent, + }, + body: JSON.stringify(payload), + timeout: 10000, // 10 second timeout to prevent indefinite hangs }); if (!response.ok) { + // Consume response body to release socket back to connection pool await response.text().catch(() => {}); const error: any = new Error(`Telemetry export failed: ${response.status} ${response.statusText}`); error.statusCode = response.status; throw error; } + // Consume response body to release socket back to connection pool await response.text().catch(() => {}); - // Successful round-trip re-arms the "auth missing" warn so operators see - // a fresh signal the next time auth breaks. - this.authMissingWarned = false; logger.log(LogLevel.debug, `Successfully exported ${metrics.length} telemetry metrics`); } - private async getAuthHeaders(): Promise> { - if (!this.authProvider) { - return {}; - } - const logger = this.context.getLogger(); - try { - return normalizeHeaders(await this.authProvider.authenticate()); - } catch (error: any) { - logger.log(LogLevel.debug, `Telemetry: auth provider threw: ${error?.message ?? error}`); - return {}; - } - } - + /** + * Makes an HTTP request through the shared connection stack (agent + retry policy), + * matching the CloudFetchResultHandler pattern. + */ private async sendRequest(url: string, init: RequestInit): Promise { const connectionProvider = await this.context.getConnectionProvider(); const agent = await connectionProvider.getAgent(); - return fetch(url, { ...init, agent }); + const retryPolicy = await connectionProvider.getRetryPolicy(); + const requestConfig: RequestInit = { agent, ...init }; + const result = await retryPolicy.invokeWithRetry(() => { + const request = new Request(url, requestConfig); + return fetch(request).then((response) => ({ request, response })); + }); + return result.response; } - private toTelemetryLog( - metric: TelemetryMetric, - authenticatedExport: boolean, - userAgent: string, - ): DatabricksTelemetryLog { - // Unauthenticated export must not ship correlation IDs, fingerprint - // data, or raw error detail — an on-path observer could otherwise link - // sessions → workspaces → user activity without any auth. - const includeCorrelation = authenticatedExport; - + /** + * Convert TelemetryMetric to Databricks telemetry log format. + */ + private toTelemetryLog(metric: TelemetryMetric): DatabricksTelemetryLog { const log: DatabricksTelemetryLog = { - workspace_id: includeCorrelation ? metric.workspaceId : undefined, - frontend_log_event_id: uuidv4(), + workspace_id: metric.workspaceId, + frontend_log_event_id: this.generateUUID(), context: { client_context: { timestamp_millis: metric.timestamp, - user_agent: userAgent, + user_agent: this.userAgent, }, }, entry: { sql_driver_log: { - session_id: includeCorrelation ? metric.sessionId : undefined, - sql_statement_id: includeCorrelation ? metric.statementId : undefined, + session_id: metric.sessionId, + sql_statement_id: metric.statementId, }, }, }; - if (metric.metricType === 'connection' && metric.driverConfig && includeCorrelation) { - // system_configuration is a high-entropy client fingerprint (OS, arch, - // locale, process, runtime). Only ship on the authenticated path. + // Add metric-specific fields based on proto definition + if (metric.metricType === 'connection' && metric.driverConfig) { + // Map driverConfig to system_configuration (snake_case as per proto) log.entry.sql_driver_log.system_configuration = { driver_version: metric.driverConfig.driverVersion, driver_name: metric.driverConfig.driverName, @@ -348,7 +256,7 @@ export default class DatabricksTelemetryExporter { os_arch: metric.driverConfig.osArch, locale_name: metric.driverConfig.localeName, char_set_encoding: metric.driverConfig.charSetEncoding, - process_name: sanitizeProcessName(metric.driverConfig.processName) || undefined, + process_name: metric.driverConfig.processName, }; } else if (metric.metricType === 'statement') { log.entry.sql_driver_log.operation_latency_ms = metric.latencyMs; @@ -366,21 +274,26 @@ export default class DatabricksTelemetryExporter { } } } else if (metric.metricType === 'error') { - const stackOrMessage = metric.errorStack ?? metric.errorMessage ?? ''; log.entry.sql_driver_log.error_info = { error_name: metric.errorName || 'UnknownError', - // Redact common secret shapes and cap length. On the unauth path we - // keep only the error class — no message body. - stack_trace: includeCorrelation ? redactSensitive(stackOrMessage) : '', + stack_trace: metric.errorMessage || '', }; } return log; } - private sleep(ms: number): Promise { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); + /** + * Generate a UUID v4. + */ + private generateUUID(): string { + return uuidv4(); + } + + /** + * Get driver version from the version module. + */ + private getDriverVersion(): string { + return driverVersion; } } diff --git a/lib/telemetry/ExceptionClassifier.ts b/lib/telemetry/ExceptionClassifier.ts index a9185eb4..d0e30d64 100644 --- a/lib/telemetry/ExceptionClassifier.ts +++ b/lib/telemetry/ExceptionClassifier.ts @@ -81,17 +81,13 @@ export default class ExceptionClassifier { return true; } - // Transient network errors. Notably does NOT include `ENOTFOUND`: - // DNS "not found" usually indicates a misconfigured host, not a transient - // fault. Retrying pushes load at the resolver without any expectation of - // success; circuit-breaker failure accounting is the better response. + // Check for transient network errors (connection refused, DNS failure, host unreachable) const errorCode = (error as any).code; if ( errorCode === 'ECONNREFUSED' || - errorCode === 'ECONNRESET' || + errorCode === 'ENOTFOUND' || errorCode === 'EHOSTUNREACH' || - errorCode === 'ETIMEDOUT' || - errorCode === 'EAI_AGAIN' + errorCode === 'ECONNRESET' ) { return true; } diff --git a/lib/telemetry/FeatureFlagCache.ts b/lib/telemetry/FeatureFlagCache.ts index 58c758ff..744d1ff9 100644 --- a/lib/telemetry/FeatureFlagCache.ts +++ b/lib/telemetry/FeatureFlagCache.ts @@ -14,51 +14,49 @@ * limitations under the License. */ -import fetch, { RequestInit, Response } from 'node-fetch'; +import fetch, { Request, RequestInit } from 'node-fetch'; import IClientContext from '../contracts/IClientContext'; import { LogLevel } from '../contracts/IDBSQLLogger'; -import IAuthentication from '../connection/contracts/IAuthentication'; -import { buildTelemetryUrl, normalizeHeaders } from './telemetryUtils'; -import ExceptionClassifier from './ExceptionClassifier'; -import buildUserAgentString from '../utils/buildUserAgentString'; +import buildTelemetryUrl from './telemetryUtils'; import driverVersion from '../version'; +/** + * Context holding feature flag state for a specific host. + */ export interface FeatureFlagContext { telemetryEnabled?: boolean; lastFetched?: Date; refCount: number; - cacheDuration: number; + cacheDuration: number; // 15 minutes in ms } /** - * Per-host feature-flag cache used to gate telemetry emission. Responsibilities: - * - dedupe in-flight fetches (thundering-herd protection); - * - ref-count so context goes away when the last consumer closes; - * - clamp server-provided TTL into a safe band. - * - * Shares HTTP plumbing (agent, user agent) with DatabricksTelemetryExporter. - * Consumer wiring lands in a later PR in this stack (see PR description). + * Manages feature flag cache per host. + * Prevents rate limiting by caching feature flag responses. + * Instance-based, stored in DBSQLClient. */ export default class FeatureFlagCache { private contexts: Map; + /** In-flight fetch promises for deduplication (prevents thundering herd) */ private fetchPromises: Map> = new Map(); - private readonly userAgent: string; + private readonly CACHE_DURATION_MS = 15 * 60 * 1000; // 15 minutes - private readonly CACHE_DURATION_MS = 15 * 60 * 1000; + private readonly MIN_CACHE_DURATION_S = 60; // 1 minute minimum TTL - private readonly MIN_CACHE_DURATION_S = 60; - - private readonly MAX_CACHE_DURATION_S = 3600; + private readonly MAX_CACHE_DURATION_S = 3600; // 1 hour maximum TTL private readonly FEATURE_FLAG_NAME = 'databricks.partnerplatform.clientConfigsFeatureFlags.enableTelemetryForNodeJs'; - constructor(private context: IClientContext, private authProvider?: IAuthentication) { + constructor(private context: IClientContext) { this.contexts = new Map(); - this.userAgent = buildUserAgentString(this.context.getConfig().userAgentEntry); } + /** + * Gets or creates a feature flag context for the host. + * Increments reference count. + */ getOrCreateContext(host: string): FeatureFlagContext { let ctx = this.contexts.get(host); if (!ctx) { @@ -72,17 +70,25 @@ export default class FeatureFlagCache { return ctx; } + /** + * Decrements reference count for the host. + * Removes context when ref count reaches zero. + */ releaseContext(host: string): void { const ctx = this.contexts.get(host); if (ctx) { ctx.refCount -= 1; if (ctx.refCount <= 0) { this.contexts.delete(host); - this.fetchPromises.delete(host); + this.fetchPromises.delete(host); // Invalidate stale in-flight fetch } } } + /** + * Checks if telemetry is enabled for the host. + * Uses cached value if available and not expired. + */ async isTelemetryEnabled(host: string): Promise { const logger = this.context.getLogger(); const ctx = this.contexts.get(host); @@ -94,6 +100,7 @@ export default class FeatureFlagCache { const isExpired = !ctx.lastFetched || Date.now() - ctx.lastFetched.getTime() > ctx.cacheDuration; if (isExpired) { + // Deduplicate concurrent fetches for the same host (prevents thundering herd) if (!this.fetchPromises.has(host)) { const fetchPromise = this.fetchFeatureFlag(host) .then((enabled) => { @@ -111,6 +118,7 @@ export default class FeatureFlagCache { this.fetchPromises.set(host, fetchPromise); } + // Promise is guaranteed to resolve (never rejects) due to .catch() in the chain above await this.fetchPromises.get(host); } @@ -118,106 +126,96 @@ export default class FeatureFlagCache { } /** - * Strips the `-oss` suffix the feature-flag API does not accept. The server - * keys off the SemVer triplet only, so anything appended would 404. + * Fetches feature flag from server using connector-service API. + * Calls GET /api/2.0/connector-service/feature-flags/NODEJS/{version} + * + * @param host The host to fetch feature flag for + * @returns true if feature flag is enabled, false otherwise */ - private getDriverVersion(): string { - return driverVersion.replace(/-oss$/, ''); - } - private async fetchFeatureFlag(host: string): Promise { const logger = this.context.getLogger(); try { - const endpoint = buildTelemetryUrl( - host, - `/api/2.0/connector-service/feature-flags/NODEJS/${this.getDriverVersion()}`, - ); - if (!endpoint) { - logger.log(LogLevel.debug, `Feature flag fetch skipped: invalid host ${host}`); - return false; - } + // Get driver version for endpoint + const version = this.getDriverVersion(); - const headers: Record = { - 'Content-Type': 'application/json', - 'User-Agent': this.userAgent, - ...(await this.getAuthHeaders()), - }; + // Build feature flags endpoint for Node.js driver + const endpoint = buildTelemetryUrl(host, `/api/2.0/connector-service/feature-flags/NODEJS/${version}`); + + // Get authentication headers + const authHeaders = await this.context.getAuthHeaders(); logger.log(LogLevel.debug, `Fetching feature flags from ${endpoint}`); - const response = await this.fetchWithRetry(endpoint, { + const connectionProvider = await this.context.getConnectionProvider(); + const agent = await connectionProvider.getAgent(); + const retryPolicy = await connectionProvider.getRetryPolicy(); + + const requestConfig: RequestInit = { method: 'GET', - headers, + headers: { + ...authHeaders, + 'Content-Type': 'application/json', + 'User-Agent': `databricks-sql-nodejs/${driverVersion}`, + }, + agent, timeout: 10000, + }; + + const result = await retryPolicy.invokeWithRetry(() => { + const request = new Request(endpoint, requestConfig); + return fetch(request).then((response) => ({ request, response })); }); + const response = result.response; if (!response.ok) { + // Consume response body to release socket back to connection pool await response.text().catch(() => {}); logger.log(LogLevel.debug, `Feature flag fetch failed: ${response.status} ${response.statusText}`); return false; } + // Parse response JSON const data: any = await response.json(); + // Response format: { flags: [{ name: string, value: string }], ttl_seconds?: number } if (data && data.flags && Array.isArray(data.flags)) { + // Update cache duration if TTL provided, clamped to safe bounds const ctx = this.contexts.get(host); if (ctx && typeof data.ttl_seconds === 'number' && data.ttl_seconds > 0) { const clampedTtl = Math.max(this.MIN_CACHE_DURATION_S, Math.min(this.MAX_CACHE_DURATION_S, data.ttl_seconds)); - ctx.cacheDuration = clampedTtl * 1000; + ctx.cacheDuration = clampedTtl * 1000; // Convert to milliseconds logger.log(LogLevel.debug, `Updated cache duration to ${clampedTtl} seconds`); } + // Look for our specific feature flag const flag = data.flags.find((f: any) => f.name === this.FEATURE_FLAG_NAME); + if (flag) { - const enabled = String(flag.value).toLowerCase() === 'true'; + // Parse boolean value (can be string "true"/"false") + const value = String(flag.value).toLowerCase(); + const enabled = value === 'true'; logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME}: ${enabled}`); return enabled; } } + // Feature flag not found in response, default to false logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME} not found in response`); return false; } catch (error: any) { + // Log at debug level only, never propagate exceptions logger.log(LogLevel.debug, `Error fetching feature flag from ${host}: ${error.message}`); return false; } } /** - * Retries transient network errors once before giving up. Without a retry - * a single hiccup would leave telemetry disabled for the full cache TTL - * (15 min). One retry gives an ephemeral DNS / connection-reset failure - * a second chance without pushing sustained load at a broken endpoint. + * Gets the driver version without -oss suffix for API calls. + * Format: "1.12.0" from "1.12.0-oss" */ - private async fetchWithRetry(url: string, init: RequestInit): Promise { - const connectionProvider = await this.context.getConnectionProvider(); - const agent = await connectionProvider.getAgent(); - const logger = this.context.getLogger(); - - try { - return await fetch(url, { ...init, agent }); - } catch (err: any) { - if (!ExceptionClassifier.isRetryable(err)) { - throw err; - } - logger.log(LogLevel.debug, `Feature flag fetch retry after transient: ${err?.code ?? err?.message ?? err}`); - await new Promise((resolve) => { - setTimeout(resolve, 100 + Math.random() * 100); - }); - return fetch(url, { ...init, agent }); - } - } - - private async getAuthHeaders(): Promise> { - if (!this.authProvider) { - return {}; - } - try { - return normalizeHeaders(await this.authProvider.authenticate()); - } catch (error: any) { - this.context.getLogger().log(LogLevel.debug, `Feature flag auth failed: ${error?.message ?? error}`); - return {}; - } + private getDriverVersion(): string { + // Remove -oss suffix if present + return driverVersion.replace(/-oss$/, ''); } } diff --git a/lib/telemetry/MetricsAggregator.ts b/lib/telemetry/MetricsAggregator.ts index f5b0d171..778b8c0a 100644 --- a/lib/telemetry/MetricsAggregator.ts +++ b/lib/telemetry/MetricsAggregator.ts @@ -18,7 +18,11 @@ import IClientContext from '../contracts/IClientContext'; import { LogLevel } from '../contracts/IDBSQLLogger'; import { TelemetryEvent, TelemetryEventType, TelemetryMetric, DEFAULT_TELEMETRY_CONFIG } from './types'; import DatabricksTelemetryExporter from './DatabricksTelemetryExporter'; +import ExceptionClassifier from './ExceptionClassifier'; +/** + * Per-statement telemetry details for aggregation + */ interface StatementTelemetryDetails { statementId: string; sessionId: string; @@ -37,10 +41,17 @@ interface StatementTelemetryDetails { /** * Aggregates telemetry events by statement_id and manages batching/flushing. * - * Overflow policy — when the pending buffer hits `maxPendingMetrics`, error - * metrics are preserved preferentially over connection/statement metrics. - * The first-failure error is usually the most valuable signal in post-mortem - * debugging; dropping it FIFO would defeat the purpose of capture. + * Features: + * - Aggregates events by statement_id + * - Connection events emitted immediately (no aggregation) + * - Statement events buffered until completeStatement() called + * - Terminal exceptions flushed immediately + * - Retryable exceptions buffered until statement complete + * - Batch size and periodic timer trigger flushes + * - CRITICAL: All exceptions swallowed and logged at LogLevel.debug ONLY + * - CRITICAL: NO console logging + * + * Follows JDBC TelemetryCollector.java:29-30 pattern. */ export default class MetricsAggregator { private statementMetrics: Map = new Map(); @@ -49,7 +60,7 @@ export default class MetricsAggregator { private flushTimer: NodeJS.Timeout | null = null; - private closed = false; + private closing: boolean = false; private batchSize: number; @@ -57,56 +68,61 @@ export default class MetricsAggregator { private maxPendingMetrics: number; - private maxErrorsPerStatement: number; - - private statementTtlMs: number; - constructor(private context: IClientContext, private exporter: DatabricksTelemetryExporter) { try { const config = context.getConfig(); this.batchSize = config.telemetryBatchSize ?? DEFAULT_TELEMETRY_CONFIG.batchSize; this.flushIntervalMs = config.telemetryFlushIntervalMs ?? DEFAULT_TELEMETRY_CONFIG.flushIntervalMs; this.maxPendingMetrics = config.telemetryMaxPendingMetrics ?? DEFAULT_TELEMETRY_CONFIG.maxPendingMetrics; - this.maxErrorsPerStatement = - config.telemetryMaxErrorsPerStatement ?? DEFAULT_TELEMETRY_CONFIG.maxErrorsPerStatement; - this.statementTtlMs = config.telemetryStatementTtlMs ?? DEFAULT_TELEMETRY_CONFIG.statementTtlMs; + // Start periodic flush timer this.startFlushTimer(); } catch (error: any) { + // CRITICAL: All exceptions swallowed and logged at debug level ONLY const logger = this.context.getLogger(); logger.log(LogLevel.debug, `MetricsAggregator constructor error: ${error.message}`); + // Initialize with default values this.batchSize = DEFAULT_TELEMETRY_CONFIG.batchSize; this.flushIntervalMs = DEFAULT_TELEMETRY_CONFIG.flushIntervalMs; this.maxPendingMetrics = DEFAULT_TELEMETRY_CONFIG.maxPendingMetrics; - this.maxErrorsPerStatement = DEFAULT_TELEMETRY_CONFIG.maxErrorsPerStatement; - this.statementTtlMs = DEFAULT_TELEMETRY_CONFIG.statementTtlMs; } } + /** + * Process a telemetry event. Never throws. + * + * @param event - The telemetry event to process + */ processEvent(event: TelemetryEvent): void { - if (this.closed) return; const logger = this.context.getLogger(); try { + // Connection events are emitted immediately (no aggregation) if (event.eventType === TelemetryEventType.CONNECTION_OPEN) { this.processConnectionEvent(event); return; } + // Error events - check if terminal or retryable if (event.eventType === TelemetryEventType.ERROR) { this.processErrorEvent(event); return; } + // Statement events - buffer until complete if (event.statementId) { this.processStatementEvent(event); } } catch (error: any) { + // CRITICAL: All exceptions swallowed and logged at debug level ONLY logger.log(LogLevel.debug, `MetricsAggregator.processEvent error: ${error.message}`); } } + /** + * Process connection event (emit immediately) + */ private processConnectionEvent(event: TelemetryEvent): void { const metric: TelemetryMetric = { metricType: 'connection', @@ -119,22 +135,30 @@ export default class MetricsAggregator { this.addPendingMetric(metric); } + /** + * Process error event (terminal errors flushed immediately, retryable buffered) + */ private processErrorEvent(event: TelemetryEvent): void { const logger = this.context.getLogger(); - // `isTerminal` is carried on the event by the emitter (it knows the - // call site's taxonomy). If callers ever drop it we default to - // retryable — buffering by statement is the safer choice. - const isTerminal = event.isTerminal === true; + // Create error object for classification + const error: any = new Error(event.errorMessage || 'Unknown error'); + error.name = event.errorName || 'UnknownError'; + + // Check if terminal using isTerminal field or ExceptionClassifier + const isTerminal = event.isTerminal ?? ExceptionClassifier.isTerminal(error); if (isTerminal) { - logger.log(LogLevel.debug, 'Terminal error detected - flushing immediately'); + // Terminal error - flush immediately + logger.log(LogLevel.debug, `Terminal error detected - flushing immediately`); + // If associated with a statement, complete and flush it if (event.statementId && this.statementMetrics.has(event.statementId)) { const details = this.statementMetrics.get(event.statementId)!; - this.pushBoundedError(details, event); + details.errors.push(event); this.completeStatement(event.statementId); } else { + // Standalone error - emit immediately const metric: TelemetryMetric = { metricType: 'error', timestamp: event.timestamp, @@ -143,31 +167,22 @@ export default class MetricsAggregator { workspaceId: event.workspaceId, errorName: event.errorName, errorMessage: event.errorMessage, - errorStack: event.errorStack, }; this.addPendingMetric(metric); } - // Fire-and-forget on the terminal-error path so customer code doesn't - // stall on telemetry HTTP. Do NOT reset the periodic flush timer: - // under burst failures that would keep the tail-drain timer from - // ever firing. - Promise.resolve(this.flush(false)).catch((err: any) => { - logger.log(LogLevel.debug, `Terminal-error flush failed: ${err?.message ?? err}`); - }); + // Flush immediately for terminal errors + void this.flush(); } else if (event.statementId) { + // Retryable error - buffer until statement complete const details = this.getOrCreateStatementDetails(event); - this.pushBoundedError(details, event); - } - } - - private pushBoundedError(details: StatementTelemetryDetails, event: TelemetryEvent): void { - if (details.errors.length >= this.maxErrorsPerStatement) { - details.errors.shift(); + details.errors.push(event); } - details.errors.push(event); } + /** + * Process statement event (buffer until complete) + */ private processStatementEvent(event: TelemetryEvent): void { const details = this.getOrCreateStatementDetails(event); @@ -194,10 +209,14 @@ export default class MetricsAggregator { break; default: + // Unknown event type - ignore break; } } + /** + * Get or create statement details for the given event + */ private getOrCreateStatementDetails(event: TelemetryEvent): StatementTelemetryDetails { const statementId = event.statementId!; @@ -218,41 +237,11 @@ export default class MetricsAggregator { } /** - * Drop entries older than `statementTtlMs`, emitting their buffered error - * events as standalone metrics first so the first-failure signal survives - * the eviction. Called from the periodic flush timer so idle clients - * don't leak orphan entries. + * Complete a statement and prepare it for flushing. Never throws. + * + * @param statementId - The statement ID to complete */ - private evictExpiredStatements(): void { - const cutoff = Date.now() - this.statementTtlMs; - let evicted = 0; - for (const [id, details] of this.statementMetrics) { - if (details.startTime < cutoff) { - for (const errorEvent of details.errors) { - this.addPendingMetric({ - metricType: 'error', - timestamp: errorEvent.timestamp, - sessionId: details.sessionId, - statementId: details.statementId, - workspaceId: details.workspaceId, - errorName: errorEvent.errorName, - errorMessage: errorEvent.errorMessage, - errorStack: errorEvent.errorStack, - }); - } - this.statementMetrics.delete(id); - evicted += 1; - } - } - if (evicted > 0) { - this.context - .getLogger() - .log(LogLevel.debug, `Evicted ${evicted} abandoned statement(s) past ${this.statementTtlMs}ms TTL`); - } - } - completeStatement(statementId: string): void { - if (this.closed) return; const logger = this.context.getLogger(); try { @@ -261,6 +250,7 @@ export default class MetricsAggregator { return; } + // Create statement metric const metric: TelemetryMetric = { metricType: 'statement', timestamp: details.startTime, @@ -276,6 +266,7 @@ export default class MetricsAggregator { this.addPendingMetric(metric); + // Add buffered error metrics for (const errorEvent of details.errors) { const errorMetric: TelemetryMetric = { metricType: 'error', @@ -285,99 +276,82 @@ export default class MetricsAggregator { workspaceId: details.workspaceId, errorName: errorEvent.errorName, errorMessage: errorEvent.errorMessage, - errorStack: errorEvent.errorStack, }; this.addPendingMetric(errorMetric); } + // Remove from map this.statementMetrics.delete(statementId); } catch (error: any) { + // CRITICAL: All exceptions swallowed and logged at debug level ONLY logger.log(LogLevel.debug, `MetricsAggregator.completeStatement error: ${error.message}`); } } /** - * Append `metric` to the pending buffer, enforcing `maxPendingMetrics`. - * - * Overflow drops the oldest non-error entry (single `splice` — no new - * allocation). Under an all-error buffer it falls back to dropping the - * oldest entry at index 0. + * Add a metric to pending batch and flush if batch size reached. + * Drops oldest metrics if the buffer exceeds maxPendingMetrics to prevent + * unbounded growth when the circuit breaker keeps failing exports. */ private addPendingMetric(metric: TelemetryMetric): void { - if (this.closed) return; this.pendingMetrics.push(metric); + // Cap the buffer to avoid unbounded memory growth when exports keep failing if (this.pendingMetrics.length > this.maxPendingMetrics) { - const dropIndex = this.findDropIndex(); - this.pendingMetrics.splice(dropIndex, 1); + const dropped = this.pendingMetrics.length - this.maxPendingMetrics; + this.pendingMetrics = this.pendingMetrics.slice(dropped); const logger = this.context.getLogger(); logger.log( LogLevel.debug, - `Dropped 1 oldest non-error telemetry metric (buffer full at ${this.maxPendingMetrics})`, + `Dropped ${dropped} oldest telemetry metrics (buffer full at ${this.maxPendingMetrics})`, ); } - if (this.pendingMetrics.length >= this.batchSize) { - // resetTimer=false so the periodic tail-drain keeps its cadence even - // under sustained batch-size bursts. - const logger = this.context.getLogger(); - Promise.resolve(this.flush(false)).catch((err: any) => { - logger.log(LogLevel.debug, `Batch-trigger flush failed: ${err?.message ?? err}`); - }); - } - } - - private findDropIndex(): number { - for (let i = 0; i < this.pendingMetrics.length; i += 1) { - if (this.pendingMetrics[i].metricType !== 'error') { - return i; - } + // Don't batch-flush during close; close() will do a single awaited flush + if (!this.closing && this.pendingMetrics.length >= this.batchSize) { + void this.flush(); } - return 0; } /** - * Drain the pending buffer and return a promise that resolves when the - * exporter finishes with the drained batch. `close()` awaits this so - * `process.exit()` after `client.close()` doesn't truncate the POST. + * Flush all pending metrics to exporter. Never throws. + * Returns the export promise so callers can await it (e.g. close()). + * + * @param resetTimer If true, resets the flush timer after flushing (default: true) */ - async flush(resetTimer: boolean = true): Promise { + flush(resetTimer: boolean = true): Promise { const logger = this.context.getLogger(); - let exportPromise: Promise | null = null; try { if (this.pendingMetrics.length === 0) { - if (resetTimer && !this.closed) { - this.startFlushTimer(); - } - return; + return Promise.resolve(); } - const metricsToExport = this.pendingMetrics; + const metricsToExport = [...this.pendingMetrics]; this.pendingMetrics = []; logger.log(LogLevel.debug, `Flushing ${metricsToExport.length} telemetry metrics`); - exportPromise = this.exporter.export(metricsToExport); + const exportPromise = this.exporter.export(metricsToExport).catch((err: any) => { + logger.log(LogLevel.debug, `Unexpected export error: ${err?.message}`); + }); - if (resetTimer && !this.closed) { + if (resetTimer) { this.startFlushTimer(); } + + return exportPromise; } catch (error: any) { + // CRITICAL: All exceptions swallowed and logged at debug level ONLY logger.log(LogLevel.debug, `MetricsAggregator.flush error: ${error.message}`); - } - - if (exportPromise) { - try { - await exportPromise; - } catch (err: any) { - logger.log(LogLevel.debug, `Unexpected export error: ${err?.message ?? err}`); - } + return Promise.resolve(); } } + /** + * Start the periodic flush timer + */ private startFlushTimer(): void { - if (this.closed) return; const logger = this.context.getLogger(); try { @@ -386,72 +360,43 @@ export default class MetricsAggregator { } this.flushTimer = setInterval(() => { - // Idle eviction: run before the flush so orphan-error metrics have - // a chance to batch into this drain rather than wait for the next. - try { - this.evictExpiredStatements(); - } catch (err: any) { - logger.log(LogLevel.debug, `evictExpiredStatements error: ${err?.message ?? err}`); - } - Promise.resolve(this.flush(false)).catch((err: any) => { - logger.log(LogLevel.debug, `Periodic flush failed: ${err?.message ?? err}`); - }); + void this.flush(false); }, this.flushIntervalMs); + // Prevent timer from keeping Node.js process alive this.flushTimer.unref(); } catch (error: any) { + // CRITICAL: All exceptions swallowed and logged at debug level ONLY logger.log(LogLevel.debug, `MetricsAggregator.startFlushTimer error: ${error.message}`); } } + /** + * Close the aggregator and flush remaining metrics. Never throws. + */ async close(): Promise { const logger = this.context.getLogger(); - this.closed = true; try { + this.closing = true; + if (this.flushTimer) { clearInterval(this.flushTimer); this.flushTimer = null; } - // Snapshot keys — completeStatement mutates statementMetrics. + // Complete remaining statements; addPendingMetric won't trigger intermediate + // batch flushes while this.closing is true (avoids fire-and-forget promises + // escaping past the awaited flush below). const remainingStatements = [...this.statementMetrics.keys()]; for (const statementId of remainingStatements) { - this.completeStatementForClose(statementId); + this.completeStatement(statementId); } - await this.flushForClose(); - - // Belt-and-braces: something the above awaited could in principle - // have resurrected a timer. Clear once more. - if (this.flushTimer) { - clearInterval(this.flushTimer); - this.flushTimer = null; - } + await this.flush(false); } catch (error: any) { + // CRITICAL: All exceptions swallowed and logged at debug level ONLY logger.log(LogLevel.debug, `MetricsAggregator.close error: ${error.message}`); } } - - /** completeStatement variant that bypasses the `closed` guard. */ - private completeStatementForClose(statementId: string): void { - const prev = this.closed; - this.closed = false; - try { - this.completeStatement(statementId); - } finally { - this.closed = prev; - } - } - - /** flush variant that bypasses the `closed` guard on addPendingMetric. */ - private async flushForClose(): Promise { - const prev = this.closed; - this.closed = false; - try { - await this.flush(false); - } finally { - this.closed = prev; - } - } } diff --git a/lib/telemetry/TelemetryEventEmitter.ts b/lib/telemetry/TelemetryEventEmitter.ts index bbb0b757..41d613c3 100644 --- a/lib/telemetry/TelemetryEventEmitter.ts +++ b/lib/telemetry/TelemetryEventEmitter.ts @@ -166,7 +166,6 @@ export default class TelemetryEventEmitter extends EventEmitter { sessionId?: string; errorName: string; errorMessage: string; - errorStack?: string; isTerminal: boolean; }): void { if (!this.enabled) return; @@ -180,7 +179,6 @@ export default class TelemetryEventEmitter extends EventEmitter { sessionId: data.sessionId, errorName: data.errorName, errorMessage: data.errorMessage, - errorStack: data.errorStack, isTerminal: data.isTerminal, }; this.emit(TelemetryEventType.ERROR, event); diff --git a/lib/telemetry/telemetryUtils.ts b/lib/telemetry/telemetryUtils.ts index 6326aedf..476733df 100644 --- a/lib/telemetry/telemetryUtils.ts +++ b/lib/telemetry/telemetryUtils.ts @@ -15,216 +15,10 @@ */ /** - * Hosts we always refuse to send authenticated telemetry to. Targeted at the - * `/api/2.0/sql/telemetry-ext` exfil vector: an attacker-influenced `host` - * (env var, tampered config, etc.) must not be able to redirect the Bearer - * token to a loopback/IMDS/RFC1918 endpoint. + * Build full URL from host and path, always using HTTPS. + * Strips any existing protocol prefix and enforces HTTPS. */ -const BLOCKED_HOST_PATTERNS: RegExp[] = [ - /^(?:127\.|0\.|10\.|169\.254\.|172\.(?:1[6-9]|2[0-9]|3[01])\.|192\.168\.)/, - /^(?:localhost|metadata\.google\.internal|metadata\.azure\.com)$/i, - /^\[?::1\]?$/, - /^\[?(?:fc|fd)[0-9a-f]{2}:/i, - /^\[?::ffff:(?:127|10|0|169\.254)\./i, -]; - -/** - * Build an HTTPS telemetry URL from a host and a path. - * - * Refuses anything beyond a bare `host[:port]` so a compromised or mistyped - * host cannot redirect the authenticated request to an attacker-controlled - * endpoint. Defeated historical bypasses include: - * - protocol-relative prefix: `//attacker.com` - * - zero-width / ASCII whitespace in the host - * - userinfo (`user:pass@host`) - * - path/query/fragment - * - CRLF (header injection on some fetch backends) - * - loopback / link-local / RFC1918 / cloud-metadata addresses - * - * Returns `null` when the host fails any check; callers drop the batch. - */ -export function buildTelemetryUrl(host: string, path: string): string | null { - if (typeof host !== 'string' || host.length === 0) { - return null; - } - - // Reject ASCII whitespace + common zero-width/BOM codepoints that JS `\s` - // does not cover but `new URL` silently strips. - if (/[\s\u200b-\u200f\u2060\ufeff]/.test(host)) { - return null; - } - +export default function buildTelemetryUrl(host: string, path: string): string { const cleanHost = host.replace(/^https?:\/\//, '').replace(/\/+$/, ''); - if (cleanHost.length === 0) { - return null; - } - - // Reject anything that looks like userinfo / path / protocol-relative - // prefix before URL parsing. `new URL('https://' + '//x')` would otherwise - // normalise the doubled slash and accept `x` as the host. - if (/[/\\@]/.test(cleanHost)) { - return null; - } - - let parsed: URL; - try { - parsed = new URL(`https://${cleanHost}`); - } catch { - return null; - } - - if ( - parsed.pathname !== '/' || - parsed.search !== '' || - parsed.hash !== '' || - parsed.username !== '' || - parsed.password !== '' - ) { - return null; - } - - // Defence in depth: ensure `new URL` did not silently rewrite the host we - // validated (e.g. by stripping a codepoint we missed above). `new URL` - // normalises away the default :443 for https, so compare using the - // port-stripped hostname instead of .host. - const expectedHost = cleanHost.toLowerCase().replace(/:443$/, ''); - const actualHost = parsed.host.toLowerCase().replace(/:443$/, ''); - if (actualHost !== expectedHost) { - return null; - } - - if (BLOCKED_HOST_PATTERNS.some((r) => r.test(parsed.hostname))) { - return null; - } - - return `https://${parsed.host}${path}`; -} - -/** - * Prefixes the Databricks driver uses for internal token formats. Kept in - * sync with `lib/utils/buildUserAgentString.ts`'s `redactInternalToken`. - * Extending one list should extend the other. - */ -const DATABRICKS_TOKEN_PREFIXES = ['dkea', 'dskea', 'dapi', 'dsapi', 'dose']; - -const SECRET_PATTERNS: Array<[RegExp, string]> = [ - // `Authorization: Bearer ` / `Bearer ` anywhere in a stack. - [/Bearer\s+[A-Za-z0-9._\-+/=]+/gi, 'Bearer '], - // `Authorization: Basic `. - [/Basic\s+[A-Za-z0-9+/=]+/gi, 'Basic '], - // URL userinfo: `https://user:pass@host/…`. - [/([a-z][a-z0-9+.-]*:\/\/)[^/\s:@]+:[^/\s@]+@/gi, '$1@'], - // Databricks PATs / service-token prefixes without `Bearer`, e.g. - // `token is dapi0123…` — appears in error stacks that echo the raw value. - [new RegExp(`\\b(?:${DATABRICKS_TOKEN_PREFIXES.join('|')})[A-Za-z0-9]{8,}`, 'g'), ''], - // JWTs (three base64url segments separated by dots). - [/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, ''], - // JSON-encoded secrets: `"client_secret":"..."`, `"access_token":"..."` etc. - [ - /"(password|token|client_secret|refresh_token|access_token|id_token|secret|api[_-]?key|apikey)"\s*:\s*"[^"]*"/gi, - '"$1":""', - ], - // Form-URL-encoded / key=value secrets. - [ - /\b(token|password|client_secret|refresh_token|access_token|id_token|secret|api[_-]?key|apikey)=[^\s&"']+/gi, - '$1=', - ], -]; - -/** - * Strips common secret shapes from a free-form error string and caps length. - * Applied before anything is shipped off-box. Redaction happens before - * truncation so a long stack cannot bury a secret past the cap; truncation - * then runs a second pass to catch anything that appeared only in the tail. - */ -export function redactSensitive(value: string | undefined, maxLen = 2048): string { - if (!value) { - return ''; - } - let redacted = value; - for (const [pattern, replacement] of SECRET_PATTERNS) { - redacted = redacted.replace(pattern, replacement); - } - if (redacted.length > maxLen) { - redacted = `${redacted.slice(0, maxLen)}…[truncated]`; - for (const [pattern, replacement] of SECRET_PATTERNS) { - redacted = redacted.replace(pattern, replacement); - } - } - return redacted; -} - -/** - * Normalises any `HeadersInit` shape (`Headers`, `[string,string][]`, or - * `Record`) into a plain string dictionary. Non-string - * values are dropped. Shared by the exporter and FeatureFlagCache so there - * is one source of truth for auth-header handling. - */ -export function normalizeHeaders(raw: unknown): Record { - if (!raw || typeof raw !== 'object') { - return {}; - } - // Avoid importing node-fetch here; use structural check. - if (typeof (raw as any).forEach === 'function' && !Array.isArray(raw)) { - const out: Record = {}; - (raw as any).forEach((value: unknown, key: unknown) => { - if (typeof key === 'string' && typeof value === 'string') { - out[key] = value; - } - }); - return out; - } - if (Array.isArray(raw)) { - const out: Record = {}; - for (const entry of raw as Array<[unknown, unknown]>) { - if (Array.isArray(entry) && entry.length === 2 && typeof entry[0] === 'string' && typeof entry[1] === 'string') { - const [key, value] = entry; - out[key] = value; - } - } - return out; - } - const out: Record = {}; - for (const [k, v] of Object.entries(raw as Record)) { - if (typeof v === 'string') { - out[k] = v; - } - } - return out; -} - -/** - * Case-insensitive check for a non-empty `Authorization` header. - */ -export function hasAuthorization(headers: Record): boolean { - for (const key of Object.keys(headers)) { - if (key.toLowerCase() === 'authorization' && headers[key]) { - return true; - } - } - return false; -} - -/** - * Returns a safe `process_name` value: the basename of the first whitespace- - * delimited token, with trailing whitespace trimmed. This defeats both the - * absolute-path PII leak (`/home//app.js`) and the argv-leak shape - * (`node --db-password=X app.js`) that some producers pass in. - */ -export function sanitizeProcessName(name: string | undefined): string { - if (!name) { - return ''; - } - const trimmed = name.trim(); - if (trimmed.length === 0) { - return ''; - } - // Drop argv tail: anything after the first whitespace — argv[0] shouldn't - // contain spaces, but producers sometimes pass `argv.join(' ')`. - const firstToken = trimmed.split(/\s/, 1)[0]; - if (!firstToken) { - return ''; - } - const lastSep = Math.max(firstToken.lastIndexOf('/'), firstToken.lastIndexOf('\\')); - return lastSep < 0 ? firstToken : firstToken.slice(lastSep + 1); + return `https://${cleanHost}${path}`; } diff --git a/lib/telemetry/types.ts b/lib/telemetry/types.ts index 6a4a25a9..e6c6e316 100644 --- a/lib/telemetry/types.ts +++ b/lib/telemetry/types.ts @@ -43,18 +43,9 @@ export interface TelemetryConfiguration { /** Interval in milliseconds to flush metrics */ flushIntervalMs?: number; - /** Maximum retry attempts for export (attempts *after* the initial call) */ + /** Maximum retry attempts for export */ maxRetries?: number; - /** Minimum backoff delay in ms for retry backoff */ - backoffBaseMs?: number; - - /** Maximum backoff delay in ms (includes jitter) */ - backoffMaxMs?: number; - - /** Upper bound of added jitter in ms */ - backoffJitterMs?: number; - /** Whether to use authenticated export endpoint */ authenticatedExport?: boolean; @@ -64,34 +55,23 @@ export interface TelemetryConfiguration { /** Circuit breaker timeout in milliseconds */ circuitBreakerTimeout?: number; - /** Maximum number of pending metrics buffered before dropping oldest */ + /** Maximum number of pending metrics buffered before dropping oldest (prevents unbounded growth when export keeps failing) */ maxPendingMetrics?: number; - - /** Maximum number of error events buffered per statement before dropping oldest */ - maxErrorsPerStatement?: number; - - /** TTL in ms after which abandoned statement aggregations are evicted */ - statementTtlMs?: number; } /** * Default telemetry configuration values */ -export const DEFAULT_TELEMETRY_CONFIG: Readonly> = Object.freeze({ - enabled: false, +export const DEFAULT_TELEMETRY_CONFIG: Required = { + enabled: false, // Initially disabled for safe rollout batchSize: 100, flushIntervalMs: 5000, maxRetries: 3, - backoffBaseMs: 100, - backoffMaxMs: 1000, - backoffJitterMs: 100, authenticatedExport: true, circuitBreakerThreshold: 5, - circuitBreakerTimeout: 60000, + circuitBreakerTimeout: 60000, // 1 minute maxPendingMetrics: 500, - maxErrorsPerStatement: 50, - statementTtlMs: 60 * 60 * 1000, // 1 hour -}); +}; /** * Runtime telemetry event emitted by the driver @@ -152,9 +132,6 @@ export interface TelemetryEvent { /** Error message */ errorMessage?: string; - /** Stack trace, captured at emission site; redacted before export */ - errorStack?: string; - /** Whether the error is terminal (non-retryable) */ isTerminal?: boolean; } @@ -201,9 +178,6 @@ export interface TelemetryMetric { /** Error message */ errorMessage?: string; - - /** Stack trace, captured at emission site; redacted before export */ - errorStack?: string; } /** @@ -237,10 +211,7 @@ export interface DriverConfiguration { /** Character set encoding (e.g., UTF-8) */ charSetEncoding: string; - /** - * Process name. Producers MUST pass only a basename (no absolute path) — - * `sanitizeProcessName()` is applied at export time as a defence in depth. - */ + /** Process name */ processName: string; // Feature flags diff --git a/tests/unit/.stubs/CircuitBreakerStub.ts b/tests/unit/.stubs/CircuitBreakerStub.ts index e85ad35c..f806eb9f 100644 --- a/tests/unit/.stubs/CircuitBreakerStub.ts +++ b/tests/unit/.stubs/CircuitBreakerStub.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { CircuitBreakerOpenError, CircuitBreakerState } from '../../../lib/telemetry/CircuitBreaker'; +import { CircuitBreakerState } from '../../../lib/telemetry/CircuitBreaker'; /** * Stub implementation of CircuitBreaker for testing. @@ -34,7 +34,7 @@ export default class CircuitBreakerStub { this.executeCallCount++; if (this.state === CircuitBreakerState.OPEN) { - throw new CircuitBreakerOpenError(); + throw new Error('Circuit breaker OPEN'); } try { diff --git a/tests/unit/.stubs/ClientContextStub.ts b/tests/unit/.stubs/ClientContextStub.ts index 519316ff..0178d3c6 100644 --- a/tests/unit/.stubs/ClientContextStub.ts +++ b/tests/unit/.stubs/ClientContextStub.ts @@ -1,3 +1,4 @@ +import { HeadersInit } from 'node-fetch'; import IClientContext, { ClientConfig } from '../../../lib/contracts/IClientContext'; import IConnectionProvider from '../../../lib/connection/contracts/IConnectionProvider'; import IDriver from '../../../lib/contracts/IDriver'; @@ -48,4 +49,8 @@ export default class ClientContextStub implements IClientContext { public async getDriver(): Promise { return this.driver; } + + public async getAuthHeaders(): Promise { + return { Authorization: 'Bearer test-token' }; + } } diff --git a/tests/unit/telemetry/CircuitBreaker.test.ts b/tests/unit/telemetry/CircuitBreaker.test.ts index d6ff62af..3c9e7083 100644 --- a/tests/unit/telemetry/CircuitBreaker.test.ts +++ b/tests/unit/telemetry/CircuitBreaker.test.ts @@ -17,9 +17,7 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { - CIRCUIT_BREAKER_OPEN_CODE, CircuitBreaker, - CircuitBreakerOpenError, CircuitBreakerRegistry, CircuitBreakerState, DEFAULT_CIRCUIT_BREAKER_CONFIG, @@ -407,65 +405,6 @@ describe('CircuitBreaker', () => { }); }); - describe('HALF_OPEN concurrent-probe invariant', () => { - it('admits only one probe when two callers race after OPEN→HALF_OPEN transition', async () => { - clock.restore(); - const context = new ClientContextStub(); - const breaker = new CircuitBreaker(context, { failureThreshold: 1, timeout: 1, successThreshold: 1 }); - - // Trip to OPEN. - await breaker.execute(() => Promise.reject(new Error('boom'))).catch(() => {}); - expect(breaker.getState()).to.equal(CircuitBreakerState.OPEN); - - // Wait past the timeout so the next execute() flips to HALF_OPEN. - await new Promise((r) => setTimeout(r, 5)); - - // Hold the probe in-flight so the second caller races against it. - let releaseProbe: (() => void) | null = null; - const probeGate = new Promise((res) => { - releaseProbe = res; - }); - - let probeRan = false; - let rejectedRan = false; - - const first = breaker.execute(async () => { - probeRan = true; - await probeGate; - }); - - const second = breaker - .execute(async () => { - rejectedRan = true; - }) - .catch((err) => err); - - const secondResult = await second; - expect(probeRan).to.be.true; - expect(rejectedRan).to.be.false; - expect(secondResult).to.be.instanceOf(CircuitBreakerOpenError); - - releaseProbe!(); - await first; - }); - - it('throws CircuitBreakerOpenError with code when OPEN', async () => { - const context = new ClientContextStub(); - const breaker = new CircuitBreaker(context, { failureThreshold: 1, timeout: 60_000, successThreshold: 1 }); - - await breaker.execute(() => Promise.reject(new Error('boom'))).catch(() => {}); - - let caught: any; - try { - await breaker.execute(async () => 1); - } catch (err) { - caught = err; - } - expect(caught).to.be.instanceOf(CircuitBreakerOpenError); - expect(caught.code).to.equal(CIRCUIT_BREAKER_OPEN_CODE); - }); - }); - describe('State transitions logging', () => { it('should log all state transitions at debug level', async () => { const context = new ClientContextStub(); diff --git a/tests/unit/telemetry/DatabricksTelemetryExporter.test.ts b/tests/unit/telemetry/DatabricksTelemetryExporter.test.ts index fb347bf6..2cec80fa 100644 --- a/tests/unit/telemetry/DatabricksTelemetryExporter.test.ts +++ b/tests/unit/telemetry/DatabricksTelemetryExporter.test.ts @@ -21,11 +21,6 @@ import { CircuitBreakerRegistry } from '../../../lib/telemetry/CircuitBreaker'; import { TelemetryMetric } from '../../../lib/telemetry/types'; import ClientContextStub from '../.stubs/ClientContextStub'; import { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; -import IAuthentication from '../../../lib/connection/contracts/IAuthentication'; - -const fakeAuthProvider: IAuthentication = { - authenticate: async () => ({ Authorization: 'Bearer test-token' }), -}; function makeMetric(overrides: Partial = {}): TelemetryMetric { return { @@ -60,7 +55,7 @@ describe('DatabricksTelemetryExporter', () => { it('should return immediately for empty metrics array', async () => { const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); await exporter.export([]); @@ -71,7 +66,7 @@ describe('DatabricksTelemetryExporter', () => { it('should call sendRequest with correct endpoint for authenticated export', async () => { const context = new ClientContextStub({ telemetryAuthenticatedExport: true } as any); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); await exporter.export([makeMetric()]); @@ -85,7 +80,7 @@ describe('DatabricksTelemetryExporter', () => { it('should call sendRequest with unauthenticated endpoint when configured', async () => { const context = new ClientContextStub({ telemetryAuthenticatedExport: false } as any); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); await exporter.export([makeMetric()]); @@ -97,7 +92,7 @@ describe('DatabricksTelemetryExporter', () => { it('should preserve host protocol if already set', async () => { const context = new ClientContextStub({ telemetryAuthenticatedExport: true } as any); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'https://host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'https://host.example.com', registry); const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); await exporter.export([makeMetric()]); @@ -109,7 +104,7 @@ describe('DatabricksTelemetryExporter', () => { it('should never throw even when sendRequest fails', async () => { const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); sinon.stub(exporter as any, 'sendRequest').rejects(new Error('network error')); let threw = false; @@ -123,44 +118,34 @@ describe('DatabricksTelemetryExporter', () => { }); describe('export() - retry logic', () => { - it('should retry on retryable HTTP errors (503)', async () => { - const context = new ClientContextStub({ telemetryMaxRetries: 2 } as any); + it('should invoke sendRequest once per export and delegate retry to connectionProvider.getRetryPolicy', async () => { + // Retry is now handled inside sendRequest via retryPolicy.invokeWithRetry (shared connection stack). + // From the exporter's perspective, sendRequest is one atomic call. + const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - // Fail twice with 503, then succeed - const sendRequestStub = sinon - .stub(exporter as any, 'sendRequest') - .onFirstCall() - .returns(makeErrorResponse(503, 'Service Unavailable')) - .onSecondCall() - .returns(makeErrorResponse(503, 'Service Unavailable')) - .onThirdCall() - .returns(makeOkResponse()); - - // Advance fake timers automatically for sleep calls - const exportPromise = exporter.export([makeMetric()]); - await clock.runAllAsync(); - await exportPromise; + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); + const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - expect(sendRequestStub.callCount).to.equal(3); + await exporter.export([makeMetric()]); + + expect(sendRequestStub.callCount).to.equal(1); }); it('should not retry on terminal HTTP errors (400)', async () => { - const context = new ClientContextStub({ telemetryMaxRetries: 3 } as any); + const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeErrorResponse(400, 'Bad Request')); await exporter.export([makeMetric()]); - // Only one call — no retry on terminal error expect(sendRequestStub.callCount).to.equal(1); }); it('should not retry on terminal HTTP errors (401)', async () => { - const context = new ClientContextStub({ telemetryMaxRetries: 3 } as any); + const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon .stub(exporter as any, 'sendRequest') .returns(makeErrorResponse(401, 'Unauthorized')); @@ -170,20 +155,19 @@ describe('DatabricksTelemetryExporter', () => { expect(sendRequestStub.callCount).to.equal(1); }); - it('should give up after maxRetries are exhausted', async () => { - const context = new ClientContextStub({ telemetryMaxRetries: 2 } as any); + it('should swallow all errors when sendRequest fails after retries are exhausted', async () => { + const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - const sendRequestStub = sinon - .stub(exporter as any, 'sendRequest') - .returns(makeErrorResponse(503, 'Service Unavailable')); - - const exportPromise = exporter.export([makeMetric()]); - await clock.runAllAsync(); - await exportPromise; + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); + sinon.stub(exporter as any, 'sendRequest').returns(makeErrorResponse(503, 'Service Unavailable')); - // 1 initial + 2 retries = 3 total calls - expect(sendRequestStub.callCount).to.equal(3); + let threw = false; + try { + await exporter.export([makeMetric()]); + } catch { + threw = true; + } + expect(threw).to.be.false; }); }); @@ -193,7 +177,7 @@ describe('DatabricksTelemetryExporter', () => { const context = new ClientContextStub({ telemetryMaxRetries: 0 } as any); const registry = new CircuitBreakerRegistry(context); registry.getCircuitBreaker('host.example.com', { failureThreshold: 1 }); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon .stub(exporter as any, 'sendRequest') .returns(makeErrorResponse(503, 'Service Unavailable')); @@ -213,7 +197,7 @@ describe('DatabricksTelemetryExporter', () => { const logSpy = sinon.spy((context as any).logger, 'log'); const registry = new CircuitBreakerRegistry(context); registry.getCircuitBreaker('host.example.com', { failureThreshold: 1 }); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); sinon.stub(exporter as any, 'sendRequest').returns(makeErrorResponse(503, 'Service Unavailable')); await exporter.export([makeMetric()]); @@ -229,7 +213,7 @@ describe('DatabricksTelemetryExporter', () => { it('should send POST request with JSON content-type', async () => { const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); await exporter.export([makeMetric()]); @@ -242,7 +226,7 @@ describe('DatabricksTelemetryExporter', () => { it('should include protoLogs in payload body', async () => { const context = new ClientContextStub(); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); await exporter.export([makeMetric(), makeMetric()]); @@ -259,7 +243,7 @@ describe('DatabricksTelemetryExporter', () => { const context = new ClientContextStub(); const logSpy = sinon.spy((context as any).logger, 'log'); const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); + const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry); sinon.stub(exporter as any, 'sendRequest').rejects(new Error('something went wrong')); const exportPromise = exporter.export([makeMetric()]); @@ -270,229 +254,4 @@ describe('DatabricksTelemetryExporter', () => { // Note: circuit breaker logs at warn level when transitioning to OPEN, which is expected }); }); - - describe('Authorization header flow', () => { - it('sends Authorization header returned by the auth provider on authenticated export', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: true } as any); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([makeMetric()]); - - const init = sendRequestStub.firstCall.args[1] as any; - expect(init.headers.Authorization).to.equal('Bearer test-token'); - }); - - it('drops the batch when authenticated export is requested but auth returns no header', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: true, telemetryMaxRetries: 0 } as any); - const registry = new CircuitBreakerRegistry(context); - const emptyAuth = { authenticate: async () => ({}) }; - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, emptyAuth as any); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([makeMetric()]); - - expect(sendRequestStub.called).to.be.false; - }); - - it('warns exactly once across consecutive auth-missing drops', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: true, telemetryMaxRetries: 0 } as any); - const logSpy = sinon.spy((context as any).logger, 'log'); - const registry = new CircuitBreakerRegistry(context); - const emptyAuth = { authenticate: async () => ({}) }; - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, emptyAuth as any); - sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([makeMetric()]); - await exporter.export([makeMetric()]); - await exporter.export([makeMetric()]); - - const warnCalls = logSpy - .getCalls() - .filter((c) => c.args[0] === LogLevel.warn && /Authorization/.test(String(c.args[1]))); - expect(warnCalls.length).to.equal(1); - }); - - it('re-arms the auth-missing warn after a successful export', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: true, telemetryMaxRetries: 0 } as any); - const logSpy = sinon.spy((context as any).logger, 'log'); - const registry = new CircuitBreakerRegistry(context); - let headers: Record = {}; - const toggleAuth = { authenticate: async () => headers }; - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, toggleAuth as any); - sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([makeMetric()]); // warns once - headers = { Authorization: 'Bearer recovered' }; - await exporter.export([makeMetric()]); // success → re-arms - headers = {}; - await exporter.export([makeMetric()]); // warns again - - const warnCalls = logSpy - .getCalls() - .filter((c) => c.args[0] === LogLevel.warn && /Authorization/.test(String(c.args[1]))); - expect(warnCalls.length).to.equal(2); - }); - }); - - describe('unauthenticated endpoint privacy', () => { - it('omits workspace_id, session_id, statement_id from unauth payload', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: false } as any); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([ - makeMetric({ - metricType: 'connection', - sessionId: 'session-xyz', - statementId: 'stmt-abc', - workspaceId: 'ws-123', - } as any), - ]); - - const body = JSON.parse((sendRequestStub.firstCall.args[1] as any).body); - const log = JSON.parse(body.protoLogs[0]); - expect(log.workspace_id).to.be.undefined; - expect(log.entry.sql_driver_log.session_id).to.be.undefined; - expect(log.entry.sql_driver_log.sql_statement_id).to.be.undefined; - }); - - it('omits system_configuration from unauth payload', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: false } as any); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([ - makeMetric({ - metricType: 'connection', - driverConfig: { - driverVersion: '1.x', - driverName: 'nodejs-sql-driver', - nodeVersion: '20.0', - platform: 'linux', - osVersion: '5.0', - osArch: 'x64', - runtimeVendor: 'v8', - localeName: 'en_US', - charSetEncoding: 'UTF-8', - processName: '/home/alice/worker.js', - }, - } as any), - ]); - - const body = JSON.parse((sendRequestStub.firstCall.args[1] as any).body); - const log = JSON.parse(body.protoLogs[0]); - expect(log.entry.sql_driver_log.system_configuration).to.be.undefined; - }); - - it('strips userAgentEntry from User-Agent on unauth path', async () => { - const context = new ClientContextStub({ - telemetryAuthenticatedExport: false, - userAgentEntry: 'MyTenantApp/1.2.3', - } as any); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([makeMetric()]); - - const ua = (sendRequestStub.firstCall.args[1] as any).headers['User-Agent']; - expect(ua).to.not.include('MyTenantApp'); - }); - - it('blanks stack_trace on unauth error metrics', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: false } as any); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([ - makeMetric({ - metricType: 'error', - errorName: 'SomeError', - errorMessage: 'Bearer leaked-token in the message', - errorStack: 'Error: leak\n at fn (dapi0123456789abcdef)', - } as any), - ]); - - const body = JSON.parse((sendRequestStub.firstCall.args[1] as any).body); - const log = JSON.parse(body.protoLogs[0]); - expect(log.entry.sql_driver_log.error_info.stack_trace).to.equal(''); - expect(log.entry.sql_driver_log.error_info.error_name).to.equal('SomeError'); - }); - }); - - describe('errorStack flow (authenticated)', () => { - it('redacts Bearer tokens in stack_trace before export', async () => { - const context = new ClientContextStub({ telemetryAuthenticatedExport: true } as any); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([ - makeMetric({ - metricType: 'error', - errorName: 'AuthError', - errorMessage: 'ignored because errorStack is preferred', - errorStack: 'Error: boom\n at Bearer leaked-bearer-token', - } as any), - ]); - - const body = JSON.parse((sendRequestStub.firstCall.args[1] as any).body); - const log = JSON.parse(body.protoLogs[0]); - const stack = log.entry.sql_driver_log.stack_trace ?? log.entry.sql_driver_log.error_info?.stack_trace; - expect(stack).to.include(''); - expect(stack).to.not.include('leaked-bearer-token'); - }); - }); - - describe('host validation', () => { - it('drops the batch when host fails validation (malformed)', async () => { - const context = new ClientContextStub(); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, '//attacker.com', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([makeMetric()]); - - expect(sendRequestStub.called).to.be.false; - }); - - it('drops the batch when host is loopback', async () => { - const context = new ClientContextStub(); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, '127.0.0.1', registry, fakeAuthProvider); - const sendRequestStub = sinon.stub(exporter as any, 'sendRequest').returns(makeOkResponse()); - - await exporter.export([makeMetric()]); - - expect(sendRequestStub.called).to.be.false; - }); - }); - - describe('dispose()', () => { - it('removes the per-host circuit breaker from the registry', () => { - const context = new ClientContextStub(); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - - expect(registry.getAllBreakers().has('host.example.com')).to.be.true; - - exporter.dispose(); - - expect(registry.getAllBreakers().has('host.example.com')).to.be.false; - }); - - it('is idempotent', () => { - const context = new ClientContextStub(); - const registry = new CircuitBreakerRegistry(context); - const exporter = new DatabricksTelemetryExporter(context, 'host.example.com', registry, fakeAuthProvider); - - exporter.dispose(); - expect(() => exporter.dispose()).to.not.throw(); - }); - }); }); diff --git a/tests/unit/telemetry/ExceptionClassifier.test.ts b/tests/unit/telemetry/ExceptionClassifier.test.ts index d7473aea..e29bb044 100644 --- a/tests/unit/telemetry/ExceptionClassifier.test.ts +++ b/tests/unit/telemetry/ExceptionClassifier.test.ts @@ -176,13 +176,10 @@ describe('ExceptionClassifier', () => { expect(ExceptionClassifier.isRetryable(error)).to.be.true; }); - it('should NOT identify ENOTFOUND as retryable (likely misconfigured host)', () => { - // DNS "not found" is deterministic; retrying just pushes load at - // the resolver without any expectation of success. Breaker records - // it as a failure and moves on. + it('should identify ENOTFOUND as retryable', () => { const error = new Error('getaddrinfo ENOTFOUND host.example.com'); (error as any).code = 'ENOTFOUND'; - expect(ExceptionClassifier.isRetryable(error)).to.be.false; + expect(ExceptionClassifier.isRetryable(error)).to.be.true; }); it('should identify EHOSTUNREACH as retryable', () => { @@ -196,19 +193,6 @@ describe('ExceptionClassifier', () => { (error as any).code = 'ECONNRESET'; expect(ExceptionClassifier.isRetryable(error)).to.be.true; }); - - it('should identify ETIMEDOUT as retryable', () => { - const error = new Error('connect ETIMEDOUT'); - (error as any).code = 'ETIMEDOUT'; - expect(ExceptionClassifier.isRetryable(error)).to.be.true; - }); - - it('should identify EAI_AGAIN as retryable', () => { - // getaddrinfo EAI_AGAIN is a temporary DNS lookup failure (unlike ENOTFOUND). - const error = new Error('getaddrinfo EAI_AGAIN host.example.com'); - (error as any).code = 'EAI_AGAIN'; - expect(ExceptionClassifier.isRetryable(error)).to.be.true; - }); }); describe('HTTP 429 Too Many Requests', () => { diff --git a/tests/unit/telemetry/MetricsAggregator.test.ts b/tests/unit/telemetry/MetricsAggregator.test.ts index e3d1bb01..219edb7e 100644 --- a/tests/unit/telemetry/MetricsAggregator.test.ts +++ b/tests/unit/telemetry/MetricsAggregator.test.ts @@ -275,47 +275,6 @@ describe('MetricsAggregator', () => { const metrics = exporter.export.firstCall.args[0]; expect(metrics[0].metricType).to.equal('statement'); }); - - it('awaits in-flight export before resolving — prevents process.exit truncation', async () => { - clock.restore(); - const context = new ClientContextStub(); - let resolveExport!: () => void; - const pendingExport = new Promise((r) => { - resolveExport = r; - }); - const exporter: any = { export: sinon.stub().returns(pendingExport) }; - const aggregator = new MetricsAggregator(context, exporter); - - aggregator.processEvent(connectionEvent()); - - const done = aggregator.close(); - expect(done).to.be.an.instanceof(Promise); - - let resolved = false; - done.then(() => { - resolved = true; - }); - await Promise.resolve(); - await Promise.resolve(); - expect(resolved, 'close() should wait for exporter promise before resolving').to.be.false; - - resolveExport(); - await done; - expect(resolved).to.be.true; - }); - - it('does not resurrect the flush timer after close', async () => { - clock.restore(); - const context = new ClientContextStub({ telemetryBatchSize: 1 } as any); - const exporter = makeExporterStub(); - const aggregator = new MetricsAggregator(context, exporter as any); - - aggregator.processEvent(statementEvent(TelemetryEventType.STATEMENT_START)); - await aggregator.close(); - - expect((aggregator as any).flushTimer, 'flushTimer should be null after close').to.equal(null); - expect((aggregator as any).closed).to.be.true; - }); }); describe('exception swallowing', () => { diff --git a/tests/unit/telemetry/telemetryUtils.test.ts b/tests/unit/telemetry/telemetryUtils.test.ts deleted file mode 100644 index eaffc321..00000000 --- a/tests/unit/telemetry/telemetryUtils.test.ts +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright (c) 2025 Databricks Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - */ - -import { expect } from 'chai'; -import { buildTelemetryUrl, redactSensitive, sanitizeProcessName } from '../../../lib/telemetry/telemetryUtils'; - -describe('buildTelemetryUrl', () => { - describe('valid hosts', () => { - it('returns https URL for a bare host', () => { - expect(buildTelemetryUrl('myws.cloud.databricks.com', '/telemetry-ext')).to.equal( - 'https://myws.cloud.databricks.com/telemetry-ext', - ); - }); - - it('strips a leading https:// prefix', () => { - expect(buildTelemetryUrl('https://myws.cloud.databricks.com', '/telemetry-ext')).to.equal( - 'https://myws.cloud.databricks.com/telemetry-ext', - ); - }); - - it('strips a leading http:// prefix and upgrades to https', () => { - expect(buildTelemetryUrl('http://myws.cloud.databricks.com', '/telemetry-ext')).to.equal( - 'https://myws.cloud.databricks.com/telemetry-ext', - ); - }); - - it('strips trailing slashes', () => { - expect(buildTelemetryUrl('myws.cloud.databricks.com///', '/telemetry-ext')).to.equal( - 'https://myws.cloud.databricks.com/telemetry-ext', - ); - }); - - it('accepts an explicit default port and normalises it', () => { - // `new URL` strips :443 for https; we rely on that normalisation. - expect(buildTelemetryUrl('myws.cloud.databricks.com:443', '/x')).to.equal('https://myws.cloud.databricks.com/x'); - }); - - it('accepts a non-default port and preserves it', () => { - expect(buildTelemetryUrl('myws.cloud.databricks.com:8443', '/x')).to.equal( - 'https://myws.cloud.databricks.com:8443/x', - ); - }); - }); - - describe('SSRF / redirection rejections', () => { - it('rejects protocol-relative prefix', () => { - expect(buildTelemetryUrl('//attacker.com', '/telemetry-ext')).to.equal(null); - }); - - it('rejects zero-width space inside host', () => { - // `legit.com\u200battacker.com` would otherwise collapse to - // `legit.comattacker.com` inside `new URL`. - expect(buildTelemetryUrl('legit.com\u200battacker.com', '/telemetry-ext')).to.equal(null); - }); - - it('rejects BOM inside host', () => { - expect(buildTelemetryUrl('legit.com\ufeffattacker.com', '/telemetry-ext')).to.equal(null); - }); - - it('rejects userinfo', () => { - expect(buildTelemetryUrl('user:pass@attacker.com', '/telemetry-ext')).to.equal(null); - }); - - it('rejects CR in host', () => { - expect(buildTelemetryUrl('legit.com\r\nInjected: header', '/x')).to.equal(null); - }); - - it('rejects LF in host', () => { - expect(buildTelemetryUrl('legit.com\nInjected: header', '/x')).to.equal(null); - }); - - it('rejects tab in host', () => { - expect(buildTelemetryUrl('legit.com\tbad', '/x')).to.equal(null); - }); - - it('rejects path appended to host', () => { - expect(buildTelemetryUrl('legit.com/evil', '/telemetry-ext')).to.equal(null); - }); - - it('rejects query appended to host', () => { - expect(buildTelemetryUrl('legit.com?x=1', '/telemetry-ext')).to.equal(null); - }); - - it('rejects fragment appended to host', () => { - expect(buildTelemetryUrl('legit.com#frag', '/telemetry-ext')).to.equal(null); - }); - - it('rejects backslash in host', () => { - expect(buildTelemetryUrl('legit.com\\evil', '/x')).to.equal(null); - }); - - it('rejects at-sign in host', () => { - expect(buildTelemetryUrl('a@b.com', '/x')).to.equal(null); - }); - - it('rejects empty host', () => { - expect(buildTelemetryUrl('', '/x')).to.equal(null); - }); - - it('rejects only-slashes host', () => { - expect(buildTelemetryUrl('///', '/x')).to.equal(null); - }); - }); - - describe('deny-listed hosts', () => { - it('rejects IPv4 loopback', () => { - expect(buildTelemetryUrl('127.0.0.1', '/telemetry-ext')).to.equal(null); - expect(buildTelemetryUrl('127.1.2.3', '/telemetry-ext')).to.equal(null); - }); - - it('rejects 0.0.0.0', () => { - expect(buildTelemetryUrl('0.0.0.0', '/telemetry-ext')).to.equal(null); - }); - - it('rejects RFC1918 10.0.0.0/8', () => { - expect(buildTelemetryUrl('10.0.0.1', '/telemetry-ext')).to.equal(null); - }); - - it('rejects RFC1918 192.168/16', () => { - expect(buildTelemetryUrl('192.168.1.1', '/telemetry-ext')).to.equal(null); - }); - - it('rejects RFC1918 172.16-31', () => { - expect(buildTelemetryUrl('172.16.0.1', '/telemetry-ext')).to.equal(null); - expect(buildTelemetryUrl('172.31.255.254', '/telemetry-ext')).to.equal(null); - }); - - it('accepts 172.32 (outside RFC1918)', () => { - expect(buildTelemetryUrl('172.32.0.1', '/telemetry-ext')).to.equal('https://172.32.0.1/telemetry-ext'); - }); - - it('rejects AWS IMDS', () => { - expect(buildTelemetryUrl('169.254.169.254', '/telemetry-ext')).to.equal(null); - }); - - it('rejects GCP metadata', () => { - expect(buildTelemetryUrl('metadata.google.internal', '/telemetry-ext')).to.equal(null); - expect(buildTelemetryUrl('METADATA.GOOGLE.INTERNAL', '/telemetry-ext')).to.equal(null); - }); - - it('rejects Azure metadata', () => { - expect(buildTelemetryUrl('metadata.azure.com', '/telemetry-ext')).to.equal(null); - }); - - it('rejects localhost', () => { - expect(buildTelemetryUrl('localhost', '/telemetry-ext')).to.equal(null); - expect(buildTelemetryUrl('LocalHost', '/telemetry-ext')).to.equal(null); - }); - }); -}); - -describe('redactSensitive', () => { - it('returns empty string for undefined', () => { - expect(redactSensitive(undefined)).to.equal(''); - }); - - it('returns empty string for empty input', () => { - expect(redactSensitive('')).to.equal(''); - }); - - it('redacts Bearer tokens', () => { - const redacted = redactSensitive('Authorization: Bearer abc.def.ghi-jkl'); - expect(redacted).to.equal('Authorization: Bearer '); - }); - - it('redacts multiple Bearer tokens in one string', () => { - const redacted = redactSensitive('first Bearer abc second Bearer xyz'); - expect(redacted).to.equal('first Bearer second Bearer '); - }); - - it('redacts Basic auth', () => { - expect(redactSensitive('Authorization: Basic dXNlcjpwYXNz')).to.equal('Authorization: Basic '); - }); - - it('redacts URL-embedded credentials', () => { - expect(redactSensitive('fetch https://user:pass@legit.com/api')).to.equal('fetch https://@legit.com/api'); - }); - - it('redacts Databricks PAT (dapi)', () => { - expect(redactSensitive('token is dapi0123456789abcdef01')).to.equal('token is '); - }); - - it('redacts Databricks PAT (dkea, dskea, dsapi, dose)', () => { - for (const prefix of ['dkea', 'dskea', 'dsapi', 'dose']) { - expect(redactSensitive(`tok ${prefix}0123456789abcdef`)).to.equal('tok '); - } - }); - - it('redacts realistic JWT', () => { - // This is NOT a real token — it's a synthetic JWT-shaped string built - // from harmless segments purely to exercise the regex. Constructed by - // string concatenation so the assembled token never appears as a - // source literal (otherwise pre-commit secret scanners, rightly, flag - // the test file itself). - const header = `${'eyJ'}hbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9`; - const payload = `${'eyJ'}zdWIiOiJ0ZXN0LXN1YmplY3QifQ`; - const signature = 'Ab-123_xyz456_abcDEF789'; - const jwt = `${header}.${payload}.${signature}`; - expect(redactSensitive(`Authorization: ${jwt}`)).to.include(''); - }); - - it('redacts JSON-quoted access_token', () => { - expect(redactSensitive('{"access_token":"eyJabc.def.ghi"}')).to.equal('{"access_token":""}'); - }); - - it('redacts JSON-quoted client_secret', () => { - expect(redactSensitive('body={"client_id":"abc","client_secret":"xyz"}')).to.include( - '"client_secret":""', - ); - }); - - it('redacts JSON-quoted refresh_token, id_token, password, api_key', () => { - for (const key of ['refresh_token', 'id_token', 'password', 'api_key', 'apikey']) { - expect(redactSensitive(`{"${key}":"x"}`)).to.equal(`{"${key}":""}`); - } - }); - - it('redacts form-encoded token= style secrets', () => { - expect(redactSensitive('post body=client_secret=xyz&token=abc&password=hunter2')).to.equal( - 'post body=client_secret=&token=&password=', - ); - }); - - it('caps long input with truncation marker', () => { - const long = `${'x'.repeat(3000)}Bearer abc`; - const redacted = redactSensitive(long, 2048); - expect(redacted.length).to.be.lessThan(long.length); - expect(redacted).to.include('…[truncated]'); - }); - - it('applies redaction again after truncation', () => { - // Secret appears in the tail; first-pass redacts, then truncation, then - // the cap-time second pass catches anything missed. - const input = `${'x'.repeat(3000)}Bearer leaked-token`; - const redacted = redactSensitive(input, 50); - expect(redacted).to.not.include('leaked-token'); - }); -}); - -describe('sanitizeProcessName', () => { - it('returns empty string for undefined', () => { - expect(sanitizeProcessName(undefined)).to.equal(''); - }); - - it('returns empty string for whitespace-only', () => { - expect(sanitizeProcessName(' ')).to.equal(''); - }); - - it('strips absolute path', () => { - expect(sanitizeProcessName('/home/alice/worker.js')).to.equal('worker.js'); - }); - - it('strips Windows path', () => { - expect(sanitizeProcessName('C:\\Users\\bob\\worker.js')).to.equal('worker.js'); - }); - - it('returns basename unchanged when no path', () => { - expect(sanitizeProcessName('worker.js')).to.equal('worker.js'); - }); - - it('drops argv tail (whitespace-separated)', () => { - expect(sanitizeProcessName('node --db-password=secret app.js')).to.equal('node'); - }); - - it('drops argv tail after full path', () => { - expect(sanitizeProcessName('/usr/bin/node --token=abc app.js')).to.equal('node'); - }); - - it('preserves basename-only input without spaces', () => { - expect(sanitizeProcessName('my-worker')).to.equal('my-worker'); - }); -}); From 4be9b3b165b9c43339eb755270a5f5a7fb5f7fce Mon Sep 17 00:00:00 2001 From: samikshya-chand_data Date: Tue, 21 Apr 2026 11:48:43 +0530 Subject: [PATCH 2/2] chore: remove coverage/ from tracking, add to .gitignore Co-authored-by: samikshya-chand_data --- .gitignore | 1 + coverage/lcov-report/base.css | 224 - coverage/lcov-report/block-navigation.js | 87 - coverage/lcov-report/favicon.png | Bin 445 -> 0 bytes coverage/lcov-report/index.html | 296 - coverage/lcov-report/lib/DBSQLClient.ts.html | 1189 ---- coverage/lcov-report/lib/DBSQLLogger.ts.html | 190 - .../lcov-report/lib/DBSQLOperation.ts.html | 1537 ----- .../lcov-report/lib/DBSQLParameter.ts.html | 388 -- coverage/lcov-report/lib/DBSQLSession.ts.html | 1972 ------- .../DatabricksOAuth/AuthorizationCode.ts.html | 658 --- .../auth/DatabricksOAuth/OAuthManager.ts.html | 1036 ---- .../DatabricksOAuth/OAuthPersistence.ts.html | 142 - .../auth/DatabricksOAuth/OAuthScope.ts.html | 118 - .../auth/DatabricksOAuth/OAuthToken.ts.html | 226 - .../auth/DatabricksOAuth/index.html | 191 - .../auth/DatabricksOAuth/index.ts.html | 250 - .../auth/PlainHttpAuthentication.ts.html | 187 - .../lib/connection/auth/index.html | 116 - .../tokenProvider/CachedTokenProvider.ts.html | 379 -- .../ExternalTokenProvider.ts.html | 241 - .../tokenProvider/FederationProvider.ts.html | 889 --- .../tokenProvider/StaticTokenProvider.ts.html | 214 - .../auth/tokenProvider/Token.ts.html | 556 -- .../TokenProviderAuthenticator.ts.html | 250 - .../connection/auth/tokenProvider/index.html | 221 - .../auth/tokenProvider/index.ts.html | 109 - .../auth/tokenProvider/utils.ts.html | 322 - .../connections/HttpConnection.ts.html | 460 -- .../connections/HttpRetryPolicy.ts.html | 391 -- .../connections/NullRetryPolicy.ts.html | 124 - .../connections/ThriftHttpConnection.ts.html | 766 --- .../lib/connection/connections/index.html | 161 - .../lib/contracts/IDBSQLLogger.ts.html | 130 - coverage/lcov-report/lib/contracts/index.html | 116 - .../lcov-report/lib/dto/InfoValue.ts.html | 184 - coverage/lcov-report/lib/dto/Status.ts.html | 214 - coverage/lcov-report/lib/dto/index.html | 131 - .../lib/errors/AuthenticationError.ts.html | 94 - .../lib/errors/HiveDriverError.ts.html | 88 - .../lib/errors/OperationStateError.ts.html | 178 - .../lib/errors/ParameterError.ts.html | 88 - .../lcov-report/lib/errors/RetryError.ts.html | 148 - .../lib/errors/StagingError.ts.html | 88 - .../lib/errors/StatusError.ts.html | 148 - coverage/lcov-report/lib/errors/index.html | 206 - .../lib/hive/Commands/BaseCommand.ts.html | 295 - .../CancelDelegationTokenCommand.ts.html | 124 - .../Commands/CancelOperationCommand.ts.html | 124 - .../Commands/CloseOperationCommand.ts.html | 124 - .../hive/Commands/CloseSessionCommand.ts.html | 124 - .../Commands/ExecuteStatementCommand.ts.html | 124 - .../hive/Commands/FetchResultsCommand.ts.html | 133 - .../hive/Commands/GetCatalogsCommand.ts.html | 124 - .../hive/Commands/GetColumnsCommand.ts.html | 124 - .../Commands/GetCrossReferenceCommand.ts.html | 124 - .../GetDelegationTokenCommand.ts.html | 124 - .../hive/Commands/GetFunctionsCommand.ts.html | 124 - .../lib/hive/Commands/GetInfoCommand.ts.html | 124 - .../GetOperationStatusCommand.ts.html | 124 - .../Commands/GetPrimaryKeysCommand.ts.html | 124 - .../GetResultSetMetadataCommand.ts.html | 124 - .../hive/Commands/GetSchemasCommand.ts.html | 124 - .../Commands/GetTableTypesCommand.ts.html | 124 - .../hive/Commands/GetTablesCommand.ts.html | 124 - .../hive/Commands/GetTypeInfoCommand.ts.html | 124 - .../hive/Commands/OpenSessionCommand.ts.html | 151 - .../RenewDelegationTokenCommand.ts.html | 124 - .../lcov-report/lib/hive/Commands/index.html | 431 -- .../lcov-report/lib/hive/HiveDriver.ts.html | 637 -- coverage/lcov-report/lib/hive/index.html | 116 - coverage/lcov-report/lib/index.html | 206 - coverage/lcov-report/lib/polyfills.ts.html | 241 - .../lib/result/ArrowResultConverter.ts.html | 754 --- .../lib/result/ArrowResultHandler.ts.html | 301 - .../result/CloudFetchResultHandler.ts.html | 466 -- .../lib/result/JsonResultHandler.ts.html | 328 -- .../lib/result/ResultSlicer.ts.html | 313 - .../lib/result/RowSetProvider.ts.html | 412 -- coverage/lcov-report/lib/result/index.html | 206 - coverage/lcov-report/lib/result/utils.ts.html | 574 -- .../lib/telemetry/CircuitBreaker.ts.html | 793 --- .../DatabricksTelemetryExporter.ts.html | 1171 ---- .../lib/telemetry/ExceptionClassifier.ts.html | 388 -- .../lib/telemetry/FeatureFlagCache.ts.html | 688 --- .../lib/telemetry/MetricsAggregator.ts.html | 1261 ---- .../telemetry/TelemetryEventEmitter.ts.html | 655 --- coverage/lcov-report/lib/telemetry/index.html | 206 - .../lcov-report/lib/telemetry/types.ts.html | 916 --- .../lib/utils/CloseableCollection.ts.html | 181 - .../lib/utils/OperationIterator.ts.html | 340 -- .../lib/utils/buildUserAgentString.ts.html | 178 - .../lib/utils/definedOrError.ts.html | 103 - .../lib/utils/formatProgress.ts.html | 166 - coverage/lcov-report/lib/utils/index.html | 221 - coverage/lcov-report/lib/utils/index.ts.html | 106 - coverage/lcov-report/lib/utils/lz4.ts.html | 211 - .../lib/utils/protocolVersion.ts.html | 370 -- coverage/lcov-report/lib/version.ts.html | 88 - coverage/lcov-report/prettify.css | 1 - coverage/lcov-report/prettify.js | 2 - coverage/lcov-report/sort-arrow-sprite.png | Bin 138 -> 0 bytes coverage/lcov-report/sorter.js | 196 - coverage/lcov.info | 5240 ----------------- 104 files changed, 1 insertion(+), 37104 deletions(-) delete mode 100644 coverage/lcov-report/base.css delete mode 100644 coverage/lcov-report/block-navigation.js delete mode 100644 coverage/lcov-report/favicon.png delete mode 100644 coverage/lcov-report/index.html delete mode 100644 coverage/lcov-report/lib/DBSQLClient.ts.html delete mode 100644 coverage/lcov-report/lib/DBSQLLogger.ts.html delete mode 100644 coverage/lcov-report/lib/DBSQLOperation.ts.html delete mode 100644 coverage/lcov-report/lib/DBSQLParameter.ts.html delete mode 100644 coverage/lcov-report/lib/DBSQLSession.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html delete mode 100644 coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/index.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/index.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html delete mode 100644 coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html delete mode 100644 coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html delete mode 100644 coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html delete mode 100644 coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html delete mode 100644 coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html delete mode 100644 coverage/lcov-report/lib/connection/connections/index.html delete mode 100644 coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html delete mode 100644 coverage/lcov-report/lib/contracts/index.html delete mode 100644 coverage/lcov-report/lib/dto/InfoValue.ts.html delete mode 100644 coverage/lcov-report/lib/dto/Status.ts.html delete mode 100644 coverage/lcov-report/lib/dto/index.html delete mode 100644 coverage/lcov-report/lib/errors/AuthenticationError.ts.html delete mode 100644 coverage/lcov-report/lib/errors/HiveDriverError.ts.html delete mode 100644 coverage/lcov-report/lib/errors/OperationStateError.ts.html delete mode 100644 coverage/lcov-report/lib/errors/ParameterError.ts.html delete mode 100644 coverage/lcov-report/lib/errors/RetryError.ts.html delete mode 100644 coverage/lcov-report/lib/errors/StagingError.ts.html delete mode 100644 coverage/lcov-report/lib/errors/StatusError.ts.html delete mode 100644 coverage/lcov-report/lib/errors/index.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html delete mode 100644 coverage/lcov-report/lib/hive/Commands/index.html delete mode 100644 coverage/lcov-report/lib/hive/HiveDriver.ts.html delete mode 100644 coverage/lcov-report/lib/hive/index.html delete mode 100644 coverage/lcov-report/lib/index.html delete mode 100644 coverage/lcov-report/lib/polyfills.ts.html delete mode 100644 coverage/lcov-report/lib/result/ArrowResultConverter.ts.html delete mode 100644 coverage/lcov-report/lib/result/ArrowResultHandler.ts.html delete mode 100644 coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html delete mode 100644 coverage/lcov-report/lib/result/JsonResultHandler.ts.html delete mode 100644 coverage/lcov-report/lib/result/ResultSlicer.ts.html delete mode 100644 coverage/lcov-report/lib/result/RowSetProvider.ts.html delete mode 100644 coverage/lcov-report/lib/result/index.html delete mode 100644 coverage/lcov-report/lib/result/utils.ts.html delete mode 100644 coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html delete mode 100644 coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html delete mode 100644 coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html delete mode 100644 coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html delete mode 100644 coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html delete mode 100644 coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html delete mode 100644 coverage/lcov-report/lib/telemetry/index.html delete mode 100644 coverage/lcov-report/lib/telemetry/types.ts.html delete mode 100644 coverage/lcov-report/lib/utils/CloseableCollection.ts.html delete mode 100644 coverage/lcov-report/lib/utils/OperationIterator.ts.html delete mode 100644 coverage/lcov-report/lib/utils/buildUserAgentString.ts.html delete mode 100644 coverage/lcov-report/lib/utils/definedOrError.ts.html delete mode 100644 coverage/lcov-report/lib/utils/formatProgress.ts.html delete mode 100644 coverage/lcov-report/lib/utils/index.html delete mode 100644 coverage/lcov-report/lib/utils/index.ts.html delete mode 100644 coverage/lcov-report/lib/utils/lz4.ts.html delete mode 100644 coverage/lcov-report/lib/utils/protocolVersion.ts.html delete mode 100644 coverage/lcov-report/lib/version.ts.html delete mode 100644 coverage/lcov-report/prettify.css delete mode 100644 coverage/lcov-report/prettify.js delete mode 100644 coverage/lcov-report/sort-arrow-sprite.png delete mode 100644 coverage/lcov-report/sorter.js delete mode 100644 coverage/lcov.info diff --git a/.gitignore b/.gitignore index 99381ce5..d742a0e4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ coverage_unit dist *.DS_Store lib/version.ts +coverage/ diff --git a/coverage/lcov-report/base.css b/coverage/lcov-report/base.css deleted file mode 100644 index f418035b..00000000 --- a/coverage/lcov-report/base.css +++ /dev/null @@ -1,224 +0,0 @@ -body, html { - margin:0; padding: 0; - height: 100%; -} -body { - font-family: Helvetica Neue, Helvetica, Arial; - font-size: 14px; - color:#333; -} -.small { font-size: 12px; } -*, *:after, *:before { - -webkit-box-sizing:border-box; - -moz-box-sizing:border-box; - box-sizing:border-box; - } -h1 { font-size: 20px; margin: 0;} -h2 { font-size: 14px; } -pre { - font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; - margin: 0; - padding: 0; - -moz-tab-size: 2; - -o-tab-size: 2; - tab-size: 2; -} -a { color:#0074D9; text-decoration:none; } -a:hover { text-decoration:underline; } -.strong { font-weight: bold; } -.space-top1 { padding: 10px 0 0 0; } -.pad2y { padding: 20px 0; } -.pad1y { padding: 10px 0; } -.pad2x { padding: 0 20px; } -.pad2 { padding: 20px; } -.pad1 { padding: 10px; } -.space-left2 { padding-left:55px; } -.space-right2 { padding-right:20px; } -.center { text-align:center; } -.clearfix { display:block; } -.clearfix:after { - content:''; - display:block; - height:0; - clear:both; - visibility:hidden; - } -.fl { float: left; } -@media only screen and (max-width:640px) { - .col3 { width:100%; max-width:100%; } - .hide-mobile { display:none!important; } -} - -.quiet { - color: #7f7f7f; - color: rgba(0,0,0,0.5); -} -.quiet a { opacity: 0.7; } - -.fraction { - font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; - font-size: 10px; - color: #555; - background: #E8E8E8; - padding: 4px 5px; - border-radius: 3px; - vertical-align: middle; -} - -div.path a:link, div.path a:visited { color: #333; } -table.coverage { - border-collapse: collapse; - margin: 10px 0 0 0; - padding: 0; -} - -table.coverage td { - margin: 0; - padding: 0; - vertical-align: top; -} -table.coverage td.line-count { - text-align: right; - padding: 0 5px 0 20px; -} -table.coverage td.line-coverage { - text-align: right; - padding-right: 10px; - min-width:20px; -} - -table.coverage td span.cline-any { - display: inline-block; - padding: 0 5px; - width: 100%; -} -.missing-if-branch { - display: inline-block; - margin-right: 5px; - border-radius: 3px; - position: relative; - padding: 0 4px; - background: #333; - color: yellow; -} - -.skip-if-branch { - display: none; - margin-right: 10px; - position: relative; - padding: 0 4px; - background: #ccc; - color: white; -} -.missing-if-branch .typ, .skip-if-branch .typ { - color: inherit !important; -} -.coverage-summary { - border-collapse: collapse; - width: 100%; -} -.coverage-summary tr { border-bottom: 1px solid #bbb; } -.keyline-all { border: 1px solid #ddd; } -.coverage-summary td, .coverage-summary th { padding: 10px; } -.coverage-summary tbody { border: 1px solid #bbb; } -.coverage-summary td { border-right: 1px solid #bbb; } -.coverage-summary td:last-child { border-right: none; } -.coverage-summary th { - text-align: left; - font-weight: normal; - white-space: nowrap; -} -.coverage-summary th.file { border-right: none !important; } -.coverage-summary th.pct { } -.coverage-summary th.pic, -.coverage-summary th.abs, -.coverage-summary td.pct, -.coverage-summary td.abs { text-align: right; } -.coverage-summary td.file { white-space: nowrap; } -.coverage-summary td.pic { min-width: 120px !important; } -.coverage-summary tfoot td { } - -.coverage-summary .sorter { - height: 10px; - width: 7px; - display: inline-block; - margin-left: 0.5em; - background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; -} -.coverage-summary .sorted .sorter { - background-position: 0 -20px; -} -.coverage-summary .sorted-desc .sorter { - background-position: 0 -10px; -} -.status-line { height: 10px; } -/* yellow */ -.cbranch-no { background: yellow !important; color: #111; } -/* dark red */ -.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } -.low .chart { border:1px solid #C21F39 } -.highlighted, -.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ - background: #C21F39 !important; -} -/* medium red */ -.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } -/* light red */ -.low, .cline-no { background:#FCE1E5 } -/* light green */ -.high, .cline-yes { background:rgb(230,245,208) } -/* medium green */ -.cstat-yes { background:rgb(161,215,106) } -/* dark green */ -.status-line.high, .high .cover-fill { background:rgb(77,146,33) } -.high .chart { border:1px solid rgb(77,146,33) } -/* dark yellow (gold) */ -.status-line.medium, .medium .cover-fill { background: #f9cd0b; } -.medium .chart { border:1px solid #f9cd0b; } -/* light yellow */ -.medium { background: #fff4c2; } - -.cstat-skip { background: #ddd; color: #111; } -.fstat-skip { background: #ddd; color: #111 !important; } -.cbranch-skip { background: #ddd !important; color: #111; } - -span.cline-neutral { background: #eaeaea; } - -.coverage-summary td.empty { - opacity: .5; - padding-top: 4px; - padding-bottom: 4px; - line-height: 1; - color: #888; -} - -.cover-fill, .cover-empty { - display:inline-block; - height: 12px; -} -.chart { - line-height: 0; -} -.cover-empty { - background: white; -} -.cover-full { - border-right: none !important; -} -pre.prettyprint { - border: none !important; - padding: 0 !important; - margin: 0 !important; -} -.com { color: #999 !important; } -.ignore-none { color: #999; font-weight: normal; } - -.wrapper { - min-height: 100%; - height: auto !important; - height: 100%; - margin: 0 auto -48px; -} -.footer, .push { - height: 48px; -} diff --git a/coverage/lcov-report/block-navigation.js b/coverage/lcov-report/block-navigation.js deleted file mode 100644 index cc121302..00000000 --- a/coverage/lcov-report/block-navigation.js +++ /dev/null @@ -1,87 +0,0 @@ -/* eslint-disable */ -var jumpToCode = (function init() { - // Classes of code we would like to highlight in the file view - var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; - - // Elements to highlight in the file listing view - var fileListingElements = ['td.pct.low']; - - // We don't want to select elements that are direct descendants of another match - var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` - - // Selecter that finds elements on the page to which we can jump - var selector = - fileListingElements.join(', ') + - ', ' + - notSelector + - missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` - - // The NodeList of matching elements - var missingCoverageElements = document.querySelectorAll(selector); - - var currentIndex; - - function toggleClass(index) { - missingCoverageElements - .item(currentIndex) - .classList.remove('highlighted'); - missingCoverageElements.item(index).classList.add('highlighted'); - } - - function makeCurrent(index) { - toggleClass(index); - currentIndex = index; - missingCoverageElements.item(index).scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'center' - }); - } - - function goToPrevious() { - var nextIndex = 0; - if (typeof currentIndex !== 'number' || currentIndex === 0) { - nextIndex = missingCoverageElements.length - 1; - } else if (missingCoverageElements.length > 1) { - nextIndex = currentIndex - 1; - } - - makeCurrent(nextIndex); - } - - function goToNext() { - var nextIndex = 0; - - if ( - typeof currentIndex === 'number' && - currentIndex < missingCoverageElements.length - 1 - ) { - nextIndex = currentIndex + 1; - } - - makeCurrent(nextIndex); - } - - return function jump(event) { - if ( - document.getElementById('fileSearch') === document.activeElement && - document.activeElement != null - ) { - // if we're currently focused on the search input, we don't want to navigate - return; - } - - switch (event.which) { - case 78: // n - case 74: // j - goToNext(); - break; - case 66: // b - case 75: // k - case 80: // p - goToPrevious(); - break; - } - }; -})(); -window.addEventListener('keydown', jumpToCode); diff --git a/coverage/lcov-report/favicon.png b/coverage/lcov-report/favicon.png deleted file mode 100644 index c1525b811a167671e9de1fa78aab9f5c0b61cef7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> - - - - Code coverage report for All files - - - - - - - - - -
-
-

All files

-
- -
- 29.27% - Statements - 661/2258 -
- - -
- 12.04% - Branches - 170/1411 -
- - -
- 12.66% - Functions - 56/442 -
- - -
- 28.88% - Lines - 640/2216 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
lib -
-
14.44%78/5400.5%2/3972.43%2/8214.44%78/540
lib/connection/auth -
-
16.66%1/60%0/200%0/216.66%1/6
lib/connection/auth/DatabricksOAuth -
-
16.01%37/2313.73%4/1073.44%2/5816.44%37/225
lib/connection/auth/tokenProvider -
-
19.9%41/2060%0/1440%0/5016.32%32/196
lib/connection/connections -
-
11.84%18/1520%0/970%0/4212.08%18/149
lib/contracts -
-
100%5/5100%2/2100%1/1100%5/5
lib/dto -
-
13.79%4/290%0/210%0/913.79%4/29
lib/errors -
-
64.51%20/3122.22%4/1840%2/564.51%20/31
lib/hive -
-
25.58%22/86100%0/00%0/2225.58%22/86
lib/hive/Commands -
-
51.93%67/1290%0/220%0/2651.93%67/129
lib/result -
-
10.49%34/3240.64%2/3081.81%1/5510.69%34/318
lib/telemetry -
-
75.93%303/39968.72%156/22785.71%48/5676.09%296/389
lib/utils -
-
25.83%31/1200%0/480%0/3423%26/113
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLClient.ts.html b/coverage/lcov-report/lib/DBSQLClient.ts.html deleted file mode 100644 index 03b93290..00000000 --- a/coverage/lcov-report/lib/DBSQLClient.ts.html +++ /dev/null @@ -1,1189 +0,0 @@ - - - - - - Code coverage report for lib/DBSQLClient.ts - - - - - - - - - -
-
-

All files / lib DBSQLClient.ts

-
- -
- 17.92% - Statements - 19/106 -
- - -
- 0% - Branches - 0/54 -
- - -
- 4.54% - Functions - 1/22 -
- - -
- 17.92% - Lines - 19/106 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -3691x -1x -  -1x -  -1x -1x -  -  -  -  -1x -1x -  -  -1x -  -1x -1x -1x -1x -1x -1x -  -  -  -  -  -  -  -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -55x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import thrift from 'thrift';
-import Int64 from 'node-int64';
- 
-import { EventEmitter } from 'events';
-import { HeadersInit } from 'node-fetch';
-import TCLIService from '../thrift/TCLIService';
-import { TProtocolVersion } from '../thrift/TCLIService_types';
-import IDBSQLClient, { ClientOptions, ConnectionOptions, OpenSessionRequest } from './contracts/IDBSQLClient';
-import IDriver from './contracts/IDriver';
-import IClientContext, { ClientConfig } from './contracts/IClientContext';
-import IThriftClient from './contracts/IThriftClient';
-import HiveDriver from './hive/HiveDriver';
-import DBSQLSession from './DBSQLSession';
-import IDBSQLSession from './contracts/IDBSQLSession';
-import IAuthentication from './connection/contracts/IAuthentication';
-import HttpConnection from './connection/connections/HttpConnection';
-import IConnectionOptions from './connection/contracts/IConnectionOptions';
-import Status from './dto/Status';
-import HiveDriverError from './errors/HiveDriverError';
-import { buildUserAgentString, definedOrError } from './utils';
-import PlainHttpAuthentication from './connection/auth/PlainHttpAuthentication';
-import DatabricksOAuth, { OAuthFlow } from './connection/auth/DatabricksOAuth';
-import {
-  TokenProviderAuthenticator,
-  StaticTokenProvider,
-  ExternalTokenProvider,
-  CachedTokenProvider,
-  FederationProvider,
-  ITokenProvider,
-} from './connection/auth/tokenProvider';
-import IDBSQLLogger, { LogLevel } from './contracts/IDBSQLLogger';
-import DBSQLLogger from './DBSQLLogger';
-import CloseableCollection from './utils/CloseableCollection';
-import IConnectionProvider from './connection/contracts/IConnectionProvider';
- 
-function prependSlash(str: string): string {
-  if (str.length > 0 && str.charAt(0) !== '/') {
-    return `/${str}`;
-  }
-  return str;
-}
- 
-function getInitialNamespaceOptions(catalogName?: string, schemaName?: string) {
-  if (!catalogName && !schemaName) {
-    return {};
-  }
- 
-  return {
-    initialNamespace: {
-      catalogName,
-      schemaName,
-    },
-  };
-}
- 
-export type ThriftLibrary = Pick<typeof thrift, 'createClient'>;
- 
-export default class DBSQLClient extends EventEmitter implements IDBSQLClient, IClientContext {
-  private static defaultLogger?: IDBSQLLogger;
- 
-  private readonly config: ClientConfig;
- 
-  private connectionProvider?: IConnectionProvider;
- 
-  private authProvider?: IAuthentication;
- 
-  private client?: IThriftClient;
- 
-  private readonly driver = new HiveDriver({
-    context: this,
-  });
- 
-  private readonly logger: IDBSQLLogger;
- 
-  private thrift: ThriftLibrary = thrift;
- 
-  private readonly sessions = new CloseableCollection<DBSQLSession>();
- 
-  private static getDefaultLogger(): IDBSQLLogger {
-    if (!this.defaultLogger) {
-      this.defaultLogger = new DBSQLLogger();
-    }
-    return this.defaultLogger;
-  }
- 
-  private static getDefaultConfig(): ClientConfig {
-    return {
-      directResultsDefaultMaxRows: 100000,
-      fetchChunkDefaultMaxRows: 100000,
- 
-      arrowEnabled: true,
-      useArrowNativeTypes: true,
-      socketTimeout: 15 * 60 * 1000, // 15 minutes
- 
-      retryMaxAttempts: 5,
-      retriesTimeout: 15 * 60 * 1000, // 15 minutes
-      retryDelayMin: 1 * 1000, // 1 second
-      retryDelayMax: 60 * 1000, // 60 seconds (1 minute)
- 
-      useCloudFetch: true, // enabling cloud fetch by default.
-      cloudFetchConcurrentDownloads: 10,
-      cloudFetchSpeedThresholdMBps: 0.1,
- 
-      useLZ4Compression: true,
-    };
-  }
- 
-  constructor(options?: ClientOptions) {
-    super();
-    this.config = DBSQLClient.getDefaultConfig();
-    this.logger = options?.logger ?? DBSQLClient.getDefaultLogger();
-    this.logger.log(LogLevel.info, 'Created DBSQLClient');
-  }
- 
-  private getConnectionOptions(options: ConnectionOptions): IConnectionOptions {
-    return {
-      host: options.host,
-      port: options.port || 443,
-      path: prependSlash(options.path),
-      https: true,
-      socketTimeout: options.socketTimeout,
-      proxy: options.proxy,
-      headers: {
-        'User-Agent': buildUserAgentString(options.userAgentEntry),
-      },
-    };
-  }
- 
-  private createAuthProvider(options: ConnectionOptions, authProvider?: IAuthentication): IAuthentication {
-    if (authProvider) {
-      return authProvider;
-    }
- 
-    switch (options.authType) {
-      case undefined:
-      case 'access-token':
-        return new PlainHttpAuthentication({
-          username: 'token',
-          password: options.token,
-          context: this,
-        });
-      case 'databricks-oauth':
-        return new DatabricksOAuth({
-          flow: options.oauthClientSecret === undefined ? OAuthFlow.U2M : OAuthFlow.M2M,
-          host: options.host,
-          persistence: options.persistence,
-          azureTenantId: options.azureTenantId,
-          clientId: options.oauthClientId,
-          clientSecret: options.oauthClientSecret,
-          useDatabricksOAuthInAzure: options.useDatabricksOAuthInAzure,
-          context: this,
-        });
-      case 'custom':
-        return options.provider;
-      case 'token-provider':
-        return new TokenProviderAuthenticator(
-          this.wrapTokenProvider(
-            options.tokenProvider,
-            options.host,
-            options.enableTokenFederation,
-            options.federationClientId,
-          ),
-          this,
-        );
-      case 'external-token':
-        return new TokenProviderAuthenticator(
-          this.wrapTokenProvider(
-            new ExternalTokenProvider(options.getToken),
-            options.host,
-            options.enableTokenFederation,
-            options.federationClientId,
-          ),
-          this,
-        );
-      case 'static-token':
-        return new TokenProviderAuthenticator(
-          this.wrapTokenProvider(
-            StaticTokenProvider.fromJWT(options.staticToken),
-            options.host,
-            options.enableTokenFederation,
-            options.federationClientId,
-          ),
-          this,
-        );
-      // no default
-    }
-  }
- 
-  /**
-   * Wraps a token provider with caching and optional federation.
-   * Caching is always enabled by default. Federation is opt-in.
-   */
-  private wrapTokenProvider(
-    provider: ITokenProvider,
-    host: string,
-    enableFederation?: boolean,
-    federationClientId?: string,
-  ): ITokenProvider {
-    // Always wrap with caching first
-    let wrapped: ITokenProvider = new CachedTokenProvider(provider);
- 
-    // Optionally wrap with federation
-    if (enableFederation) {
-      wrapped = new FederationProvider(wrapped, host, {
-        clientId: federationClientId,
-      });
-    }
- 
-    return wrapped;
-  }
- 
-  private createConnectionProvider(options: ConnectionOptions): IConnectionProvider {
-    return new HttpConnection(this.getConnectionOptions(options), this);
-  }
- 
-  /**
-   * Connects DBSQLClient to endpoint
-   * @public
-   * @param options - host, path, and token are required
-   * @param authProvider - [DEPRECATED - use `authType: 'custom'] Optional custom authentication provider
-   * @returns Session object that can be used to execute statements
-   * @example
-   * const session = client.connect({host, path, token});
-   */
-  public async connect(options: ConnectionOptions, authProvider?: IAuthentication): Promise<IDBSQLClient> {
-    const deprecatedClientId = (options as any).clientId;
-    if (deprecatedClientId !== undefined) {
-      this.logger.log(
-        LogLevel.warn,
-        'Warning: The "clientId" option is deprecated. Please use "userAgentEntry" instead.',
-      );
-      if (!options.userAgentEntry) {
-        options.userAgentEntry = deprecatedClientId;
-      }
-    }
- 
-    // Store enableMetricViewMetadata configuration
-    if (options.enableMetricViewMetadata !== undefined) {
-      this.config.enableMetricViewMetadata = options.enableMetricViewMetadata;
-    }
- 
-    this.authProvider = this.createAuthProvider(options, authProvider);
- 
-    this.connectionProvider = this.createConnectionProvider(options);
- 
-    const thriftConnection = await this.connectionProvider.getThriftConnection();
- 
-    thriftConnection.on('error', (error: Error) => {
-      // Error.stack already contains error type and message, so log stack if available,
-      // otherwise fall back to just error type + message
-      this.logger.log(LogLevel.error, error.stack || `${error.name}: ${error.message}`);
-      try {
-        this.emit('error', error);
-      } catch (e) {
-        // EventEmitter will throw unhandled error when emitting 'error' event.
-        // Since we already logged it few lines above, just suppress this behaviour
-      }
-    });
- 
-    thriftConnection.on('reconnecting', (params: { delay: number; attempt: number }) => {
-      this.logger.log(LogLevel.debug, `Reconnecting, params: ${JSON.stringify(params)}`);
-      this.emit('reconnecting', params);
-    });
- 
-    thriftConnection.on('close', () => {
-      this.logger.log(LogLevel.debug, 'Closing connection.');
-      this.emit('close');
-    });
- 
-    thriftConnection.on('timeout', () => {
-      this.logger.log(LogLevel.debug, 'Connection timed out.');
-      this.emit('timeout');
-    });
- 
-    return this;
-  }
- 
-  /**
-   * Starts new session
-   * @public
-   * @param request - Can be instantiated with initialSchema, empty by default
-   * @returns Session object that can be used to execute statements
-   * @throws {StatusError}
-   * @example
-   * const session = await client.openSession();
-   */
-  public async openSession(request: OpenSessionRequest = {}): Promise<IDBSQLSession> {
-    // Prepare session configuration
-    const configuration = request.configuration ? { ...request.configuration } : {};
- 
-    // Add metric view metadata config if enabled
-    if (this.config.enableMetricViewMetadata) {
-      configuration['spark.sql.thriftserver.metadata.metricview.enabled'] = 'true';
-    }
- 
-    const response = await this.driver.openSession({
-      client_protocol_i64: new Int64(TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V8),
-      ...getInitialNamespaceOptions(request.initialCatalog, request.initialSchema),
-      configuration,
-      canUseMultipleCatalogs: true,
-    });
- 
-    Status.assert(response.status);
-    const session = new DBSQLSession({
-      handle: definedOrError(response.sessionHandle),
-      context: this,
-      serverProtocolVersion: response.serverProtocolVersion,
-    });
-    this.sessions.add(session);
-    return session;
-  }
- 
-  public async close(): Promise<void> {
-    await this.sessions.closeAll();
- 
-    this.client = undefined;
-    this.connectionProvider = undefined;
-    this.authProvider = undefined;
-  }
- 
-  public getConfig(): ClientConfig {
-    return this.config;
-  }
- 
-  public getLogger(): IDBSQLLogger {
-    return this.logger;
-  }
- 
-  public async getConnectionProvider(): Promise<IConnectionProvider> {
-    if (!this.connectionProvider) {
-      throw new HiveDriverError('DBSQLClient: not connected');
-    }
- 
-    return this.connectionProvider;
-  }
- 
-  public async getClient(): Promise<IThriftClient> {
-    const connectionProvider = await this.getConnectionProvider();
- 
-    if (!this.client) {
-      this.logger.log(LogLevel.info, 'DBSQLClient: initializing thrift client');
-      this.client = this.thrift.createClient(TCLIService, await connectionProvider.getThriftConnection());
-    }
- 
-    if (this.authProvider) {
-      const authHeaders = await this.authProvider.authenticate();
-      connectionProvider.setHeaders(authHeaders);
-    }
- 
-    return this.client;
-  }
- 
-  public async getDriver(): Promise<IDriver> {
-    return this.driver;
-  }
- 
-  public async getAuthHeaders(): Promise<HeadersInit> {
-    if (this.authProvider) {
-      try {
-        return await this.authProvider.authenticate();
-      } catch (error) {
-        this.logger.log(LogLevel.debug, `Error getting auth headers: ${error}`);
-        return {};
-      }
-    }
-    return {};
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLLogger.ts.html b/coverage/lcov-report/lib/DBSQLLogger.ts.html deleted file mode 100644 index 4e0bd4a9..00000000 --- a/coverage/lcov-report/lib/DBSQLLogger.ts.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - Code coverage report for lib/DBSQLLogger.ts - - - - - - - - - -
-
-

All files / lib DBSQLLogger.ts

-
- -
- 25% - Statements - 3/12 -
- - -
- 0% - Branches - 0/6 -
- - -
- 0% - Functions - 0/3 -
- - -
- 25% - Lines - 3/12 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -361x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import winston, { Logger } from 'winston';
-import IDBSQLLogger, { LoggerOptions, LogLevel } from './contracts/IDBSQLLogger';
- 
-export default class DBSQLLogger implements IDBSQLLogger {
-  logger: Logger;
- 
-  transports: {
-    console: winston.transports.ConsoleTransportInstance;
-    file?: winston.transports.FileTransportInstance;
-  };
- 
-  constructor({ level = LogLevel.info, filepath }: LoggerOptions = {}) {
-    this.transports = {
-      console: new winston.transports.Console({ handleExceptions: true, level }),
-    };
-    this.logger = winston.createLogger({
-      transports: [this.transports.console],
-    });
-    if (filepath) {
-      this.transports.file = new winston.transports.File({ filename: filepath, handleExceptions: true, level });
-      this.logger.add(this.transports.file);
-    }
-  }
- 
-  async log(level: LogLevel, message: string) {
-    this.logger.log({ level, message });
-  }
- 
-  setLevel(level: LogLevel) {
-    this.transports.console.level = level;
-    if (this.transports.file) {
-      this.transports.file.level = level;
-    }
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLOperation.ts.html b/coverage/lcov-report/lib/DBSQLOperation.ts.html deleted file mode 100644 index 69bfd8cc..00000000 --- a/coverage/lcov-report/lib/DBSQLOperation.ts.html +++ /dev/null @@ -1,1537 +0,0 @@ - - - - - - Code coverage report for lib/DBSQLOperation.ts - - - - - - - - - -
-
-

All files / lib DBSQLOperation.ts

-
- -
- 9.58% - Statements - 16/167 -
- - -
- 0% - Branches - 0/165 -
- - -
- 0% - Functions - 0/24 -
- - -
- 9.58% - Lines - 16/167 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 -413 -414 -415 -416 -417 -418 -419 -420 -421 -422 -423 -424 -425 -426 -427 -428 -429 -430 -431 -432 -433 -434 -435 -436 -437 -438 -439 -440 -441 -442 -443 -444 -445 -446 -447 -448 -449 -450 -451 -452 -453 -454 -455 -456 -457 -458 -459 -460 -461 -462 -463 -464 -465 -466 -467 -468 -469 -470 -471 -472 -473 -474 -475 -476 -477 -478 -479 -480 -481 -482 -483 -484 -4851x -1x -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -1x -1x -1x -  -1x -1x -1x -1x -1x -1x -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { stringify, NIL } from 'uuid';
-import { Readable } from 'node:stream';
-import IOperation, {
-  FetchOptions,
-  FinishedOptions,
-  GetSchemaOptions,
-  WaitUntilReadyOptions,
-  IteratorOptions,
-  IOperationChunksIterator,
-  IOperationRowsIterator,
-  NodeStreamOptions,
-} from './contracts/IOperation';
-import {
-  TGetOperationStatusResp,
-  TOperationHandle,
-  TTableSchema,
-  TSparkDirectResults,
-  TGetResultSetMetadataResp,
-  TSparkRowSetType,
-  TCloseOperationResp,
-  TOperationState,
-} from '../thrift/TCLIService_types';
-import Status from './dto/Status';
-import { LogLevel } from './contracts/IDBSQLLogger';
-import OperationStateError, { OperationStateErrorCode } from './errors/OperationStateError';
-import IResultsProvider from './result/IResultsProvider';
-import RowSetProvider from './result/RowSetProvider';
-import JsonResultHandler from './result/JsonResultHandler';
-import ArrowResultHandler from './result/ArrowResultHandler';
-import CloudFetchResultHandler from './result/CloudFetchResultHandler';
-import ArrowResultConverter from './result/ArrowResultConverter';
-import ResultSlicer from './result/ResultSlicer';
-import { definedOrError } from './utils';
-import { OperationChunksIterator, OperationRowsIterator } from './utils/OperationIterator';
-import HiveDriverError from './errors/HiveDriverError';
-import IClientContext from './contracts/IClientContext';
- 
-interface DBSQLOperationConstructorOptions {
-  handle: TOperationHandle;
-  directResults?: TSparkDirectResults;
-  context: IClientContext;
-}
- 
-async function delay(ms?: number): Promise<void> {
-  return new Promise((resolve) => {
-    setTimeout(() => {
-      resolve();
-    }, ms);
-  });
-}
- 
-export default class DBSQLOperation implements IOperation {
-  private readonly context: IClientContext;
- 
-  private readonly operationHandle: TOperationHandle;
- 
-  public onClose?: () => void;
- 
-  private readonly _data: RowSetProvider;
- 
-  private readonly closeOperation?: TCloseOperationResp;
- 
-  private closed: boolean = false;
- 
-  private cancelled: boolean = false;
- 
-  private metadata?: TGetResultSetMetadataResp;
- 
-  private metadataPromise?: Promise<TGetResultSetMetadataResp>;
- 
-  private state: TOperationState = TOperationState.INITIALIZED_STATE;
- 
-  // Once operation is finished or fails - cache status response, because subsequent calls
-  // to `getOperationStatus()` may fail with irrelevant errors, e.g. HTTP 404
-  private operationStatus?: TGetOperationStatusResp;
- 
-  private resultHandler?: ResultSlicer<any>;
- 
-  constructor({ handle, directResults, context }: DBSQLOperationConstructorOptions) {
-    this.operationHandle = handle;
-    this.context = context;
- 
-    const useOnlyPrefetchedResults = Boolean(directResults?.closeOperation);
- 
-    if (directResults?.operationStatus) {
-      this.processOperationStatusResponse(directResults.operationStatus);
-    }
- 
-    this.metadata = directResults?.resultSetMetadata;
-    this._data = new RowSetProvider(
-      this.context,
-      this.operationHandle,
-      [directResults?.resultSet],
-      useOnlyPrefetchedResults,
-    );
-    this.closeOperation = directResults?.closeOperation;
-    this.context.getLogger().log(LogLevel.debug, `Operation created with id: ${this.id}`);
-  }
- 
-  public iterateChunks(options?: IteratorOptions): IOperationChunksIterator {
-    return new OperationChunksIterator(this, options);
-  }
- 
-  public iterateRows(options?: IteratorOptions): IOperationRowsIterator {
-    return new OperationRowsIterator(this, options);
-  }
- 
-  public toNodeStream(options?: NodeStreamOptions): Readable {
-    let iterable: IOperationChunksIterator | IOperationRowsIterator | undefined;
- 
-    switch (options?.mode ?? 'chunks') {
-      case 'chunks':
-        iterable = this.iterateChunks(options?.iteratorOptions);
-        break;
-      case 'rows':
-        iterable = this.iterateRows(options?.iteratorOptions);
-        break;
-      default:
-        throw new Error(`IOperation.toNodeStream: unsupported mode ${options?.mode}`);
-    }
- 
-    return Readable.from(iterable, options?.streamOptions);
-  }
- 
-  public get id() {
-    const operationId = this.operationHandle?.operationId?.guid;
-    return operationId ? stringify(operationId) : NIL;
-  }
- 
-  /**
-   * Fetches all data
-   * @public
-   * @param options - maxRows property can be set to limit chunk size
-   * @returns Array of data with length equal to option.maxRows
-   * @throws {StatusError}
-   * @example
-   * const result = await queryOperation.fetchAll();
-   */
-  public async fetchAll(options?: FetchOptions): Promise<Array<object>> {
-    const data: Array<Array<object>> = [];
- 
-    const fetchChunkOptions = {
-      ...options,
-      // Tell slicer to return raw chunks. We're going to process all of them anyway,
-      // so no need to additionally buffer and slice chunks returned by server
-      disableBuffering: true,
-    };
- 
-    do {
-      // eslint-disable-next-line no-await-in-loop
-      const chunk = await this.fetchChunk(fetchChunkOptions);
-      data.push(chunk);
-    } while (await this.hasMoreRows()); // eslint-disable-line no-await-in-loop
-    this.context.getLogger().log(LogLevel.debug, `Fetched all data from operation with id: ${this.id}`);
- 
-    return data.flat();
-  }
- 
-  /**
-   * Fetches chunk of data
-   * @public
-   * @param options - maxRows property sets chunk size
-   * @returns Array of data with length equal to option.maxRows
-   * @throws {StatusError}
-   * @example
-   * const result = await queryOperation.fetchChunk({maxRows: 1000});
-   */
-  public async fetchChunk(options?: FetchOptions): Promise<Array<object>> {
-    await this.failIfClosed();
- 
-    if (!this.operationHandle.hasResultSet) {
-      return [];
-    }
- 
-    await this.waitUntilReady(options);
- 
-    const resultHandler = await this.getResultHandler();
-    await this.failIfClosed();
- 
-    // All the library code is Promise-based, however, since Promises are microtasks,
-    // enqueueing a lot of promises may block macrotasks execution for a while.
-    // Usually, there are no much microtasks scheduled, however, when fetching query
-    // results (especially CloudFetch ones) it's quite easy to block event loop for
-    // long enough to break a lot of things. For example, with CloudFetch, after first
-    // set of files are downloaded and being processed immediately one by one, event
-    // loop easily gets blocked for enough time to break connection pool. `http.Agent`
-    // stops receiving socket events, and marks all sockets invalid on the next attempt
-    // to use them. See these similar issues that helped to debug this particular case -
-    // https://github.com/nodejs/node/issues/47130 and https://github.com/node-fetch/node-fetch/issues/1735
-    // This simple fix allows to clean up a microtasks queue and allow Node to process
-    // macrotasks as well, allowing the normal operation of other code. Also, this
-    // fix is added to `fetchChunk` method because, unlike other methods, `fetchChunk` is
-    // a potential source of issues described above
-    await new Promise<void>((resolve) => {
-      setTimeout(resolve, 0);
-    });
- 
-    const defaultMaxRows = this.context.getConfig().fetchChunkDefaultMaxRows;
- 
-    const result = resultHandler.fetchNext({
-      limit: options?.maxRows ?? defaultMaxRows,
-      disableBuffering: options?.disableBuffering,
-    });
-    await this.failIfClosed();
- 
-    this.context
-      .getLogger()
-      .log(
-        LogLevel.debug,
-        `Fetched chunk of size: ${options?.maxRows ?? defaultMaxRows} from operation with id: ${this.id}`,
-      );
-    return result;
-  }
- 
-  /**
-   * Requests operation status
-   * @param progress
-   * @throws {StatusError}
-   */
-  public async status(progress: boolean = false): Promise<TGetOperationStatusResp> {
-    await this.failIfClosed();
-    this.context.getLogger().log(LogLevel.debug, `Fetching status for operation with id: ${this.id}`);
- 
-    if (this.operationStatus) {
-      return this.operationStatus;
-    }
- 
-    const driver = await this.context.getDriver();
-    const response = await driver.getOperationStatus({
-      operationHandle: this.operationHandle,
-      getProgressUpdate: progress,
-    });
- 
-    return this.processOperationStatusResponse(response);
-  }
- 
-  /**
-   * Cancels operation
-   * @throws {StatusError}
-   */
-  public async cancel(): Promise<Status> {
-    if (this.closed || this.cancelled) {
-      return Status.success();
-    }
- 
-    this.context.getLogger().log(LogLevel.debug, `Cancelling operation with id: ${this.id}`);
- 
-    const driver = await this.context.getDriver();
-    const response = await driver.cancelOperation({
-      operationHandle: this.operationHandle,
-    });
-    Status.assert(response.status);
-    this.cancelled = true;
-    const result = new Status(response.status);
- 
-    // Cancelled operation becomes unusable, similarly to being closed
-    this.onClose?.();
-    return result;
-  }
- 
-  /**
-   * Closes operation
-   * @throws {StatusError}
-   */
-  public async close(): Promise<Status> {
-    if (this.closed || this.cancelled) {
-      return Status.success();
-    }
- 
-    this.context.getLogger().log(LogLevel.debug, `Closing operation with id: ${this.id}`);
- 
-    const driver = await this.context.getDriver();
-    const response =
-      this.closeOperation ??
-      (await driver.closeOperation({
-        operationHandle: this.operationHandle,
-      }));
-    Status.assert(response.status);
-    this.closed = true;
-    const result = new Status(response.status);
- 
-    this.onClose?.();
-    return result;
-  }
- 
-  public async finished(options?: FinishedOptions): Promise<void> {
-    await this.failIfClosed();
-    await this.waitUntilReady(options);
-  }
- 
-  public async hasMoreRows(): Promise<boolean> {
-    // If operation is closed or cancelled - we should not try to get data from it
-    if (this.closed || this.cancelled) {
-      return false;
-    }
- 
-    // Wait for operation to finish before checking for more rows
-    // This ensures metadata can be fetched successfully
-    if (this.operationHandle.hasResultSet) {
-      await this.waitUntilReady();
-    }
- 
-    // If we fetched all the data from server - check if there's anything buffered in result handler
-    const resultHandler = await this.getResultHandler();
-    return resultHandler.hasMore();
-  }
- 
-  public async getSchema(options?: GetSchemaOptions): Promise<TTableSchema | null> {
-    await this.failIfClosed();
- 
-    if (!this.operationHandle.hasResultSet) {
-      return null;
-    }
- 
-    await this.waitUntilReady(options);
- 
-    this.context.getLogger().log(LogLevel.debug, `Fetching schema for operation with id: ${this.id}`);
-    const metadata = await this.fetchMetadata();
-    return metadata.schema ?? null;
-  }
- 
-  public async getMetadata(): Promise<TGetResultSetMetadataResp> {
-    await this.failIfClosed();
-    await this.waitUntilReady();
-    return this.fetchMetadata();
-  }
- 
-  private async failIfClosed(): Promise<void> {
-    if (this.closed) {
-      throw new OperationStateError(OperationStateErrorCode.Closed);
-    }
-    if (this.cancelled) {
-      throw new OperationStateError(OperationStateErrorCode.Canceled);
-    }
-  }
- 
-  private async waitUntilReady(options?: WaitUntilReadyOptions) {
-    if (this.state === TOperationState.FINISHED_STATE) {
-      return;
-    }
- 
-    let isReady = false;
- 
-    while (!isReady) {
-      // eslint-disable-next-line no-await-in-loop
-      const response = await this.status(Boolean(options?.progress));
- 
-      if (options?.callback) {
-        // eslint-disable-next-line no-await-in-loop
-        await Promise.resolve(options.callback(response));
-      }
- 
-      switch (response.operationState) {
-        // For these states do nothing and continue waiting
-        case TOperationState.INITIALIZED_STATE:
-        case TOperationState.PENDING_STATE:
-        case TOperationState.RUNNING_STATE:
-          break;
- 
-        // Operation is completed, so exit the loop
-        case TOperationState.FINISHED_STATE:
-          isReady = true;
-          break;
- 
-        // Operation was cancelled, so set a flag and exit the loop (throw an error)
-        case TOperationState.CANCELED_STATE:
-          this.cancelled = true;
-          throw new OperationStateError(OperationStateErrorCode.Canceled, response);
- 
-        // Operation was closed, so set a flag and exit the loop (throw an error)
-        case TOperationState.CLOSED_STATE:
-          this.closed = true;
-          throw new OperationStateError(OperationStateErrorCode.Closed, response);
- 
-        // Error states - throw and exit the loop
-        case TOperationState.ERROR_STATE:
-          throw new OperationStateError(OperationStateErrorCode.Error, response);
-        case TOperationState.TIMEDOUT_STATE:
-          throw new OperationStateError(OperationStateErrorCode.Timeout, response);
-        case TOperationState.UKNOWN_STATE:
-        default:
-          throw new OperationStateError(OperationStateErrorCode.Unknown, response);
-      }
- 
-      // If not ready yet - make some delay before the next status requests
-      if (!isReady) {
-        // eslint-disable-next-line no-await-in-loop
-        await delay(100);
-      }
-    }
-  }
- 
-  private async fetchMetadata() {
-    // If metadata is already cached, return it immediately
-    if (this.metadata) {
-      return this.metadata;
-    }
- 
-    // If a fetch is already in progress, wait for it to complete
-    if (this.metadataPromise) {
-      return this.metadataPromise;
-    }
- 
-    // Start a new fetch and cache the promise to prevent concurrent fetches
-    this.metadataPromise = (async () => {
-      const driver = await this.context.getDriver();
-      const metadata = await driver.getResultSetMetadata({
-        operationHandle: this.operationHandle,
-      });
-      Status.assert(metadata.status);
-      this.metadata = metadata;
-      return metadata;
-    })();
- 
-    try {
-      return await this.metadataPromise;
-    } finally {
-      // Clear the promise once completed (success or failure)
-      this.metadataPromise = undefined;
-    }
-  }
- 
-  private async getResultHandler(): Promise<ResultSlicer<any>> {
-    const metadata = await this.fetchMetadata();
-    const resultFormat = definedOrError(metadata.resultFormat);
- 
-    if (!this.resultHandler) {
-      let resultSource: IResultsProvider<Array<any>> | undefined;
- 
-      switch (resultFormat) {
-        case TSparkRowSetType.COLUMN_BASED_SET:
-          resultSource = new JsonResultHandler(this.context, this._data, metadata);
-          break;
-        case TSparkRowSetType.ARROW_BASED_SET:
-          resultSource = new ArrowResultConverter(
-            this.context,
-            new ArrowResultHandler(this.context, this._data, metadata),
-            metadata,
-          );
-          break;
-        case TSparkRowSetType.URL_BASED_SET:
-          resultSource = new ArrowResultConverter(
-            this.context,
-            new CloudFetchResultHandler(this.context, this._data, metadata),
-            metadata,
-          );
-          break;
-        // no default
-      }
- 
-      if (resultSource) {
-        this.resultHandler = new ResultSlicer(this.context, resultSource);
-      }
-    }
- 
-    if (!this.resultHandler) {
-      throw new HiveDriverError(`Unsupported result format: ${TSparkRowSetType[resultFormat]}`);
-    }
- 
-    return this.resultHandler;
-  }
- 
-  private processOperationStatusResponse(response: TGetOperationStatusResp) {
-    Status.assert(response.status);
- 
-    this.state = response.operationState ?? this.state;
- 
-    if (typeof response.hasResultSet === 'boolean') {
-      this.operationHandle.hasResultSet = response.hasResultSet;
-    }
- 
-    const isInProgress = [
-      TOperationState.INITIALIZED_STATE,
-      TOperationState.PENDING_STATE,
-      TOperationState.RUNNING_STATE,
-    ].includes(this.state);
- 
-    if (!isInProgress) {
-      this.operationStatus = response;
-    }
- 
-    return response;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLParameter.ts.html b/coverage/lcov-report/lib/DBSQLParameter.ts.html deleted file mode 100644 index 58d43aed..00000000 --- a/coverage/lcov-report/lib/DBSQLParameter.ts.html +++ /dev/null @@ -1,388 +0,0 @@ - - - - - - Code coverage report for lib/DBSQLParameter.ts - - - - - - - - - -
-
-

All files / lib DBSQLParameter.ts

-
- -
- 54.54% - Statements - 18/33 -
- - -
- 4.65% - Branches - 2/43 -
- - -
- 33.33% - Functions - 1/3 -
- - -
- 54.54% - Lines - 18/33 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -1021x -1x -  -  -  -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import Int64 from 'node-int64';
-import { TSparkParameter, TSparkParameterValue } from '../thrift/TCLIService_types';
- 
-export type DBSQLParameterValue = undefined | null | boolean | number | bigint | Int64 | Date | string;
- 
-export enum DBSQLParameterType {
-  VOID = 'VOID', // aka NULL
-  STRING = 'STRING',
-  DATE = 'DATE',
-  TIMESTAMP = 'TIMESTAMP',
-  FLOAT = 'FLOAT',
-  DECIMAL = 'DECIMAL',
-  DOUBLE = 'DOUBLE',
-  INTEGER = 'INTEGER',
-  BIGINT = 'BIGINT',
-  SMALLINT = 'SMALLINT',
-  TINYINT = 'TINYINT',
-  BOOLEAN = 'BOOLEAN',
-  INTERVALMONTH = 'INTERVAL MONTH',
-  INTERVALDAY = 'INTERVAL DAY',
-}
- 
-interface DBSQLParameterOptions {
-  type?: DBSQLParameterType;
-  value: DBSQLParameterValue;
-}
- 
-interface ToSparkParameterOptions {
-  name?: string;
-}
- 
-export class DBSQLParameter {
-  public readonly type?: string;
- 
-  public readonly value: DBSQLParameterValue;
- 
-  constructor({ type, value }: DBSQLParameterOptions) {
-    this.type = type;
-    this.value = value;
-  }
- 
-  public toSparkParameter({ name }: ToSparkParameterOptions = {}): TSparkParameter {
-    // If VOID type was set explicitly - ignore value
-    if (this.type === DBSQLParameterType.VOID) {
-      return new TSparkParameter({ name }); // for NULL neither `type` nor `value` should be set
-    }
- 
-    // Infer NULL values
-    if (this.value === undefined || this.value === null) {
-      return new TSparkParameter({ name }); // for NULL neither `type` nor `value` should be set
-    }
- 
-    if (typeof this.value === 'boolean') {
-      return new TSparkParameter({
-        name,
-        type: this.type ?? DBSQLParameterType.BOOLEAN,
-        value: new TSparkParameterValue({
-          stringValue: this.value ? 'TRUE' : 'FALSE',
-        }),
-      });
-    }
- 
-    if (typeof this.value === 'number') {
-      return new TSparkParameter({
-        name,
-        type: this.type ?? (Number.isInteger(this.value) ? DBSQLParameterType.INTEGER : DBSQLParameterType.DOUBLE),
-        value: new TSparkParameterValue({
-          stringValue: Number(this.value).toString(),
-        }),
-      });
-    }
- 
-    if (this.value instanceof Int64 || typeof this.value === 'bigint') {
-      return new TSparkParameter({
-        name,
-        type: this.type ?? DBSQLParameterType.BIGINT,
-        value: new TSparkParameterValue({
-          stringValue: this.value.toString(),
-        }),
-      });
-    }
- 
-    if (this.value instanceof Date) {
-      return new TSparkParameter({
-        name,
-        type: this.type ?? DBSQLParameterType.TIMESTAMP,
-        value: new TSparkParameterValue({
-          stringValue: this.value.toISOString(),
-        }),
-      });
-    }
- 
-    return new TSparkParameter({
-      name,
-      type: this.type ?? DBSQLParameterType.STRING,
-      value: new TSparkParameterValue({
-        stringValue: this.value,
-      }),
-    });
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/DBSQLSession.ts.html b/coverage/lcov-report/lib/DBSQLSession.ts.html deleted file mode 100644 index 35b15f0c..00000000 --- a/coverage/lcov-report/lib/DBSQLSession.ts.html +++ /dev/null @@ -1,1972 +0,0 @@ - - - - - - Code coverage report for lib/DBSQLSession.ts - - - - - - - - - -
-
-

All files / lib DBSQLSession.ts

-
- -
- 10.19% - Statements - 21/206 -
- - -
- 0% - Branches - 0/115 -
- - -
- 0% - Functions - 0/26 -
- - -
- 10.19% - Lines - 21/206 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 -413 -414 -415 -416 -417 -418 -419 -420 -421 -422 -423 -424 -425 -426 -427 -428 -429 -430 -431 -432 -433 -434 -435 -436 -437 -438 -439 -440 -441 -442 -443 -444 -445 -446 -447 -448 -449 -450 -451 -452 -453 -454 -455 -456 -457 -458 -459 -460 -461 -462 -463 -464 -465 -466 -467 -468 -469 -470 -471 -472 -473 -474 -475 -476 -477 -478 -479 -480 -481 -482 -483 -484 -485 -486 -487 -488 -489 -490 -491 -492 -493 -494 -495 -496 -497 -498 -499 -500 -501 -502 -503 -504 -505 -506 -507 -508 -509 -510 -511 -512 -513 -514 -515 -516 -517 -518 -519 -520 -521 -522 -523 -524 -525 -526 -527 -528 -529 -530 -531 -532 -533 -534 -535 -536 -537 -538 -539 -540 -541 -542 -543 -544 -545 -546 -547 -548 -549 -550 -551 -552 -553 -554 -555 -556 -557 -558 -559 -560 -561 -562 -563 -564 -565 -566 -567 -568 -569 -570 -571 -572 -573 -574 -575 -576 -577 -578 -579 -580 -581 -582 -583 -584 -585 -586 -587 -588 -589 -590 -591 -592 -593 -594 -595 -596 -597 -598 -599 -600 -601 -602 -603 -604 -605 -606 -607 -608 -609 -610 -611 -612 -613 -614 -615 -616 -617 -618 -619 -620 -621 -622 -623 -624 -625 -626 -627 -628 -629 -6301x -1x -1x -1x -1x -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -  -  -  -1x -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import * as fs from 'fs';
-import * as path from 'path';
-import stream from 'node:stream';
-import util from 'node:util';
-import { stringify, NIL } from 'uuid';
-import Int64 from 'node-int64';
-import fetch, { HeadersInit } from 'node-fetch';
-import {
-  TSessionHandle,
-  TStatus,
-  TOperationHandle,
-  TSparkDirectResults,
-  TSparkArrowTypes,
-  TSparkParameter,
-  TProtocolVersion,
-  TExecuteStatementReq,
-} from '../thrift/TCLIService_types';
-import IDBSQLSession, {
-  ExecuteStatementOptions,
-  TypeInfoRequest,
-  CatalogsRequest,
-  SchemasRequest,
-  TablesRequest,
-  TableTypesRequest,
-  ColumnsRequest,
-  FunctionsRequest,
-  PrimaryKeysRequest,
-  CrossReferenceRequest,
-} from './contracts/IDBSQLSession';
-import IOperation from './contracts/IOperation';
-import DBSQLOperation from './DBSQLOperation';
-import Status from './dto/Status';
-import InfoValue from './dto/InfoValue';
-import { definedOrError, LZ4, ProtocolVersion } from './utils';
-import CloseableCollection from './utils/CloseableCollection';
-import { LogLevel } from './contracts/IDBSQLLogger';
-import HiveDriverError from './errors/HiveDriverError';
-import StagingError from './errors/StagingError';
-import { DBSQLParameter, DBSQLParameterValue } from './DBSQLParameter';
-import ParameterError from './errors/ParameterError';
-import IClientContext, { ClientConfig } from './contracts/IClientContext';
- 
-// Explicitly promisify a callback-style `pipeline` because `node:stream/promises` is not available in Node 14
-const pipeline = util.promisify(stream.pipeline);
- 
-interface OperationResponseShape {
-  status: TStatus;
-  operationHandle?: TOperationHandle;
-  directResults?: TSparkDirectResults;
-}
- 
-export function numberToInt64(value: number | bigint | Int64): Int64 {
-  if (value instanceof Int64) {
-    return value;
-  }
- 
-  if (typeof value === 'bigint') {
-    const buffer = new ArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT);
-    const view = new DataView(buffer);
-    view.setBigInt64(0, value, false); // `false` to use big-endian order
-    return new Int64(Buffer.from(buffer));
-  }
- 
-  return new Int64(value);
-}
- 
-function getDirectResultsOptions(maxRows: number | bigint | Int64 | null | undefined, config: ClientConfig) {
-  if (maxRows === null) {
-    return {};
-  }
- 
-  return {
-    getDirectResults: {
-      maxRows: numberToInt64(maxRows ?? config.directResultsDefaultMaxRows),
-    },
-  };
-}
- 
-function getArrowOptions(
-  config: ClientConfig,
-  serverProtocolVersion: TProtocolVersion | undefined | null,
-): {
-  canReadArrowResult: boolean;
-  useArrowNativeTypes?: TSparkArrowTypes;
-} {
-  const { arrowEnabled = true, useArrowNativeTypes = true } = config;
- 
-  if (!arrowEnabled || !ProtocolVersion.supportsArrowMetadata(serverProtocolVersion)) {
-    return {
-      canReadArrowResult: false,
-    };
-  }
- 
-  return {
-    canReadArrowResult: true,
-    useArrowNativeTypes: {
-      timestampAsArrow: useArrowNativeTypes,
-      decimalAsArrow: useArrowNativeTypes,
-      complexTypesAsArrow: useArrowNativeTypes,
-      // TODO: currently unsupported by `apache-arrow` (see https://github.com/streamlit/streamlit/issues/4489)
-      intervalTypesAsArrow: false,
-    },
-  };
-}
- 
-function getQueryParameters(
-  namedParameters?: Record<string, DBSQLParameter | DBSQLParameterValue>,
-  ordinalParameters?: Array<DBSQLParameter | DBSQLParameterValue>,
-): Array<TSparkParameter> {
-  const namedParametersProvided = namedParameters !== undefined && Object.keys(namedParameters).length > 0;
-  const ordinalParametersProvided = ordinalParameters !== undefined && ordinalParameters.length > 0;
- 
-  if (namedParametersProvided && ordinalParametersProvided) {
-    throw new ParameterError('Driver does not support both ordinal and named parameters.');
-  }
- 
-  if (!namedParametersProvided && !ordinalParametersProvided) {
-    return [];
-  }
- 
-  const result: Array<TSparkParameter> = [];
- 
-  if (namedParameters !== undefined) {
-    for (const name of Object.keys(namedParameters)) {
-      const value = namedParameters[name];
-      const param = value instanceof DBSQLParameter ? value : new DBSQLParameter({ value });
-      result.push(param.toSparkParameter({ name }));
-    }
-  }
- 
-  if (ordinalParameters !== undefined) {
-    for (const value of ordinalParameters) {
-      const param = value instanceof DBSQLParameter ? value : new DBSQLParameter({ value });
-      result.push(param.toSparkParameter());
-    }
-  }
- 
-  return result;
-}
- 
-interface DBSQLSessionConstructorOptions {
-  handle: TSessionHandle;
-  context: IClientContext;
-  serverProtocolVersion?: TProtocolVersion;
-}
- 
-export default class DBSQLSession implements IDBSQLSession {
-  private readonly context: IClientContext;
- 
-  private readonly sessionHandle: TSessionHandle;
- 
-  private isOpen = true;
- 
-  private serverProtocolVersion?: TProtocolVersion;
- 
-  public onClose?: () => void;
- 
-  private operations = new CloseableCollection<DBSQLOperation>();
- 
-  /**
-   * Helper method to determine if runAsync should be set for metadata operations
-   * @private
-   * @returns true if supported by protocol version, undefined otherwise
-   */
-  private getRunAsyncForMetadataOperations(): boolean | undefined {
-    return ProtocolVersion.supportsAsyncMetadataOperations(this.serverProtocolVersion) ? true : undefined;
-  }
- 
-  constructor({ handle, context, serverProtocolVersion }: DBSQLSessionConstructorOptions) {
-    this.sessionHandle = handle;
-    this.context = context;
-    // Get the server protocol version from the provided parameter (from TOpenSessionResp)
-    this.serverProtocolVersion = serverProtocolVersion;
-    this.context.getLogger().log(LogLevel.debug, `Session created with id: ${this.id}`);
-    this.context.getLogger().log(LogLevel.debug, `Server protocol version: ${this.serverProtocolVersion}`);
-  }
- 
-  public get id() {
-    const sessionId = this.sessionHandle?.sessionId?.guid;
-    return sessionId ? stringify(sessionId) : NIL;
-  }
- 
-  /**
-   * Fetches info
-   * @public
-   * @param infoType - One of the values TCLIService_types.TGetInfoType
-   * @returns Value corresponding to info type requested
-   * @example
-   * const response = await session.getInfo(thrift.TCLIService_types.TGetInfoType.CLI_DBMS_VER);
-   */
-  public async getInfo(infoType: number): Promise<InfoValue> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const operationPromise = driver.getInfo({
-      sessionHandle: this.sessionHandle,
-      infoType,
-    });
-    const response = await this.handleResponse(operationPromise);
-    Status.assert(response.status);
-    return new InfoValue(response.infoValue);
-  }
- 
-  /**
-   * Executes statement
-   * @public
-   * @param statement - SQL statement to be executed
-   * @param options - maxRows field is used to specify Direct Results
-   * @returns DBSQLOperation
-   * @example
-   * const operation = await session.executeStatement(query);
-   */
-  public async executeStatement(statement: string, options: ExecuteStatementOptions = {}): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const request = new TExecuteStatementReq({
-      sessionHandle: this.sessionHandle,
-      statement,
-      queryTimeout: options.queryTimeout ? numberToInt64(options.queryTimeout) : undefined,
-      runAsync: true,
-      ...getDirectResultsOptions(options.maxRows, clientConfig),
-      ...getArrowOptions(clientConfig, this.serverProtocolVersion),
-    });
- 
-    if (ProtocolVersion.supportsParameterizedQueries(this.serverProtocolVersion)) {
-      request.parameters = getQueryParameters(options.namedParameters, options.ordinalParameters);
-    }
- 
-    if (ProtocolVersion.supportsCloudFetch(this.serverProtocolVersion)) {
-      request.canDownloadResult = options.useCloudFetch ?? clientConfig.useCloudFetch;
-    }
- 
-    if (ProtocolVersion.supportsArrowCompression(this.serverProtocolVersion) && request.canDownloadResult !== true) {
-      request.canDecompressLZ4Result = (options.useLZ4Compression ?? clientConfig.useLZ4Compression) && Boolean(LZ4());
-    }
- 
-    const operationPromise = driver.executeStatement(request);
-    const response = await this.handleResponse(operationPromise);
-    const operation = this.createOperation(response);
- 
-    // If `stagingAllowedLocalPath` is provided - assume that operation possibly may be a staging operation.
-    // To know for sure, fetch metadata and check a `isStagingOperation` flag. If it happens that it wasn't
-    // a staging operation - not a big deal, we just fetched metadata earlier, but operation is still usable
-    // and user can get data from it.
-    // If `stagingAllowedLocalPath` is not provided - don't do anything to the operation. In a case of regular
-    // operation, everything will work as usual. In a case of staging operation, it will be processed like any
-    // other query - it will be possible to get data from it as usual, or use other operation methods.
-    if (options.stagingAllowedLocalPath !== undefined) {
-      const metadata = await operation.getMetadata();
-      if (metadata.isStagingOperation) {
-        const allowedLocalPath = Array.isArray(options.stagingAllowedLocalPath)
-          ? options.stagingAllowedLocalPath
-          : [options.stagingAllowedLocalPath];
-        return this.handleStagingOperation(operation, allowedLocalPath);
-      }
-    }
-    return operation;
-  }
- 
-  private async handleStagingOperation(operation: IOperation, allowedLocalPath: Array<string>): Promise<IOperation> {
-    type StagingResponse = {
-      presignedUrl: string;
-      localFile?: string;
-      headers: HeadersInit;
-      operation: string;
-    };
-    const rows = await operation.fetchAll();
-    if (rows.length !== 1) {
-      throw new StagingError('Staging operation: expected only one row in result');
-    }
-    const row = rows[0] as StagingResponse;
- 
-    // For REMOVE operation local file is not available, so no need to validate it
-    if (row.localFile !== undefined) {
-      let allowOperation = false;
- 
-      for (const filepath of allowedLocalPath) {
-        const relativePath = path.relative(filepath, row.localFile);
- 
-        if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
-          allowOperation = true;
-        }
-      }
- 
-      if (!allowOperation) {
-        throw new StagingError('Staging path not a subset of allowed local paths.');
-      }
-    }
- 
-    const { localFile, presignedUrl, headers } = row;
- 
-    switch (row.operation) {
-      case 'GET':
-        await this.handleStagingGet(localFile, presignedUrl, headers);
-        return operation;
-      case 'PUT':
-        await this.handleStagingPut(localFile, presignedUrl, headers);
-        return operation;
-      case 'REMOVE':
-        await this.handleStagingRemove(presignedUrl, headers);
-        return operation;
-      default:
-        throw new StagingError(`Staging query operation is not supported: ${row.operation}`);
-    }
-  }
- 
-  private async handleStagingGet(
-    localFile: string | undefined,
-    presignedUrl: string,
-    headers: HeadersInit,
-  ): Promise<void> {
-    if (localFile === undefined) {
-      throw new StagingError('Local file path not provided');
-    }
- 
-    const connectionProvider = await this.context.getConnectionProvider();
-    const agent = await connectionProvider.getAgent();
- 
-    const response = await fetch(presignedUrl, { method: 'GET', headers, agent });
-    if (!response.ok) {
-      throw new StagingError(`HTTP error ${response.status} ${response.statusText}`);
-    }
- 
-    const fileStream = fs.createWriteStream(localFile);
-    // `pipeline` will do all the dirty job for us, including error handling and closing all the streams properly
-    return pipeline(response.body, fileStream);
-  }
- 
-  private async handleStagingRemove(presignedUrl: string, headers: HeadersInit): Promise<void> {
-    const connectionProvider = await this.context.getConnectionProvider();
-    const agent = await connectionProvider.getAgent();
- 
-    const response = await fetch(presignedUrl, { method: 'DELETE', headers, agent });
-    // Looks that AWS and Azure have a different behavior of HTTP `DELETE` for non-existing files.
-    // AWS assumes that - since file already doesn't exist - the goal is achieved, and returns HTTP 200.
-    // Azure, on the other hand, is somewhat stricter and check if file exists before deleting it. And if
-    // file doesn't exist - Azure returns HTTP 404.
-    //
-    // For us, it's totally okay if file didn't exist before removing. So when we get an HTTP 404 -
-    // just ignore it and report success. This way we can have a uniform library behavior for all clouds
-    if (!response.ok && response.status !== 404) {
-      throw new StagingError(`HTTP error ${response.status} ${response.statusText}`);
-    }
-  }
- 
-  private async handleStagingPut(
-    localFile: string | undefined,
-    presignedUrl: string,
-    headers: HeadersInit,
-  ): Promise<void> {
-    if (localFile === undefined) {
-      throw new StagingError('Local file path not provided');
-    }
- 
-    const connectionProvider = await this.context.getConnectionProvider();
-    const agent = await connectionProvider.getAgent();
- 
-    const fileStream = fs.createReadStream(localFile);
-    const fileInfo = fs.statSync(localFile, { bigint: true });
- 
-    const response = await fetch(presignedUrl, {
-      method: 'PUT',
-      headers: {
-        ...headers,
-        // This header is required by server
-        'Content-Length': fileInfo.size.toString(),
-      },
-      agent,
-      body: fileStream,
-    });
-    if (!response.ok) {
-      throw new StagingError(`HTTP error ${response.status} ${response.statusText}`);
-    }
-  }
- 
-  /**
-   * Information about supported data types
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getTypeInfo(request: TypeInfoRequest = {}): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getTypeInfo({
-      sessionHandle: this.sessionHandle,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Get list of catalogs
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getCatalogs(request: CatalogsRequest = {}): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getCatalogs({
-      sessionHandle: this.sessionHandle,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Get list of schemas
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getSchemas(request: SchemasRequest = {}): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getSchemas({
-      sessionHandle: this.sessionHandle,
-      catalogName: request.catalogName,
-      schemaName: request.schemaName,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Get list of tables
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getTables(request: TablesRequest = {}): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getTables({
-      sessionHandle: this.sessionHandle,
-      catalogName: request.catalogName,
-      schemaName: request.schemaName,
-      tableName: request.tableName,
-      tableTypes: request.tableTypes,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Get list of supported table types
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getTableTypes(request: TableTypesRequest = {}): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getTableTypes({
-      sessionHandle: this.sessionHandle,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Get full information about columns of the table
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getColumns(request: ColumnsRequest = {}): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getColumns({
-      sessionHandle: this.sessionHandle,
-      catalogName: request.catalogName,
-      schemaName: request.schemaName,
-      tableName: request.tableName,
-      columnName: request.columnName,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Get information about function
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getFunctions(request: FunctionsRequest): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getFunctions({
-      sessionHandle: this.sessionHandle,
-      catalogName: request.catalogName,
-      schemaName: request.schemaName,
-      functionName: request.functionName,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  public async getPrimaryKeys(request: PrimaryKeysRequest): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getPrimaryKeys({
-      sessionHandle: this.sessionHandle,
-      catalogName: request.catalogName,
-      schemaName: request.schemaName,
-      tableName: request.tableName,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Request information about foreign keys between two tables
-   * @public
-   * @param request
-   * @returns DBSQLOperation
-   */
-  public async getCrossReference(request: CrossReferenceRequest): Promise<IOperation> {
-    await this.failIfClosed();
-    const driver = await this.context.getDriver();
-    const clientConfig = this.context.getConfig();
- 
-    const operationPromise = driver.getCrossReference({
-      sessionHandle: this.sessionHandle,
-      parentCatalogName: request.parentCatalogName,
-      parentSchemaName: request.parentSchemaName,
-      parentTableName: request.parentTableName,
-      foreignCatalogName: request.foreignCatalogName,
-      foreignSchemaName: request.foreignSchemaName,
-      foreignTableName: request.foreignTableName,
-      runAsync: this.getRunAsyncForMetadataOperations(),
-      ...getDirectResultsOptions(request.maxRows, clientConfig),
-    });
-    const response = await this.handleResponse(operationPromise);
-    return this.createOperation(response);
-  }
- 
-  /**
-   * Closes the session
-   * @public
-   * @returns Operation status
-   */
-  public async close(): Promise<Status> {
-    if (!this.isOpen) {
-      return Status.success();
-    }
- 
-    // Close owned operations one by one, removing successfully closed ones from the list
-    await this.operations.closeAll();
- 
-    const driver = await this.context.getDriver();
-    const response = await driver.closeSession({
-      sessionHandle: this.sessionHandle,
-    });
-    // check status for being successful
-    Status.assert(response.status);
- 
-    // notify owner connection
-    this.onClose?.();
-    this.isOpen = false;
- 
-    this.context.getLogger().log(LogLevel.debug, `Session closed with id: ${this.id}`);
-    return new Status(response.status);
-  }
- 
-  private createOperation(response: OperationResponseShape): DBSQLOperation {
-    Status.assert(response.status);
-    const handle = definedOrError(response.operationHandle);
-    const operation = new DBSQLOperation({
-      handle,
-      directResults: response.directResults,
-      context: this.context,
-    });
- 
-    this.operations.add(operation);
- 
-    return operation;
-  }
- 
-  private async failIfClosed(): Promise<void> {
-    if (!this.isOpen) {
-      throw new HiveDriverError('The session was closed or has expired');
-    }
-  }
- 
-  private async handleResponse<T>(requestPromise: Promise<T>): Promise<T> {
-    // Currently, after being closed sessions remains usable - server will not
-    // error out when trying to run operations on closed session. So it's
-    // basically useless to process any errors here
-    const result = await requestPromise;
-    await this.failIfClosed();
-    return result;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html deleted file mode 100644 index ad593bb1..00000000 --- a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts.html +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/DatabricksOAuth AuthorizationCode.ts

-
- -
- 9.72% - Statements - 7/72 -
- - -
- 0% - Branches - 0/23 -
- - -
- 0% - Functions - 0/18 -
- - -
- 9.72% - Lines - 7/72 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -1921x -1x -1x -1x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import http, { IncomingMessage, Server, ServerResponse } from 'http';
-import { BaseClient, CallbackParamsType, generators } from 'openid-client';
-import open from 'open';
-import { LogLevel } from '../../../contracts/IDBSQLLogger';
-import { OAuthScopes, scopeDelimiter } from './OAuthScope';
-import IClientContext from '../../../contracts/IClientContext';
-import AuthenticationError from '../../../errors/AuthenticationError';
- 
-export type DefaultOpenAuthUrlCallback = (authUrl: string) => Promise<void>;
- 
-export type OpenAuthUrlCallback = (authUrl: string, defaultOpenAuthUrl: DefaultOpenAuthUrlCallback) => Promise<void>;
- 
-export interface AuthorizationCodeOptions {
-  client: BaseClient;
-  ports: Array<number>;
-  context: IClientContext;
-  openAuthUrl?: OpenAuthUrlCallback;
-}
- 
-async function defaultOpenAuthUrl(authUrl: string): Promise<void> {
-  await open(authUrl);
-}
- 
-export interface AuthorizationCodeFetchResult {
-  code: string;
-  verifier: string;
-  redirectUri: string;
-}
- 
-export default class AuthorizationCode {
-  private readonly context: IClientContext;
- 
-  private readonly client: BaseClient;
- 
-  private readonly host: string = 'localhost';
- 
-  private readonly options: AuthorizationCodeOptions;
- 
-  constructor(options: AuthorizationCodeOptions) {
-    this.client = options.client;
-    this.context = options.context;
-    this.options = options;
-  }
- 
-  public async fetch(scopes: OAuthScopes): Promise<AuthorizationCodeFetchResult> {
-    const verifierString = generators.codeVerifier(32);
-    const challengeString = generators.codeChallenge(verifierString);
-    const state = generators.state(16);
- 
-    let receivedParams: CallbackParamsType | undefined;
- 
-    const server = await this.createServer((req, res) => {
-      const params = this.client.callbackParams(req);
-      if (params.state === state) {
-        receivedParams = params;
-        res.writeHead(200);
-        res.end(this.renderCallbackResponse());
-        server.stop();
-      } else {
-        res.writeHead(404);
-        res.end();
-      }
-    });
- 
-    const redirectUri = `http://${server.host}:${server.port}`;
-    const authUrl = this.client.authorizationUrl({
-      response_type: 'code',
-      response_mode: 'query',
-      scope: scopes.join(scopeDelimiter),
-      code_challenge: challengeString,
-      code_challenge_method: 'S256',
-      state,
-      redirect_uri: redirectUri,
-    });
- 
-    const openAuthUrl = this.options.openAuthUrl ?? defaultOpenAuthUrl;
-    await openAuthUrl(authUrl, defaultOpenAuthUrl);
-    await server.stopped();
- 
-    if (!receivedParams || !receivedParams.code) {
-      if (receivedParams?.error) {
-        const errorMessage = `OAuth error: ${receivedParams.error} ${receivedParams.error_description}`;
-        throw new AuthenticationError(errorMessage);
-      }
-      throw new AuthenticationError(`No path parameters were returned to the callback at ${redirectUri}`);
-    }
- 
-    return { code: receivedParams.code, verifier: verifierString, redirectUri };
-  }
- 
-  private async createServer(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) {
-    for (const port of this.options.ports) {
-      const host = this.host; // eslint-disable-line prefer-destructuring
-      try {
-        const server = await this.startServer(host, port, requestHandler); // eslint-disable-line no-await-in-loop
-        this.context.getLogger().log(LogLevel.info, `Listening for OAuth authorization callback at ${host}:${port}`);
- 
-        let resolveStopped: () => void;
-        let rejectStopped: (reason?: any) => void;
-        const stoppedPromise = new Promise<void>((resolve, reject) => {
-          resolveStopped = resolve;
-          rejectStopped = reject;
-        });
- 
-        return {
-          host,
-          port,
-          server,
-          stop: () => this.stopServer(server).then(resolveStopped).catch(rejectStopped),
-          stopped: () => stoppedPromise,
-        };
-      } catch (error) {
-        // if port already in use - try another one, otherwise re-throw an exception
-        if (error instanceof Error && 'code' in error && error.code === 'EADDRINUSE') {
-          this.context.getLogger().log(LogLevel.debug, `Failed to start server at ${host}:${port}: ${error.code}`);
-        } else {
-          throw error;
-        }
-      }
-    }
- 
-    throw new AuthenticationError('Failed to start server: all ports are in use');
-  }
- 
-  private createHttpServer(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) {
-    return http.createServer(requestHandler);
-  }
- 
-  private async startServer(
-    host: string,
-    port: number,
-    requestHandler: (req: IncomingMessage, res: ServerResponse) => void,
-  ): Promise<Server> {
-    const server = this.createHttpServer(requestHandler);
- 
-    return new Promise((resolve, reject) => {
-      const errorListener = (error: Error) => {
-        server.off('error', errorListener);
-        reject(error);
-      };
- 
-      server.on('error', errorListener);
-      server.listen(port, host, () => {
-        server.off('error', errorListener);
-        resolve(server);
-      });
-    });
-  }
- 
-  private async stopServer(server: Server): Promise<void> {
-    if (!server.listening) {
-      return;
-    }
- 
-    return new Promise((resolve, reject) => {
-      const errorListener = (error: Error) => {
-        server.off('error', errorListener);
-        reject(error);
-      };
- 
-      server.on('error', errorListener);
-      server.close(() => {
-        server.off('error', errorListener);
-        resolve();
-      });
-    });
-  }
- 
-  private renderCallbackResponse(): string {
-    const applicationName = 'Databricks Sql Connector';
- 
-    return `<html lang="en">
-<head>
-  <title>Close this Tab</title>
-  <style>
-    body {
-      font-family: "Barlow", Helvetica, Arial, sans-serif;
-      padding: 20px;
-      background-color: #f3f3f3;
-    }
-  </style>
-</head>
-<body>
-  <h1>Please close this tab.</h1>
-  <p>
-    The ${applicationName} received a response. You may close this tab.
-  </p>
-</body>
-</html>`;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html deleted file mode 100644 index dacd09bb..00000000 --- a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthManager.ts.html +++ /dev/null @@ -1,1036 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthManager.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/DatabricksOAuth OAuthManager.ts

-
- -
- 14.65% - Statements - 17/116 -
- - -
- 2.94% - Branches - 2/68 -
- - -
- 3.84% - Functions - 1/26 -
- - -
- 15.45% - Lines - 17/110 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318  -1x -1x -1x -1x -1x -1x -  -  -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -1x -  -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import http from 'http';
-import { Issuer, BaseClient, custom } from 'openid-client';
-import AuthenticationError from '../../../errors/AuthenticationError';
-import { LogLevel } from '../../../contracts/IDBSQLLogger';
-import OAuthToken from './OAuthToken';
-import AuthorizationCode from './AuthorizationCode';
-import { OAuthScope, OAuthScopes, scopeDelimiter } from './OAuthScope';
-import IClientContext from '../../../contracts/IClientContext';
- 
-export enum OAuthFlow {
-  U2M = 'U2M',
-  M2M = 'M2M',
-}
- 
-export interface OAuthManagerOptions {
-  flow: OAuthFlow;
-  host: string;
-  callbackPorts?: Array<number>;
-  clientId?: string;
-  azureTenantId?: string;
-  clientSecret?: string;
-  useDatabricksOAuthInAzure?: boolean;
-  context: IClientContext;
-}
- 
-function getDatabricksOIDCUrl(host: string): string {
-  const schema = host.startsWith('https://') ? '' : 'https://';
-  const trailingSlash = host.endsWith('/') ? '' : '/';
-  return `${schema}${host}${trailingSlash}oidc`;
-}
- 
-export default abstract class OAuthManager {
-  protected readonly context: IClientContext;
- 
-  protected readonly options: OAuthManagerOptions;
- 
-  protected agent?: http.Agent;
- 
-  protected issuer?: Issuer;
- 
-  protected client?: BaseClient;
- 
-  constructor(options: OAuthManagerOptions) {
-    this.options = options;
-    this.context = options.context;
-  }
- 
-  protected abstract getOIDCConfigUrl(): string;
- 
-  protected abstract getAuthorizationUrl(): string;
- 
-  protected abstract getClientId(): string;
- 
-  protected abstract getCallbackPorts(): Array<number>;
- 
-  protected abstract getScopes(requestedScopes: OAuthScopes): OAuthScopes;
- 
-  protected async getClient(): Promise<BaseClient> {
-    // Obtain http agent each time when we need an OAuth client
-    // to ensure that we always use a valid agent instance
-    const connectionProvider = await this.context.getConnectionProvider();
-    this.agent = await connectionProvider.getAgent();
- 
-    const getHttpOptions = () => ({
-      agent: this.agent,
-    });
- 
-    if (!this.issuer) {
-      // To use custom http agent in Issuer.discover(), we'd have to set Issuer[custom.http_options].
-      // However, that's a static field, and if multiple instances of OAuthManager used, race condition
-      // may occur when they simultaneously override that field and then try to use Issuer.discover().
-      // Therefore we create a local class derived from Issuer, and set that field for it, thus making
-      // sure that it will not interfere with other instances (or other code that may use Issuer)
-      class CustomIssuer extends Issuer {
-        static [custom.http_options] = getHttpOptions;
-      }
- 
-      const issuer = await CustomIssuer.discover(this.getOIDCConfigUrl());
- 
-      // Overwrite `authorization_endpoint` in default config (specifically needed for Azure flow
-      // where this URL has to be different)
-      this.issuer = new Issuer({
-        ...issuer.metadata,
-        authorization_endpoint: this.getAuthorizationUrl(),
-      });
- 
-      this.issuer[custom.http_options] = getHttpOptions;
-    }
- 
-    if (!this.client) {
-      this.client = new this.issuer.Client({
-        client_id: this.getClientId(),
-        client_secret: this.options.clientSecret,
-        token_endpoint_auth_method: this.options.clientSecret === undefined ? 'none' : 'client_secret_basic',
-      });
- 
-      this.client[custom.http_options] = getHttpOptions;
-    }
- 
-    return this.client;
-  }
- 
-  private async refreshAccessTokenU2M(token: OAuthToken): Promise<OAuthToken> {
-    if (!token.refreshToken) {
-      const message = `OAuth access token expired on ${token.expirationTime}.`;
-      this.context.getLogger().log(LogLevel.error, message);
-      throw new AuthenticationError(message);
-    }
- 
-    // Try to refresh using the refresh token
-    this.context
-      .getLogger()
-      .log(LogLevel.debug, `Attempting to refresh OAuth access token that expired on ${token.expirationTime}`);
- 
-    const client = await this.getClient();
-    const { access_token: accessToken, refresh_token: refreshToken } = await client.refresh(token.refreshToken);
-    if (!accessToken || !refreshToken) {
-      throw new AuthenticationError('Failed to refresh token: invalid response');
-    }
-    return new OAuthToken(accessToken, refreshToken, token.scopes);
-  }
- 
-  private async refreshAccessTokenM2M(token: OAuthToken): Promise<OAuthToken> {
-    return this.getTokenM2M(token.scopes ?? []);
-  }
- 
-  public async refreshAccessToken(token: OAuthToken): Promise<OAuthToken> {
-    try {
-      if (!token.hasExpired) {
-        // The access token is fine. Just return it.
-        return token;
-      }
-    } catch (error) {
-      this.context.getLogger().log(LogLevel.error, `${error}`);
-      throw error;
-    }
- 
-    switch (this.options.flow) {
-      case OAuthFlow.U2M:
-        return this.refreshAccessTokenU2M(token);
-      case OAuthFlow.M2M:
-        return this.refreshAccessTokenM2M(token);
-      // no default
-    }
-  }
- 
-  private async getTokenU2M(scopes: OAuthScopes): Promise<OAuthToken> {
-    const client = await this.getClient();
- 
-    const authCode = new AuthorizationCode({
-      client,
-      ports: this.getCallbackPorts(),
-      context: this.context,
-    });
- 
-    const mappedScopes = this.getScopes(scopes);
- 
-    const { code, verifier, redirectUri } = await authCode.fetch(mappedScopes);
- 
-    const { access_token: accessToken, refresh_token: refreshToken } = await client.grant({
-      grant_type: 'authorization_code',
-      code,
-      code_verifier: verifier,
-      redirect_uri: redirectUri,
-    });
- 
-    if (!accessToken) {
-      throw new AuthenticationError('Failed to fetch access token');
-    }
-    return new OAuthToken(accessToken, refreshToken, mappedScopes);
-  }
- 
-  private async getTokenM2M(scopes: OAuthScopes): Promise<OAuthToken> {
-    const client = await this.getClient();
- 
-    const mappedScopes = this.getScopes(scopes);
- 
-    // M2M flow doesn't really support token refreshing, and refresh should not be available
-    // in response. Each time access token expires, client can just acquire a new one using
-    // client secret. Here we explicitly return access token only as a sign that we're not going
-    // to use refresh token for M2M flow anywhere later
-    const { access_token: accessToken } = await client.grant({
-      grant_type: 'client_credentials',
-      scope: mappedScopes.join(scopeDelimiter),
-    });
- 
-    if (!accessToken) {
-      throw new AuthenticationError('Failed to fetch access token');
-    }
-    return new OAuthToken(accessToken, undefined, mappedScopes);
-  }
- 
-  public async getToken(scopes: OAuthScopes): Promise<OAuthToken> {
-    switch (this.options.flow) {
-      case OAuthFlow.U2M:
-        return this.getTokenU2M(scopes);
-      case OAuthFlow.M2M:
-        return this.getTokenM2M(scopes);
-      // no default
-    }
-  }
- 
-  public static getManager(options: OAuthManagerOptions): OAuthManager {
-    // normalize
-    const host = options.host.toLowerCase().replace('https://', '').split('/')[0];
- 
-    const awsDomains = ['.cloud.databricks.com', '.dev.databricks.com'];
-    const isAWSDomain = awsDomains.some((domain) => host.endsWith(domain));
-    if (isAWSDomain) {
-      // eslint-disable-next-line @typescript-eslint/no-use-before-define
-      return new DatabricksOAuthManager(options);
-    }
- 
-    const gcpDomains = ['.gcp.databricks.com'];
-    const isGCPDomain = gcpDomains.some((domain) => host.endsWith(domain));
-    if (isGCPDomain) {
-      // eslint-disable-next-line @typescript-eslint/no-use-before-define
-      return new DatabricksOAuthManager(options);
-    }
- 
-    if (options.useDatabricksOAuthInAzure) {
-      const azureDomains = ['.azuredatabricks.net', '.databricks.azure.cn'];
-      const isAzureDomain = azureDomains.some((domain) => host.endsWith(domain));
-      if (isAzureDomain) {
-        // eslint-disable-next-line @typescript-eslint/no-use-before-define
-        return new DatabricksOAuthManager(options);
-      }
-    } else {
-      const azureDomains = ['.azuredatabricks.net', '.databricks.azure.us', '.databricks.azure.cn'];
-      const isAzureDomain = azureDomains.some((domain) => host.endsWith(domain));
-      if (isAzureDomain) {
-        // eslint-disable-next-line @typescript-eslint/no-use-before-define
-        return new AzureOAuthManager(options);
-      }
-    }
- 
-    throw new AuthenticationError(`OAuth is not supported for ${options.host}`);
-  }
-}
- 
-// Databricks InHouse OAuth Manager
-export class DatabricksOAuthManager extends OAuthManager {
-  public static defaultClientId = 'databricks-sql-connector';
- 
-  public static defaultCallbackPorts = [8030];
- 
-  protected getOIDCConfigUrl(): string {
-    return `${getDatabricksOIDCUrl(this.options.host)}/.well-known/oauth-authorization-server`;
-  }
- 
-  protected getAuthorizationUrl(): string {
-    return `${getDatabricksOIDCUrl(this.options.host)}/oauth2/v2.0/authorize`;
-  }
- 
-  protected getClientId(): string {
-    return this.options.clientId ?? DatabricksOAuthManager.defaultClientId;
-  }
- 
-  protected getCallbackPorts(): Array<number> {
-    return this.options.callbackPorts ?? DatabricksOAuthManager.defaultCallbackPorts;
-  }
- 
-  protected getScopes(requestedScopes: OAuthScopes): OAuthScopes {
-    if (this.options.flow === OAuthFlow.M2M) {
-      // this is the only allowed scope for M2M flow
-      return [OAuthScope.allAPIs];
-    }
-    return requestedScopes;
-  }
-}
- 
-export class AzureOAuthManager extends OAuthManager {
-  public static defaultClientId = '96eecda7-19ea-49cc-abb5-240097d554f5';
- 
-  public static defaultCallbackPorts = [8030];
- 
-  public static datatricksAzureApp = '2ff814a6-3304-4ab8-85cb-cd0e6f879c1d';
- 
-  protected getOIDCConfigUrl(): string {
-    return 'https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration';
-  }
- 
-  protected getAuthorizationUrl(): string {
-    return `${getDatabricksOIDCUrl(this.options.host)}/oauth2/v2.0/authorize`;
-  }
- 
-  protected getClientId(): string {
-    return this.options.clientId ?? AzureOAuthManager.defaultClientId;
-  }
- 
-  protected getCallbackPorts(): Array<number> {
-    return this.options.callbackPorts ?? AzureOAuthManager.defaultCallbackPorts;
-  }
- 
-  protected getScopes(requestedScopes: OAuthScopes): OAuthScopes {
-    // There is no corresponding scopes in Azure, instead, access control will be delegated to Databricks
-    const tenantId = this.options.azureTenantId ?? AzureOAuthManager.datatricksAzureApp;
- 
-    const azureScopes = [];
- 
-    switch (this.options.flow) {
-      case OAuthFlow.U2M:
-        azureScopes.push(`${tenantId}/user_impersonation`);
-        break;
-      case OAuthFlow.M2M:
-        azureScopes.push(`${tenantId}/.default`);
-        break;
-      // no default
-    }
- 
-    if (requestedScopes.includes(OAuthScope.offlineAccess)) {
-      azureScopes.push(OAuthScope.offlineAccess);
-    }
- 
-    return azureScopes;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html deleted file mode 100644 index 76fb812e..00000000 --- a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/DatabricksOAuth OAuthPersistence.ts

-
- -
- 25% - Statements - 1/4 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/3 -
- - -
- 25% - Lines - 1/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  - 
import OAuthToken from './OAuthToken';
- 
-export default interface OAuthPersistence {
-  persist(host: string, token: OAuthToken): Promise<void>;
- 
-  read(host: string): Promise<OAuthToken | undefined>;
-}
- 
-export class OAuthPersistenceCache implements OAuthPersistence {
-  private tokens: Record<string, OAuthToken | undefined> = {};
- 
-  async persist(host: string, token: OAuthToken) {
-    this.tokens[host] = token;
-  }
- 
-  async read(host: string) {
-    return this.tokens[host];
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html deleted file mode 100644 index 0b965e0a..00000000 --- a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthScope.ts.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthScope.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/DatabricksOAuth OAuthScope.ts

-
- -
- 100% - Statements - 6/6 -
- - -
- 100% - Branches - 2/2 -
- - -
- 100% - Functions - 1/1 -
- - -
- 100% - Lines - 6/6 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -121x -1x -1x -1x -  -  -  -  -1x -  -1x - 
export enum OAuthScope {
-  offlineAccess = 'offline_access',
-  SQL = 'sql',
-  allAPIs = 'all-apis',
-}
- 
-export type OAuthScopes = Array<string>;
- 
-export const defaultOAuthScopes: OAuthScopes = [OAuthScope.SQL, OAuthScope.offlineAccess];
- 
-export const scopeDelimiter = ' ';
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html deleted file mode 100644 index c6e74dab..00000000 --- a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/OAuthToken.ts.html +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/DatabricksOAuth/OAuthToken.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/DatabricksOAuth OAuthToken.ts

-
- -
- 7.14% - Statements - 1/14 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/6 -
- - -
- 7.14% - Lines - 1/14 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { OAuthScopes } from './OAuthScope';
- 
-export default class OAuthToken {
-  private readonly _accessToken: string;
- 
-  private readonly _refreshToken?: string;
- 
-  private readonly _scopes?: OAuthScopes;
- 
-  private _expirationTime?: number;
- 
-  constructor(accessToken: string, refreshToken?: string, scopes?: OAuthScopes) {
-    this._accessToken = accessToken;
-    this._refreshToken = refreshToken;
-    this._scopes = scopes;
-  }
- 
-  get accessToken(): string {
-    return this._accessToken;
-  }
- 
-  get refreshToken(): string | undefined {
-    return this._refreshToken;
-  }
- 
-  get scopes(): OAuthScopes | undefined {
-    return this._scopes;
-  }
- 
-  get expirationTime(): number {
-    // This token has already been verified, and we are just parsing it.
-    // If it has been tampered with, it will be rejected on the server side.
-    // This avoids having to fetch the public key from the issuer and perform
-    // an unnecessary signature verification.
-    if (this._expirationTime === undefined) {
-      const accessTokenPayload = Buffer.from(this._accessToken.split('.')[1], 'base64').toString('utf8');
-      const decoded = JSON.parse(accessTokenPayload);
-      this._expirationTime = Number(decoded.exp);
-    }
-    return this._expirationTime;
-  }
- 
-  get hasExpired(): boolean {
-    const now = Math.floor(Date.now() / 1000); // convert it to seconds
-    return this.expirationTime <= now;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html deleted file mode 100644 index a1e28835..00000000 --- a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/DatabricksOAuth - - - - - - - - - -
-
-

All files lib/connection/auth/DatabricksOAuth

-
- -
- 16.01% - Statements - 37/231 -
- - -
- 3.73% - Branches - 4/107 -
- - -
- 3.44% - Functions - 2/58 -
- - -
- 16.44% - Lines - 37/225 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
AuthorizationCode.ts -
-
9.72%7/720%0/230%0/189.72%7/72
OAuthManager.ts -
-
14.65%17/1162.94%2/683.84%1/2615.45%17/110
OAuthPersistence.ts -
-
25%1/4100%0/00%0/325%1/4
OAuthScope.ts -
-
100%6/6100%2/2100%1/1100%6/6
OAuthToken.ts -
-
7.14%1/140%0/20%0/67.14%1/14
index.ts -
-
26.31%5/190%0/120%0/426.31%5/19
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html b/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html deleted file mode 100644 index 15938aba..00000000 --- a/coverage/lcov-report/lib/connection/auth/DatabricksOAuth/index.ts.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/DatabricksOAuth/index.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/DatabricksOAuth index.ts

-
- -
- 26.31% - Statements - 5/19 -
- - -
- 0% - Branches - 0/12 -
- - -
- 0% - Functions - 0/4 -
- - -
- 26.31% - Lines - 5/19 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56  -  -1x -1x -1x -  -  -1x -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { HeadersInit } from 'node-fetch';
-import IAuthentication from '../../contracts/IAuthentication';
-import OAuthPersistence, { OAuthPersistenceCache } from './OAuthPersistence';
-import OAuthManager, { OAuthManagerOptions, OAuthFlow } from './OAuthManager';
-import { OAuthScopes, defaultOAuthScopes } from './OAuthScope';
-import IClientContext from '../../../contracts/IClientContext';
- 
-export { OAuthFlow };
- 
-export interface DatabricksOAuthOptions extends OAuthManagerOptions {
-  scopes?: OAuthScopes;
-  persistence?: OAuthPersistence;
-  headers?: HeadersInit;
-}
- 
-export default class DatabricksOAuth implements IAuthentication {
-  private readonly context: IClientContext;
- 
-  private readonly options: DatabricksOAuthOptions;
- 
-  private manager?: OAuthManager;
- 
-  private readonly defaultPersistence = new OAuthPersistenceCache();
- 
-  constructor(options: DatabricksOAuthOptions) {
-    this.context = options.context;
-    this.options = options;
-  }
- 
-  public async authenticate(): Promise<HeadersInit> {
-    const { host, scopes, headers } = this.options;
- 
-    const persistence = this.options.persistence ?? this.defaultPersistence;
- 
-    let token = await persistence.read(host);
-    if (!token) {
-      token = await this.getManager().getToken(scopes ?? defaultOAuthScopes);
-    }
- 
-    token = await this.getManager().refreshAccessToken(token);
-    await persistence.persist(host, token);
- 
-    return {
-      ...headers,
-      Authorization: `Bearer ${token.accessToken}`,
-    };
-  }
- 
-  private getManager(): OAuthManager {
-    if (!this.manager) {
-      this.manager = OAuthManager.getManager(this.options);
-    }
-    return this.manager;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html b/coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html deleted file mode 100644 index 78a38ff2..00000000 --- a/coverage/lcov-report/lib/connection/auth/PlainHttpAuthentication.ts.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/PlainHttpAuthentication.ts - - - - - - - - - -
-
-

All files / lib/connection/auth PlainHttpAuthentication.ts

-
- -
- 16.66% - Statements - 1/6 -
- - -
- 0% - Branches - 0/20 -
- - -
- 0% - Functions - 0/2 -
- - -
- 16.66% - Lines - 1/6 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { HeadersInit } from 'node-fetch';
-import IAuthentication from '../contracts/IAuthentication';
-import IClientContext from '../../contracts/IClientContext';
- 
-interface PlainHttpAuthenticationOptions {
-  username?: string;
-  password?: string;
-  headers?: HeadersInit;
-  context: IClientContext;
-}
- 
-export default class PlainHttpAuthentication implements IAuthentication {
-  private readonly context: IClientContext;
- 
-  private readonly username: string;
- 
-  private readonly password: string;
- 
-  private readonly headers: HeadersInit;
- 
-  constructor(options: PlainHttpAuthenticationOptions) {
-    this.context = options.context;
-    this.username = options?.username || 'anonymous';
-    this.password = options?.password ?? 'anonymous';
-    this.headers = options?.headers || {};
-  }
- 
-  public async authenticate(): Promise<HeadersInit> {
-    return {
-      ...this.headers,
-      Authorization: `Bearer ${this.password}`,
-    };
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/index.html b/coverage/lcov-report/lib/connection/auth/index.html deleted file mode 100644 index ec2fc87e..00000000 --- a/coverage/lcov-report/lib/connection/auth/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth - - - - - - - - - -
-
-

All files lib/connection/auth

-
- -
- 16.66% - Statements - 1/6 -
- - -
- 0% - Branches - 0/20 -
- - -
- 0% - Functions - 0/2 -
- - -
- 16.66% - Lines - 1/6 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
PlainHttpAuthentication.ts -
-
16.66%1/60%0/200%0/216.66%1/6
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html deleted file mode 100644 index 2bded7fb..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/CachedTokenProvider.ts.html +++ /dev/null @@ -1,379 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/CachedTokenProvider.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider CachedTokenProvider.ts

-
- -
- 7.69% - Statements - 2/26 -
- - -
- 0% - Branches - 0/16 -
- - -
- 0% - Functions - 0/6 -
- - -
- 7.69% - Lines - 2/26 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99  -  -  -  -  -  -  -1x -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import ITokenProvider from './ITokenProvider';
-import Token from './Token';
- 
-/**
- * Default refresh threshold in milliseconds (5 minutes).
- * Tokens will be refreshed when they are within this threshold of expiring.
- */
-const DEFAULT_REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
- 
-/**
- * A token provider that wraps another provider with automatic caching.
- * Tokens are cached and reused until they are close to expiring.
- */
-export default class CachedTokenProvider implements ITokenProvider {
-  private readonly baseProvider: ITokenProvider;
- 
-  private readonly refreshThresholdMs: number;
- 
-  private cache: Token | null = null;
- 
-  private refreshPromise: Promise<Token> | null = null;
- 
-  /**
-   * Creates a new CachedTokenProvider.
-   * @param baseProvider - The underlying token provider to cache
-   * @param options - Optional configuration
-   * @param options.refreshThresholdMs - Refresh tokens this many ms before expiry (default: 5 minutes)
-   */
-  constructor(
-    baseProvider: ITokenProvider,
-    options?: {
-      refreshThresholdMs?: number;
-    },
-  ) {
-    this.baseProvider = baseProvider;
-    this.refreshThresholdMs = options?.refreshThresholdMs ?? DEFAULT_REFRESH_THRESHOLD_MS;
-  }
- 
-  async getToken(): Promise<Token> {
-    // Return cached token if it's still valid
-    if (this.cache && !this.shouldRefresh(this.cache)) {
-      return this.cache;
-    }
- 
-    // If already refreshing, wait for that to complete
-    if (this.refreshPromise) {
-      return this.refreshPromise;
-    }
- 
-    // Start refresh
-    this.refreshPromise = this.refreshToken();
- 
-    try {
-      const token = await this.refreshPromise;
-      return token;
-    } finally {
-      this.refreshPromise = null;
-    }
-  }
- 
-  getName(): string {
-    return `cached[${this.baseProvider.getName()}]`;
-  }
- 
-  /**
-   * Clears the cached token, forcing a refresh on the next getToken() call.
-   */
-  clearCache(): void {
-    this.cache = null;
-  }
- 
-  /**
-   * Determines if the token should be refreshed.
-   * @param token - The token to check
-   * @returns true if the token should be refreshed
-   */
-  private shouldRefresh(token: Token): boolean {
-    // If no expiration is known, don't refresh proactively
-    if (!token.expiresAt) {
-      return false;
-    }
- 
-    const now = Date.now();
-    const expiresAtMs = token.expiresAt.getTime();
-    const refreshAtMs = expiresAtMs - this.refreshThresholdMs;
- 
-    return now >= refreshAtMs;
-  }
- 
-  /**
-   * Fetches a new token from the base provider and caches it.
-   */
-  private async refreshToken(): Promise<Token> {
-    const token = await this.baseProvider.getToken();
-    this.cache = token;
-    return token;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html deleted file mode 100644 index 99c33c49..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/ExternalTokenProvider.ts.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/ExternalTokenProvider.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider ExternalTokenProvider.ts

-
- -
- 20% - Statements - 2/10 -
- - -
- 0% - Branches - 0/18 -
- - -
- 0% - Functions - 0/3 -
- - -
- 20% - Lines - 2/10 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53  -1x -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import ITokenProvider from './ITokenProvider';
-import Token from './Token';
- 
-/**
- * Type for the callback function that retrieves tokens from external sources.
- */
-export type TokenCallback = () => Promise<string>;
- 
-/**
- * A token provider that delegates token retrieval to an external callback function.
- * Useful for integrating with secret managers, vaults, or other token sources.
- */
-export default class ExternalTokenProvider implements ITokenProvider {
-  private readonly getTokenCallback: TokenCallback;
- 
-  private readonly parseJWT: boolean;
- 
-  private readonly providerName: string;
- 
-  /**
-   * Creates a new ExternalTokenProvider.
-   * @param getToken - Callback function that returns the access token string
-   * @param options - Optional configuration
-   * @param options.parseJWT - If true, attempt to extract expiration from JWT payload (default: true)
-   * @param options.name - Custom name for this provider (default: "ExternalTokenProvider")
-   */
-  constructor(
-    getToken: TokenCallback,
-    options?: {
-      parseJWT?: boolean;
-      name?: string;
-    },
-  ) {
-    this.getTokenCallback = getToken;
-    this.parseJWT = options?.parseJWT ?? true;
-    this.providerName = options?.name ?? 'ExternalTokenProvider';
-  }
- 
-  async getToken(): Promise<Token> {
-    const accessToken = await this.getTokenCallback();
- 
-    if (this.parseJWT) {
-      return Token.fromJWT(accessToken);
-    }
- 
-    return new Token(accessToken);
-  }
- 
-  getName(): string {
-    return this.providerName;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html deleted file mode 100644 index 54f2c70d..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/FederationProvider.ts.html +++ /dev/null @@ -1,889 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/FederationProvider.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider FederationProvider.ts

-
- -
- 16% - Statements - 12/75 -
- - -
- 0% - Branches - 0/46 -
- - -
- 0% - Functions - 0/12 -
- - -
- 16.21% - Lines - 12/74 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -2691x -  -1x -1x -  -  -  -  -1x -  -  -  -  -1x -  -  -  -  -1x -  -  -  -  -1x -  -  -  -  -1x -  -  -  -  -1x -  -  -  -  -1x -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import fetch from 'node-fetch';
-import ITokenProvider from './ITokenProvider';
-import Token from './Token';
-import { getJWTIssuer, isSameHost } from './utils';
- 
-/**
- * Token exchange endpoint path for Databricks OIDC.
- */
-const TOKEN_EXCHANGE_ENDPOINT = '/oidc/v1/token';
- 
-/**
- * Grant type for RFC 8693 token exchange.
- */
-const TOKEN_EXCHANGE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange';
- 
-/**
- * Subject token type for JWT tokens.
- */
-const SUBJECT_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:jwt';
- 
-/**
- * Default scope for SQL operations.
- */
-const DEFAULT_SCOPE = 'sql';
- 
-/**
- * Timeout for token exchange requests in milliseconds.
- */
-const REQUEST_TIMEOUT_MS = 30000;
- 
-/**
- * Maximum number of retry attempts for transient errors.
- */
-const MAX_RETRY_ATTEMPTS = 3;
- 
-/**
- * Base delay in milliseconds for exponential backoff.
- */
-const RETRY_BASE_DELAY_MS = 1000;
- 
-/**
- * HTTP status codes that are considered retryable.
- */
-const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
- 
-/**
- * Error class for token exchange failures that includes the HTTP status code.
- */
-class TokenExchangeError extends Error {
-  readonly statusCode: number;
- 
-  constructor(message: string, statusCode: number) {
-    super(message);
-    this.name = 'TokenExchangeError';
-    this.statusCode = statusCode;
-  }
-}
- 
-/**
- * A token provider that wraps another provider with automatic token federation.
- * When the base provider returns a token from a different issuer, this provider
- * exchanges it for a Databricks-compatible token using RFC 8693.
- */
-export default class FederationProvider implements ITokenProvider {
-  private readonly baseProvider: ITokenProvider;
- 
-  private readonly databricksHost: string;
- 
-  private readonly clientId?: string;
- 
-  private readonly returnOriginalTokenOnFailure: boolean;
- 
-  /**
-   * Creates a new FederationProvider.
-   * @param baseProvider - The underlying token provider
-   * @param databricksHost - The Databricks workspace host URL
-   * @param options - Optional configuration
-   * @param options.clientId - Client ID for M2M/service principal federation
-   * @param options.returnOriginalTokenOnFailure - Return original token if exchange fails (default: true)
-   */
-  constructor(
-    baseProvider: ITokenProvider,
-    databricksHost: string,
-    options?: {
-      clientId?: string;
-      returnOriginalTokenOnFailure?: boolean;
-    },
-  ) {
-    this.baseProvider = baseProvider;
-    this.databricksHost = databricksHost;
-    this.clientId = options?.clientId;
-    this.returnOriginalTokenOnFailure = options?.returnOriginalTokenOnFailure ?? true;
-  }
- 
-  async getToken(): Promise<Token> {
-    const token = await this.baseProvider.getToken();
- 
-    // Check if token needs exchange
-    if (!this.needsTokenExchange(token)) {
-      return token;
-    }
- 
-    // Attempt token exchange
-    try {
-      return await this.exchangeToken(token);
-    } catch (error) {
-      if (this.returnOriginalTokenOnFailure) {
-        // Fall back to original token
-        return token;
-      }
-      throw error;
-    }
-  }
- 
-  getName(): string {
-    return `federated[${this.baseProvider.getName()}]`;
-  }
- 
-  /**
-   * Determines if the token needs to be exchanged.
-   * @param token - The token to check
-   * @returns true if the token should be exchanged
-   */
-  private needsTokenExchange(token: Token): boolean {
-    const issuer = getJWTIssuer(token.accessToken);
- 
-    // If we can't extract the issuer, don't exchange (might not be a JWT)
-    if (!issuer) {
-      return false;
-    }
- 
-    // If the issuer is the same as Databricks host, no exchange needed
-    if (isSameHost(issuer, this.databricksHost)) {
-      return false;
-    }
- 
-    return true;
-  }
- 
-  /**
-   * Exchanges the token for a Databricks-compatible token using RFC 8693.
-   * Includes retry logic for transient errors with exponential backoff.
-   * @param token - The token to exchange
-   * @returns The exchanged token
-   */
-  private async exchangeToken(token: Token): Promise<Token> {
-    return this.exchangeTokenWithRetry(token, 0);
-  }
- 
-  /**
-   * Attempts a single token exchange request.
-   * @returns The exchanged token
-   */
-  private async attemptTokenExchange(body: string): Promise<Token> {
-    const url = this.buildExchangeUrl();
-    const controller = new AbortController();
-    const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
- 
-    try {
-      const response = await fetch(url, {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/x-www-form-urlencoded',
-        },
-        body,
-        signal: controller.signal,
-      });
- 
-      if (!response.ok) {
-        const errorText = await response.text();
-        const error = new TokenExchangeError(
-          `Token exchange failed: ${response.status} ${response.statusText} - ${errorText}`,
-          response.status,
-        );
-        throw error;
-      }
- 
-      const data = (await response.json()) as {
-        access_token?: string;
-        token_type?: string;
-        expires_in?: number;
-      };
- 
-      if (!data.access_token) {
-        throw new Error('Token exchange response missing access_token');
-      }
- 
-      // Calculate expiration from expires_in
-      let expiresAt: Date | undefined;
-      if (typeof data.expires_in === 'number') {
-        expiresAt = new Date(Date.now() + data.expires_in * 1000);
-      }
- 
-      return new Token(data.access_token, {
-        tokenType: data.token_type ?? 'Bearer',
-        expiresAt,
-      });
-    } finally {
-      clearTimeout(timeoutId);
-    }
-  }
- 
-  /**
-   * Recursively attempts token exchange with exponential backoff.
-   */
-  private async exchangeTokenWithRetry(token: Token, attempt: number): Promise<Token> {
-    const params = new URLSearchParams({
-      grant_type: TOKEN_EXCHANGE_GRANT_TYPE,
-      subject_token_type: SUBJECT_TOKEN_TYPE,
-      subject_token: token.accessToken,
-      scope: DEFAULT_SCOPE,
-    });
- 
-    if (this.clientId) {
-      params.append('client_id', this.clientId);
-    }
- 
-    try {
-      return await this.attemptTokenExchange(params.toString());
-    } catch (error) {
-      const canRetry = attempt < MAX_RETRY_ATTEMPTS && this.isRetryableError(error);
- 
-      if (!canRetry) {
-        throw error;
-      }
- 
-      // Exponential backoff: 1s, 2s, 4s
-      const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
-      await new Promise<void>((resolve) => {
-        setTimeout(resolve, delay);
-      });
- 
-      return this.exchangeTokenWithRetry(token, attempt + 1);
-    }
-  }
- 
-  /**
-   * Determines if an error is retryable (transient HTTP errors, network errors, timeouts).
-   */
-  private isRetryableError(error: unknown): boolean {
-    if (error instanceof TokenExchangeError) {
-      return RETRYABLE_STATUS_CODES.has(error.statusCode);
-    }
-    if (error instanceof Error) {
-      return error.name === 'AbortError' || error.name === 'FetchError';
-    }
-    return false;
-  }
- 
-  /**
-   * Builds the token exchange URL.
-   */
-  private buildExchangeUrl(): string {
-    let host = this.databricksHost;
- 
-    // Ensure host has a protocol
-    if (!host.includes('://')) {
-      host = `https://${host}`;
-    }
- 
-    // Remove trailing slash
-    if (host.endsWith('/')) {
-      host = host.slice(0, -1);
-    }
- 
-    return `${host}${TOKEN_EXCHANGE_ENDPOINT}`;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html deleted file mode 100644 index 0b389bd6..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/StaticTokenProvider.ts.html +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/StaticTokenProvider.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider StaticTokenProvider.ts

-
- -
- 28.57% - Statements - 2/7 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/4 -
- - -
- 28.57% - Lines - 2/7 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44  -1x -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import ITokenProvider from './ITokenProvider';
-import Token, { TokenOptions, TokenFromJWTOptions } from './Token';
- 
-/**
- * A token provider that returns a static token.
- * Useful for testing or when the token is obtained through external means.
- */
-export default class StaticTokenProvider implements ITokenProvider {
-  private readonly token: Token;
- 
-  /**
-   * Creates a new StaticTokenProvider.
-   * @param accessToken - The access token string
-   * @param options - Optional token configuration (tokenType, expiresAt, refreshToken, scopes)
-   */
-  constructor(accessToken: string, options?: TokenOptions) {
-    this.token = new Token(accessToken, options);
-  }
- 
-  /**
-   * Creates a StaticTokenProvider from a JWT string.
-   * The expiration time will be extracted from the JWT payload.
-   * @param jwt - The JWT token string
-   * @param options - Optional token configuration
-   */
-  static fromJWT(jwt: string, options?: TokenFromJWTOptions): StaticTokenProvider {
-    const token = Token.fromJWT(jwt, options);
-    return new StaticTokenProvider(token.accessToken, {
-      tokenType: token.tokenType,
-      expiresAt: token.expiresAt,
-      refreshToken: token.refreshToken,
-      scopes: token.scopes,
-    });
-  }
- 
-  async getToken(): Promise<Token> {
-    return this.token;
-  }
- 
-  getName(): string {
-    return 'StaticTokenProvider';
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html deleted file mode 100644 index 9838a9f8..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/Token.ts.html +++ /dev/null @@ -1,556 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/Token.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider Token.ts

-
- -
- 7.4% - Statements - 2/27 -
- - -
- 0% - Branches - 0/42 -
- - -
- 0% - Functions - 0/10 -
- - -
- 7.4% - Lines - 2/27 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { HeadersInit } from 'node-fetch';
- 
-/**
- * Safety buffer in seconds to consider a token expired before its actual expiration time.
- * This prevents using tokens that are about to expire during in-flight requests.
- */
-const EXPIRATION_BUFFER_SECONDS = 30;
- 
-/**
- * Options for creating a Token instance.
- */
-export interface TokenOptions {
-  /** The token type (e.g., "Bearer"). Defaults to "Bearer". */
-  tokenType?: string;
-  /** The expiration time of the token. */
-  expiresAt?: Date;
-  /** The refresh token, if available. */
-  refreshToken?: string;
-  /** The scopes associated with this token. */
-  scopes?: string[];
-}
- 
-/**
- * Options for creating a Token from a JWT string.
- * Does not include expiresAt since it is extracted from the JWT payload.
- */
-export type TokenFromJWTOptions = Omit<TokenOptions, 'expiresAt'>;
- 
-/**
- * Represents an access token with optional metadata and lifecycle management.
- */
-export default class Token {
-  private readonly _accessToken: string;
- 
-  private readonly _tokenType: string;
- 
-  private readonly _expiresAt?: Date;
- 
-  private readonly _refreshToken?: string;
- 
-  private readonly _scopes?: string[];
- 
-  constructor(accessToken: string, options?: TokenOptions) {
-    this._accessToken = accessToken;
-    this._tokenType = options?.tokenType ?? 'Bearer';
-    this._expiresAt = options?.expiresAt;
-    this._refreshToken = options?.refreshToken;
-    this._scopes = options?.scopes;
-  }
- 
-  /**
-   * The access token string.
-   */
-  get accessToken(): string {
-    return this._accessToken;
-  }
- 
-  /**
-   * The token type (e.g., "Bearer").
-   */
-  get tokenType(): string {
-    return this._tokenType;
-  }
- 
-  /**
-   * The expiration time of the token, if known.
-   */
-  get expiresAt(): Date | undefined {
-    return this._expiresAt;
-  }
- 
-  /**
-   * The refresh token, if available.
-   */
-  get refreshToken(): string | undefined {
-    return this._refreshToken;
-  }
- 
-  /**
-   * The scopes associated with this token.
-   */
-  get scopes(): string[] | undefined {
-    return this._scopes;
-  }
- 
-  /**
-   * Checks if the token has expired, including a safety buffer.
-   * Returns false if expiration time is unknown.
-   */
-  isExpired(): boolean {
-    if (!this._expiresAt) {
-      return false;
-    }
-    const now = new Date();
-    const bufferMs = EXPIRATION_BUFFER_SECONDS * 1000;
-    return this._expiresAt.getTime() - bufferMs <= now.getTime();
-  }
- 
-  /**
-   * Sets the Authorization header on the provided headers object.
-   * @param headers - The headers object to modify
-   * @returns The modified headers object with Authorization set
-   */
-  setAuthHeader(headers: HeadersInit): HeadersInit {
-    return {
-      ...headers,
-      Authorization: `${this._tokenType} ${this._accessToken}`,
-    };
-  }
- 
-  /**
-   * Creates a Token from a JWT string, extracting the expiration time from the payload.
-   * If the JWT cannot be decoded, the token is created without expiration info.
-   * The server will validate the token anyway, so decoding failures are handled gracefully.
-   * @param jwt - The JWT token string
-   * @param options - Additional token options (tokenType, refreshToken, scopes).
-   *                  Note: expiresAt is not accepted here as it is extracted from the JWT payload.
-   * @returns A new Token instance with expiration extracted from the JWT (if available)
-   */
-  static fromJWT(jwt: string, options?: TokenFromJWTOptions): Token {
-    let expiresAt: Date | undefined;
- 
-    try {
-      const parts = jwt.split('.');
-      if (parts.length >= 2) {
-        const payload = Buffer.from(parts[1], 'base64').toString('utf8');
-        const decoded = JSON.parse(payload);
-        if (typeof decoded.exp === 'number') {
-          expiresAt = new Date(decoded.exp * 1000);
-        }
-      }
-    } catch {
-      // If we can't decode the JWT, we'll proceed without expiration info
-      // The server will validate the token anyway
-    }
- 
-    return new Token(jwt, {
-      tokenType: options?.tokenType,
-      expiresAt,
-      refreshToken: options?.refreshToken,
-      scopes: options?.scopes,
-    });
-  }
- 
-  /**
-   * Converts the token to a plain object for serialization.
-   */
-  toJSON(): Record<string, unknown> {
-    return {
-      accessToken: this._accessToken,
-      tokenType: this._tokenType,
-      expiresAt: this._expiresAt?.toISOString(),
-      refreshToken: this._refreshToken,
-      scopes: this._scopes,
-    };
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html deleted file mode 100644 index 66302640..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider TokenProviderAuthenticator.ts

-
- -
- 11.76% - Statements - 2/17 -
- - -
- 0% - Branches - 0/8 -
- - -
- 0% - Functions - 0/2 -
- - -
- 11.76% - Lines - 2/17 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56  -  -  -  -1x -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { HeadersInit } from 'node-fetch';
-import IAuthentication from '../../contracts/IAuthentication';
-import ITokenProvider from './ITokenProvider';
-import IClientContext from '../../../contracts/IClientContext';
-import { LogLevel } from '../../../contracts/IDBSQLLogger';
- 
-/**
- * Adapts an ITokenProvider to the IAuthentication interface used by the driver.
- * This allows token providers to be used with the existing authentication system.
- */
-export default class TokenProviderAuthenticator implements IAuthentication {
-  private readonly tokenProvider: ITokenProvider;
- 
-  private readonly context: IClientContext;
- 
-  private readonly headers: HeadersInit;
- 
-  /**
-   * Creates a new TokenProviderAuthenticator.
-   * @param tokenProvider - The token provider to use for authentication
-   * @param context - The client context for logging
-   * @param headers - Additional headers to include with each request
-   */
-  constructor(tokenProvider: ITokenProvider, context: IClientContext, headers?: HeadersInit) {
-    this.tokenProvider = tokenProvider;
-    this.context = context;
-    this.headers = headers ?? {};
-  }
- 
-  async authenticate(): Promise<HeadersInit> {
-    const logger = this.context.getLogger();
-    const providerName = this.tokenProvider.getName();
- 
-    logger.log(LogLevel.debug, `TokenProviderAuthenticator: getting token from ${providerName}`);
- 
-    let token = await this.tokenProvider.getToken();
- 
-    if (token.isExpired()) {
-      logger.log(
-        LogLevel.warn,
-        `TokenProviderAuthenticator: token from ${providerName} is expired, requesting a new token`,
-      );
- 
-      token = await this.tokenProvider.getToken();
- 
-      if (token.isExpired()) {
-        const message = `TokenProviderAuthenticator: token from ${providerName} is still expired after refresh`;
-        logger.log(LogLevel.error, message);
-        throw new Error(message);
-      }
-    }
- 
-    return token.setAuthHeader(this.headers);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/index.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/index.html deleted file mode 100644 index fe806a33..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/index.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider - - - - - - - - - -
-
-

All files lib/connection/auth/tokenProvider

-
- -
- 19.9% - Statements - 41/206 -
- - -
- 0% - Branches - 0/144 -
- - -
- 0% - Functions - 0/50 -
- - -
- 16.32% - Lines - 32/196 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
CachedTokenProvider.ts -
-
7.69%2/260%0/160%0/67.69%2/26
ExternalTokenProvider.ts -
-
20%2/100%0/180%0/320%2/10
FederationProvider.ts -
-
16%12/750%0/460%0/1216.21%12/74
StaticTokenProvider.ts -
-
28.57%2/7100%0/00%0/428.57%2/7
Token.ts -
-
7.4%2/270%0/420%0/107.4%2/27
TokenProviderAuthenticator.ts -
-
11.76%2/170%0/80%0/211.76%2/17
index.ts -
-
100%16/16100%0/00%0/9100%7/7
utils.ts -
-
10.71%3/280%0/140%0/410.71%3/28
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html deleted file mode 100644 index cb84ba38..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/index.ts.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/index.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider index.ts

-
- -
- 100% - Statements - 16/16 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/9 -
- - -
- 100% - Lines - 7/7 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9  -1x -1x -1x -1x -1x -1x -1x - 
export { default as ITokenProvider } from './ITokenProvider';
-export { default as Token } from './Token';
-export { default as StaticTokenProvider } from './StaticTokenProvider';
-export { default as ExternalTokenProvider, TokenCallback } from './ExternalTokenProvider';
-export { default as TokenProviderAuthenticator } from './TokenProviderAuthenticator';
-export { default as CachedTokenProvider } from './CachedTokenProvider';
-export { default as FederationProvider } from './FederationProvider';
-export { decodeJWT, getJWTIssuer, isSameHost } from './utils';
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html b/coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html deleted file mode 100644 index 257c87bc..00000000 --- a/coverage/lcov-report/lib/connection/auth/tokenProvider/utils.ts.html +++ /dev/null @@ -1,322 +0,0 @@ - - - - - - Code coverage report for lib/connection/auth/tokenProvider/utils.ts - - - - - - - - - -
-
-

All files / lib/connection/auth/tokenProvider utils.ts

-
- -
- 10.71% - Statements - 3/28 -
- - -
- 0% - Branches - 0/14 -
- - -
- 0% - Functions - 0/4 -
- - -
- 10.71% - Lines - 3/28 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  - 
/**
- * Decodes a JWT token without verifying the signature.
- * This is safe because the server will validate the token anyway.
- *
- * @param token - The JWT token string
- * @returns The decoded payload as a record, or null if decoding fails
- */
-export function decodeJWT(token: string): Record<string, unknown> | null {
-  try {
-    const parts = token.split('.');
-    if (parts.length < 2) {
-      return null;
-    }
-    const payload = Buffer.from(parts[1], 'base64').toString('utf8');
-    return JSON.parse(payload);
-  } catch {
-    return null;
-  }
-}
- 
-/**
- * Extracts the issuer from a JWT token.
- *
- * @param token - The JWT token string
- * @returns The issuer string, or null if not found
- */
-export function getJWTIssuer(token: string): string | null {
-  const payload = decodeJWT(token);
-  if (!payload || typeof payload.iss !== 'string') {
-    return null;
-  }
-  return payload.iss;
-}
- 
-/**
- * Extracts the hostname from a URL or hostname string.
- * Handles both full URLs and bare hostnames.
- *
- * @param urlOrHostname - A URL or hostname string
- * @returns The extracted hostname
- */
-function extractHostname(urlOrHostname: string): string {
-  // If it looks like a URL, parse it
-  if (urlOrHostname.includes('://')) {
-    const url = new URL(urlOrHostname);
-    return url.hostname;
-  }
- 
-  // Handle hostname with port (e.g., "databricks.com:443")
-  const colonIndex = urlOrHostname.indexOf(':');
-  if (colonIndex !== -1) {
-    return urlOrHostname.substring(0, colonIndex);
-  }
- 
-  // Bare hostname
-  return urlOrHostname;
-}
- 
-/**
- * Compares two host URLs, ignoring ports.
- * Treats "databricks.com" and "databricks.com:443" as equivalent.
- *
- * @param url1 - First URL or hostname
- * @param url2 - Second URL or hostname
- * @returns true if the hosts are the same
- */
-export function isSameHost(url1: string, url2: string): boolean {
-  try {
-    const host1 = extractHostname(url1);
-    const host2 = extractHostname(url2);
-    // Empty hostnames are not valid
-    if (!host1 || !host2) {
-      return false;
-    }
-    return host1.toLowerCase() === host2.toLowerCase();
-  } catch {
-    return false;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html b/coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html deleted file mode 100644 index beccabda..00000000 --- a/coverage/lcov-report/lib/connection/connections/HttpConnection.ts.html +++ /dev/null @@ -1,460 +0,0 @@ - - - - - - Code coverage report for lib/connection/connections/HttpConnection.ts - - - - - - - - - -
-
-

All files / lib/connection/connections HttpConnection.ts

-
- -
- 20% - Statements - 7/35 -
- - -
- 0% - Branches - 0/40 -
- - -
- 0% - Functions - 0/11 -
- - -
- 20% - Lines - 7/35 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -1261x -1x -1x -  -1x -  -  -  -  -  -1x -  -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import thrift from 'thrift';
-import https from 'https';
-import http from 'http';
-import { HeadersInit } from 'node-fetch';
-import { ProxyAgent } from 'proxy-agent';
- 
-import IConnectionProvider, { HttpTransactionDetails } from '../contracts/IConnectionProvider';
-import IConnectionOptions, { ProxyOptions } from '../contracts/IConnectionOptions';
-import IClientContext from '../../contracts/IClientContext';
- 
-import ThriftHttpConnection from './ThriftHttpConnection';
-import IRetryPolicy from '../contracts/IRetryPolicy';
-import HttpRetryPolicy from './HttpRetryPolicy';
- 
-export default class HttpConnection implements IConnectionProvider {
-  private readonly options: IConnectionOptions;
- 
-  private readonly context: IClientContext;
- 
-  private headers: HeadersInit = {};
- 
-  private connection?: ThriftHttpConnection;
- 
-  private agent?: http.Agent;
- 
-  constructor(options: IConnectionOptions, context: IClientContext) {
-    this.options = options;
-    this.context = context;
-  }
- 
-  public setHeaders(headers: HeadersInit) {
-    this.headers = headers;
-    this.connection?.setHeaders({
-      ...this.options.headers,
-      ...this.headers,
-    });
-  }
- 
-  public async getAgent(): Promise<http.Agent | undefined> {
-    if (!this.agent) {
-      if (this.options.proxy !== undefined) {
-        this.agent = this.createProxyAgent(this.options.proxy);
-      } else {
-        this.agent = this.options.https ? this.createHttpsAgent() : this.createHttpAgent();
-      }
-    }
- 
-    return this.agent;
-  }
- 
-  private getAgentDefaultOptions(): http.AgentOptions {
-    const clientConfig = this.context.getConfig();
- 
-    return {
-      keepAlive: true,
-      keepAliveMsecs: 10000,
-      maxSockets: Infinity, // no limit
-      timeout: this.options.socketTimeout ?? clientConfig.socketTimeout,
-    };
-  }
- 
-  private createHttpAgent(): http.Agent {
-    const httpAgentOptions = this.getAgentDefaultOptions();
-    return new http.Agent(httpAgentOptions);
-  }
- 
-  private createHttpsAgent(): https.Agent {
-    const httpsAgentOptions: https.AgentOptions = {
-      ...this.getAgentDefaultOptions(),
-      minVersion: 'TLSv1.2',
-      rejectUnauthorized: false,
-      ca: this.options.ca,
-      cert: this.options.cert,
-      key: this.options.key,
-    };
-    return new https.Agent(httpsAgentOptions);
-  }
- 
-  private createProxyAgent(proxyOptions: ProxyOptions): ProxyAgent {
-    const proxyAuth = proxyOptions.auth?.username
-      ? `${proxyOptions.auth.username}:${proxyOptions.auth?.password ?? ''}@`
-      : '';
-    const proxyUrl = `${proxyOptions.protocol}://${proxyAuth}${proxyOptions.host}:${proxyOptions.port}`;
- 
-    return new ProxyAgent({
-      ...this.getAgentDefaultOptions(),
-      getProxyForUrl: () => proxyUrl,
-      httpsAgent: this.createHttpsAgent(),
-      httpAgent: this.createHttpAgent(),
-    });
-  }
- 
-  public async getThriftConnection(): Promise<any> {
-    if (!this.connection) {
-      const { options } = this;
-      const clientConfig = this.context.getConfig();
-      const agent = await this.getAgent();
- 
-      this.connection = new ThriftHttpConnection(
-        {
-          url: `${options.https ? 'https' : 'http'}://${options.host.replace(/\/$/, '')}:${options.port}${
-            options.path ? (options.path.startsWith('/') ? '' : '/') + options.path.replace(/\/$/, '') : '/'
-          }`,
-          transport: thrift.TBufferedTransport,
-          protocol: thrift.TBinaryProtocol,
-          getRetryPolicy: () => this.getRetryPolicy(),
-        },
-        {
-          agent,
-          timeout: options.socketTimeout ?? clientConfig.socketTimeout,
-          headers: {
-            ...options.headers,
-            ...this.headers,
-          },
-        },
-      );
-    }
- 
-    return this.connection;
-  }
- 
-  public async getRetryPolicy(): Promise<IRetryPolicy<HttpTransactionDetails>> {
-    return new HttpRetryPolicy(this.context);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html b/coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html deleted file mode 100644 index 6fd725ac..00000000 --- a/coverage/lcov-report/lib/connection/connections/HttpRetryPolicy.ts.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - - - Code coverage report for lib/connection/connections/HttpRetryPolicy.ts - - - - - - - - - -
-
-

All files / lib/connection/connections HttpRetryPolicy.ts

-
- -
- 5.26% - Statements - 2/38 -
- - -
- 0% - Branches - 0/24 -
- - -
- 0% - Functions - 0/9 -
- - -
- 5.4% - Lines - 2/37 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103  -  -  -1x -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import IRetryPolicy, { ShouldRetryResult, RetryableOperation } from '../contracts/IRetryPolicy';
-import { HttpTransactionDetails } from '../contracts/IConnectionProvider';
-import IClientContext from '../../contracts/IClientContext';
-import RetryError, { RetryErrorCode } from '../../errors/RetryError';
- 
-function delay(milliseconds: number): Promise<void> {
-  return new Promise<void>((resolve) => {
-    setTimeout(() => resolve(), milliseconds);
-  });
-}
- 
-export default class HttpRetryPolicy implements IRetryPolicy<HttpTransactionDetails> {
-  private context: IClientContext;
- 
-  private startTime: number; // in milliseconds
- 
-  private attempt: number;
- 
-  constructor(context: IClientContext) {
-    this.context = context;
-    this.startTime = Date.now();
-    this.attempt = 0;
-  }
- 
-  public async shouldRetry(details: HttpTransactionDetails): Promise<ShouldRetryResult> {
-    if (this.isRetryable(details)) {
-      const clientConfig = this.context.getConfig();
- 
-      // Don't retry if overall retry timeout exceeded
-      const timeoutExceeded = Date.now() - this.startTime >= clientConfig.retriesTimeout;
-      if (timeoutExceeded) {
-        throw new RetryError(RetryErrorCode.TimeoutExceeded, details);
-      }
- 
-      this.attempt += 1;
- 
-      // Don't retry if max attempts count reached
-      const attemptsExceeded = this.attempt >= clientConfig.retryMaxAttempts;
-      if (attemptsExceeded) {
-        throw new RetryError(RetryErrorCode.AttemptsExceeded, details);
-      }
- 
-      // If possible, use `Retry-After` header as a floor for a backoff algorithm
-      const retryAfterHeader = this.getRetryAfterHeader(details, clientConfig.retryDelayMin);
-      const retryAfter = this.getBackoffDelay(
-        this.attempt,
-        retryAfterHeader ?? clientConfig.retryDelayMin,
-        clientConfig.retryDelayMax,
-      );
- 
-      return { shouldRetry: true, retryAfter };
-    }
- 
-    return { shouldRetry: false };
-  }
- 
-  public async invokeWithRetry(operation: RetryableOperation<HttpTransactionDetails>): Promise<HttpTransactionDetails> {
-    for (;;) {
-      const details = await operation(); // eslint-disable-line no-await-in-loop
-      const status = await this.shouldRetry(details); // eslint-disable-line no-await-in-loop
-      if (!status.shouldRetry) {
-        return details;
-      }
-      await delay(status.retryAfter); // eslint-disable-line no-await-in-loop
-    }
-  }
- 
-  protected isRetryable({ response }: HttpTransactionDetails): boolean {
-    const statusCode = response.status;
- 
-    const result =
-      // Retry on all codes below 100
-      statusCode < 100 ||
-      // ...and on `429 Too Many Requests`
-      statusCode === 429 ||
-      // ...and on all `5xx` codes except for `501 Not Implemented`
-      (statusCode >= 500 && statusCode !== 501);
- 
-    return result;
-  }
- 
-  protected getRetryAfterHeader({ response }: HttpTransactionDetails, delayMin: number): number | undefined {
-    // `Retry-After` header may contain a date after which to retry, or delay seconds. We support only delay seconds.
-    // Value from `Retry-After` header is used when:
-    // 1. it's available and is non-empty
-    // 2. it could be parsed as a number, and is greater than zero
-    // 3. additionally, we clamp it to not be smaller than minimal retry delay
-    const header = response.headers.get('Retry-After') || '';
-    if (header !== '') {
-      const value = Number(header);
-      if (Number.isFinite(value) && value > 0) {
-        return Math.max(delayMin, value);
-      }
-    }
-    return undefined;
-  }
- 
-  protected getBackoffDelay(attempt: number, delayMin: number, delayMax: number): number {
-    const value = 2 ** attempt * delayMin;
-    return Math.min(value, delayMax);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html b/coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html deleted file mode 100644 index 9cda4b4c..00000000 --- a/coverage/lcov-report/lib/connection/connections/NullRetryPolicy.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/connection/connections/NullRetryPolicy.ts - - - - - - - - - -
-
-

All files / lib/connection/connections NullRetryPolicy.ts

-
- -
- 33.33% - Statements - 1/3 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/2 -
- - -
- 33.33% - Lines - 1/3 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14  -  -1x -  -  -  -  -  -  -  -  -  -  - 
import IRetryPolicy, { ShouldRetryResult, RetryableOperation } from '../contracts/IRetryPolicy';
- 
-export default class NullRetryPolicy<R> implements IRetryPolicy<R> {
-  // eslint-disable-next-line @typescript-eslint/no-unused-vars
-  public async shouldRetry(details: R): Promise<ShouldRetryResult> {
-    return { shouldRetry: false };
-  }
- 
-  public async invokeWithRetry(operation: RetryableOperation<R>): Promise<R> {
-    // Just invoke the operation, don't attempt to retry it
-    return operation();
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html b/coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html deleted file mode 100644 index 97cf8bb3..00000000 --- a/coverage/lcov-report/lib/connection/connections/ThriftHttpConnection.ts.html +++ /dev/null @@ -1,766 +0,0 @@ - - - - - - Code coverage report for lib/connection/connections/ThriftHttpConnection.ts - - - - - - - - - -
-
-

All files / lib/connection/connections ThriftHttpConnection.ts

-
- -
- 10.52% - Statements - 8/76 -
- - -
- 0% - Branches - 0/33 -
- - -
- 0% - Functions - 0/20 -
- - -
- 10.81% - Lines - 8/74 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228  -  -  -  -  -  -1x -1x -1x -  -1x -  -  -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
/**
-  This file is created using node_modules/thrift/lib/nodejs/lib/thrift/http_connection.js as an example
- 
-  The code relies on thrift internals, so be careful when upgrading `thrift` library
-*/
- 
-import { EventEmitter } from 'events';
-import { TBinaryProtocol, TBufferedTransport, Thrift, TProtocol, TProtocolConstructor, TTransport } from 'thrift';
-import fetch, { RequestInit, HeadersInit, Request, Response, FetchError } from 'node-fetch';
-// @ts-expect-error TS7016: Could not find a declaration file for module
-import InputBufferUnderrunError from 'thrift/lib/nodejs/lib/thrift/input_buffer_underrun_error';
-import IRetryPolicy from '../contracts/IRetryPolicy';
-import { HttpTransactionDetails } from '../contracts/IConnectionProvider';
-import NullRetryPolicy from './NullRetryPolicy';
- 
-export class THTTPException extends Thrift.TApplicationException {
-  public readonly statusCode: unknown;
- 
-  public readonly response: Response;
- 
-  constructor(response: Response) {
-    super(
-      Thrift.TApplicationExceptionType.PROTOCOL_ERROR,
-      `Received a response with a bad HTTP status code: ${response.status}`,
-    );
-    this.statusCode = response.status;
-    this.response = response;
-  }
-}
- 
-type TTransportType = typeof TBufferedTransport;
- 
-interface ThriftHttpConnectionOptions {
-  url: string;
-  transport?: TTransportType;
-  protocol?: TProtocolConstructor;
-  getRetryPolicy(): Promise<IRetryPolicy<HttpTransactionDetails>>;
-}
- 
-// This type describes a shape of internals of Thrift client object.
-// It is not perfect good enough for our needs
-type ThriftClient = {
-  // Internal map of callbacks of running requests. Once request is completed (either successfully or not) -
-  // callback should be removed from it
-  _reqs: Record<number, (error: unknown, response?: unknown) => void>;
-} & {
-  // For each client's public method Foo there are two private ones: send_Foo and recv_Foo.
-  // We have to access recv_Foo ones to properly parse the response
-  [key: string]: (input: TProtocol, mtype: Thrift.MessageType, seqId: number) => void;
-};
- 
-const retryableThriftMethods = new Set([
-  'GetOperationStatus',
-  'CancelOperation',
-  'CloseOperation',
-  'GetResultSetMetadata',
-  'CloseSession',
-  'GetInfo',
-  'GetTypeInfo',
-  'GetCatalogs',
-  'GetSchemas',
-  'GetTables',
-  'GetTableTypes',
-  'GetColumns',
-  'GetFunctions',
-  'GetPrimaryKeys',
-  'GetCrossReference',
-]);
- 
-export default class ThriftHttpConnection extends EventEmitter {
-  private readonly url: string;
- 
-  private config: RequestInit;
- 
-  private options: ThriftHttpConnectionOptions;
- 
-  // This field is used by Thrift internally, so name and type are important
-  private readonly transport: TTransportType;
- 
-  // This field is used by Thrift internally, so name and type are important
-  private readonly protocol: TProtocolConstructor;
- 
-  // thrift.createClient sets this field internally
-  public client?: ThriftClient;
- 
-  constructor(options: ThriftHttpConnectionOptions, config: RequestInit = {}) {
-    super();
-    this.url = options.url;
-    this.config = config;
-    this.options = options;
-    this.transport = options.transport ?? TBufferedTransport;
-    this.protocol = options.protocol ?? TBinaryProtocol;
-  }
- 
-  protected async getRetryPolicy(thriftMethodName?: string): Promise<IRetryPolicy<HttpTransactionDetails>> {
-    // Allow retry behavior only for Thrift operations that are for sure safe to retry
-    if (thriftMethodName && retryableThriftMethods.has(thriftMethodName)) {
-      return this.options.getRetryPolicy();
-    }
-    // Don't retry everything that is not explicitly allowed to retry
-    return new NullRetryPolicy();
-  }
- 
-  public setHeaders(headers: HeadersInit) {
-    this.config = {
-      ...this.config,
-      headers,
-    };
-  }
- 
-  public write(data: Buffer, seqId: number) {
-    const requestConfig: RequestInit = {
-      ...this.config,
-      method: 'POST',
-      headers: {
-        ...this.config.headers,
-        Connection: 'keep-alive',
-        'Content-Length': `${data.length}`,
-        'Content-Type': 'application/x-thrift',
-      },
-      body: data,
-    };
- 
-    this.getThriftMethodName(data)
-      .then((thriftMethod) => this.getRetryPolicy(thriftMethod))
-      .then((retryPolicy) => {
-        const makeRequest = () => {
-          const request = new Request(this.url, requestConfig);
-          return fetch(request).then((response) => ({ request, response }));
-        };
-        return retryPolicy.invokeWithRetry(makeRequest);
-      })
-      .then(({ response }) => {
-        if (response.status !== 200) {
-          throw new THTTPException(response);
-        }
- 
-        return response.buffer();
-      })
-      .then((buffer) => {
-        this.transport.receiver((transportWithData) => this.handleThriftResponse(transportWithData), seqId)(buffer);
-      })
-      .catch((error) => {
-        if (error instanceof FetchError) {
-          if (error.type === 'request-timeout') {
-            error = new Thrift.TApplicationException(
-              Thrift.TApplicationExceptionType.PROTOCOL_ERROR,
-              'Request timed out',
-            );
-          }
-        }
- 
-        const defaultErrorHandler = (err: unknown) => {
-          this.emit('error', err);
-        };
- 
-        if (this.client) {
-          const callback = this.client._reqs[seqId] ?? defaultErrorHandler;
-          delete this.client._reqs[seqId];
-          callback(error);
-        } else {
-          defaultErrorHandler(error);
-        }
-      });
-  }
- 
-  private getThriftMethodName(thriftMessage: Buffer): Promise<string | undefined> {
-    return new Promise((resolve) => {
-      try {
-        const receiver = this.transport.receiver((transportWithData) => {
-          const Protocol = this.protocol;
-          const proto = new Protocol(transportWithData);
-          const header = proto.readMessageBegin();
-          resolve(header.fname);
-        }, 0 /* `seqId` could be any because it's ignored */);
- 
-        receiver(thriftMessage);
-      } catch {
-        resolve(undefined);
-      }
-    });
-  }
- 
-  private handleThriftResponse(transportWithData: TTransport) {
-    if (!this.client) {
-      throw new Thrift.TApplicationException(Thrift.TApplicationExceptionType.INTERNAL_ERROR, 'Client not available');
-    }
- 
-    const Protocol = this.protocol;
-    const proto = new Protocol(transportWithData);
-    try {
-      // eslint-disable-next-line no-constant-condition
-      while (true) {
-        const header = proto.readMessageBegin();
-        const dummySeqId = header.rseqid * -1;
-        const { client } = this;
- 
-        client._reqs[dummySeqId] = (err, success) => {
-          transportWithData.commitPosition();
-          const clientCallback = client._reqs[header.rseqid];
-          delete client._reqs[header.rseqid];
-          if (clientCallback) {
-            process.nextTick(() => {
-              clientCallback(err, success);
-            });
-          }
-        };
- 
-        if (client[`recv_${header.fname}`]) {
-          client[`recv_${header.fname}`](proto, header.mtype, dummySeqId);
-        } else {
-          delete client._reqs[dummySeqId];
-          throw new Thrift.TApplicationException(
-            Thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
-            'Received a response to an unknown RPC function',
-          );
-        }
-      }
-    } catch (error) {
-      if (error instanceof InputBufferUnderrunError) {
-        transportWithData.rollbackPosition();
-      } else {
-        throw error;
-      }
-    }
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/connection/connections/index.html b/coverage/lcov-report/lib/connection/connections/index.html deleted file mode 100644 index dcf0ae81..00000000 --- a/coverage/lcov-report/lib/connection/connections/index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - Code coverage report for lib/connection/connections - - - - - - - - - -
-
-

All files lib/connection/connections

-
- -
- 11.84% - Statements - 18/152 -
- - -
- 0% - Branches - 0/97 -
- - -
- 0% - Functions - 0/42 -
- - -
- 12.08% - Lines - 18/149 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
HttpConnection.ts -
-
20%7/350%0/400%0/1120%7/35
HttpRetryPolicy.ts -
-
5.26%2/380%0/240%0/95.4%2/37
NullRetryPolicy.ts -
-
33.33%1/3100%0/00%0/233.33%1/3
ThriftHttpConnection.ts -
-
10.52%8/760%0/330%0/2010.81%8/74
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html b/coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html deleted file mode 100644 index 2c666fce..00000000 --- a/coverage/lcov-report/lib/contracts/IDBSQLLogger.ts.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - Code coverage report for lib/contracts/IDBSQLLogger.ts - - - - - - - - - -
-
-

All files / lib/contracts IDBSQLLogger.ts

-
- -
- 100% - Statements - 5/5 -
- - -
- 100% - Branches - 2/2 -
- - -
- 100% - Functions - 1/1 -
- - -
- 100% - Lines - 5/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16  -  -  -  -  -  -  -  -  -1x -1x -1x -1x -1x -  - 
export interface LoggerOptions {
-  filepath?: string;
-  level?: LogLevel;
-}
- 
-export default interface IDBSQLLogger {
-  log(level: LogLevel, message: string): void;
-}
- 
-export enum LogLevel {
-  error = 'error',
-  warn = 'warn',
-  info = 'info',
-  debug = 'debug',
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/contracts/index.html b/coverage/lcov-report/lib/contracts/index.html deleted file mode 100644 index 92f0ae48..00000000 --- a/coverage/lcov-report/lib/contracts/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for lib/contracts - - - - - - - - - -
-
-

All files lib/contracts

-
- -
- 100% - Statements - 5/5 -
- - -
- 100% - Branches - 2/2 -
- - -
- 100% - Functions - 1/1 -
- - -
- 100% - Lines - 5/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
IDBSQLLogger.ts -
-
100%5/5100%2/2100%1/1100%5/5
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/dto/InfoValue.ts.html b/coverage/lcov-report/lib/dto/InfoValue.ts.html deleted file mode 100644 index 8524617c..00000000 --- a/coverage/lcov-report/lib/dto/InfoValue.ts.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - Code coverage report for lib/dto/InfoValue.ts - - - - - - - - - -
-
-

All files / lib/dto InfoValue.ts

-
- -
- 7.14% - Statements - 1/14 -
- - -
- 0% - Branches - 0/10 -
- - -
- 0% - Functions - 0/2 -
- - -
- 7.14% - Lines - 1/14 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import Int64 from 'node-int64';
-import { TGetInfoValue } from '../../thrift/TCLIService_types';
- 
-type InfoResultType = string | number | Buffer | Int64 | null;
- 
-export default class InfoValue {
-  private value: TGetInfoValue;
- 
-  constructor(value: TGetInfoValue) {
-    this.value = value;
-  }
- 
-  getValue(): InfoResultType {
-    const infoValue = this.value;
- 
-    if (infoValue.stringValue) {
-      return infoValue.stringValue;
-    }
-    if (infoValue.smallIntValue) {
-      return infoValue.smallIntValue;
-    }
-    if (infoValue.integerBitmask) {
-      return infoValue.integerBitmask;
-    }
-    if (infoValue.integerFlag) {
-      return infoValue.integerFlag;
-    }
-    if (infoValue.lenValue) {
-      return infoValue.lenValue;
-    }
-    return null;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/dto/Status.ts.html b/coverage/lcov-report/lib/dto/Status.ts.html deleted file mode 100644 index 16f386c6..00000000 --- a/coverage/lcov-report/lib/dto/Status.ts.html +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - Code coverage report for lib/dto/Status.ts - - - - - - - - - -
-
-

All files / lib/dto Status.ts

-
- -
- 20% - Statements - 3/15 -
- - -
- 0% - Branches - 0/11 -
- - -
- 0% - Functions - 0/7 -
- - -
- 20% - Lines - 3/15 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -441x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { TStatus, TStatusCode } from '../../thrift/TCLIService_types';
-import StatusError from '../errors/StatusError';
- 
-export default class Status {
-  private readonly status: TStatus;
- 
-  constructor(status: TStatus) {
-    this.status = status;
-  }
- 
-  public get isSuccess(): boolean {
-    const { statusCode } = this.status;
-    return statusCode === TStatusCode.SUCCESS_STATUS || statusCode === TStatusCode.SUCCESS_WITH_INFO_STATUS;
-  }
- 
-  public get isExecuting(): boolean {
-    const { statusCode } = this.status;
-    return statusCode === TStatusCode.STILL_EXECUTING_STATUS;
-  }
- 
-  public get isError(): boolean {
-    const { statusCode } = this.status;
-    return statusCode === TStatusCode.ERROR_STATUS || statusCode === TStatusCode.INVALID_HANDLE_STATUS;
-  }
- 
-  public get info(): Array<string> {
-    return this.status.infoMessages || [];
-  }
- 
-  public static assert(status: TStatus) {
-    const statusWrapper = new Status(status);
-    if (statusWrapper.isError) {
-      throw new StatusError(status);
-    }
-  }
- 
-  public static success(info: Array<string> = []): Status {
-    return new Status({
-      statusCode: info.length > 0 ? TStatusCode.SUCCESS_WITH_INFO_STATUS : TStatusCode.SUCCESS_STATUS,
-      infoMessages: info,
-    });
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/dto/index.html b/coverage/lcov-report/lib/dto/index.html deleted file mode 100644 index 9b74e63b..00000000 --- a/coverage/lcov-report/lib/dto/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - Code coverage report for lib/dto - - - - - - - - - -
-
-

All files lib/dto

-
- -
- 13.79% - Statements - 4/29 -
- - -
- 0% - Branches - 0/21 -
- - -
- 0% - Functions - 0/9 -
- - -
- 13.79% - Lines - 4/29 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
InfoValue.ts -
-
7.14%1/140%0/100%0/27.14%1/14
Status.ts -
-
20%3/150%0/110%0/720%3/15
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/AuthenticationError.ts.html b/coverage/lcov-report/lib/errors/AuthenticationError.ts.html deleted file mode 100644 index a9ca0a2b..00000000 --- a/coverage/lcov-report/lib/errors/AuthenticationError.ts.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - Code coverage report for lib/errors/AuthenticationError.ts - - - - - - - - - -
-
-

All files / lib/errors AuthenticationError.ts

-
- -
- 100% - Statements - 2/2 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 2/2 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -41x -  -1x - 
import HiveDriverError from './HiveDriverError';
- 
-export default class AuthenticationError extends HiveDriverError {}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/HiveDriverError.ts.html b/coverage/lcov-report/lib/errors/HiveDriverError.ts.html deleted file mode 100644 index eae0a9a0..00000000 --- a/coverage/lcov-report/lib/errors/HiveDriverError.ts.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - Code coverage report for lib/errors/HiveDriverError.ts - - - - - - - - - -
-
-

All files / lib/errors HiveDriverError.ts

-
- -
- 100% - Statements - 1/1 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 1/1 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -21x - 
export default class HiveDriverError extends Error {}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/OperationStateError.ts.html b/coverage/lcov-report/lib/errors/OperationStateError.ts.html deleted file mode 100644 index ad5996a4..00000000 --- a/coverage/lcov-report/lib/errors/OperationStateError.ts.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - Code coverage report for lib/errors/OperationStateError.ts - - - - - - - - - -
-
-

All files / lib/errors OperationStateError.ts

-
- -
- 75% - Statements - 9/12 -
- - -
- 20% - Branches - 2/10 -
- - -
- 50% - Functions - 1/2 -
- - -
- 75% - Lines - 9/12 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -321x -  -  -1x -1x -1x -1x -1x -1x -  -  -1x -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  - 
import HiveDriverError from './HiveDriverError';
-import { TGetOperationStatusResp } from '../../thrift/TCLIService_types';
- 
-export enum OperationStateErrorCode {
-  Canceled = 'CANCELED',
-  Closed = 'CLOSED',
-  Error = 'ERROR',
-  Timeout = 'TIMEOUT',
-  Unknown = 'UNKNOWN',
-}
- 
-const errorMessages: Record<OperationStateErrorCode, string> = {
-  [OperationStateErrorCode.Canceled]: 'The operation was canceled by a client',
-  [OperationStateErrorCode.Closed]: 'The operation was closed by a client',
-  [OperationStateErrorCode.Error]: 'The operation failed due to an error',
-  [OperationStateErrorCode.Timeout]: 'The operation is in a timed out state',
-  [OperationStateErrorCode.Unknown]: 'The operation is in an unrecognized state',
-};
- 
-export default class OperationStateError extends HiveDriverError {
-  public errorCode: OperationStateErrorCode;
- 
-  public response?: TGetOperationStatusResp;
- 
-  constructor(errorCode: OperationStateErrorCode, response?: TGetOperationStatusResp) {
-    super(response?.displayMessage ?? errorMessages[errorCode]);
- 
-    this.errorCode = errorCode;
-    this.response = response;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/ParameterError.ts.html b/coverage/lcov-report/lib/errors/ParameterError.ts.html deleted file mode 100644 index b521d421..00000000 --- a/coverage/lcov-report/lib/errors/ParameterError.ts.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - Code coverage report for lib/errors/ParameterError.ts - - - - - - - - - -
-
-

All files / lib/errors ParameterError.ts

-
- -
- 100% - Statements - 1/1 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 1/1 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -21x - 
export default class ParameterError extends Error {}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/RetryError.ts.html b/coverage/lcov-report/lib/errors/RetryError.ts.html deleted file mode 100644 index 413fab13..00000000 --- a/coverage/lcov-report/lib/errors/RetryError.ts.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - Code coverage report for lib/errors/RetryError.ts - - - - - - - - - -
-
-

All files / lib/errors RetryError.ts

-
- -
- 62.5% - Statements - 5/8 -
- - -
- 100% - Branches - 2/2 -
- - -
- 50% - Functions - 1/2 -
- - -
- 62.5% - Lines - 5/8 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -221x -1x -1x -  -  -1x -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  - 
export enum RetryErrorCode {
-  AttemptsExceeded = 'ATTEMPTS_EXCEEDED',
-  TimeoutExceeded = 'TIMEOUT_EXCEEDED',
-}
- 
-const errorMessages: Record<RetryErrorCode, string> = {
-  [RetryErrorCode.AttemptsExceeded]: 'Max retry count exceeded',
-  [RetryErrorCode.TimeoutExceeded]: 'Retry timeout exceeded',
-};
- 
-export default class RetryError extends Error {
-  public readonly errorCode: RetryErrorCode;
- 
-  public readonly payload: unknown;
- 
-  constructor(errorCode: RetryErrorCode, payload?: unknown) {
-    super(errorMessages[errorCode]);
-    this.errorCode = errorCode;
-    this.payload = payload;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/StagingError.ts.html b/coverage/lcov-report/lib/errors/StagingError.ts.html deleted file mode 100644 index c9c11c5d..00000000 --- a/coverage/lcov-report/lib/errors/StagingError.ts.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - Code coverage report for lib/errors/StagingError.ts - - - - - - - - - -
-
-

All files / lib/errors StagingError.ts

-
- -
- 100% - Statements - 1/1 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 1/1 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -21x - 
export default class StagingError extends Error {}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/StatusError.ts.html b/coverage/lcov-report/lib/errors/StatusError.ts.html deleted file mode 100644 index cf5f2dbf..00000000 --- a/coverage/lcov-report/lib/errors/StatusError.ts.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - Code coverage report for lib/errors/StatusError.ts - - - - - - - - - -
-
-

All files / lib/errors StatusError.ts

-
- -
- 16.66% - Statements - 1/6 -
- - -
- 0% - Branches - 0/6 -
- - -
- 0% - Functions - 0/1 -
- - -
- 16.66% - Lines - 1/6 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { TStatus } from '../../thrift/TCLIService_types';
- 
-export default class StatusError implements Error {
-  public name: string;
- 
-  public message: string;
- 
-  public code: number;
- 
-  public stack?: string;
- 
-  constructor(status: TStatus) {
-    this.name = 'Status Error';
-    this.message = status.errorMessage || '';
-    this.code = status.errorCode || -1;
- 
-    if (Array.isArray(status.infoMessages)) {
-      this.stack = status.infoMessages.join('\n');
-    }
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/errors/index.html b/coverage/lcov-report/lib/errors/index.html deleted file mode 100644 index 2e96fdc3..00000000 --- a/coverage/lcov-report/lib/errors/index.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - Code coverage report for lib/errors - - - - - - - - - -
-
-

All files lib/errors

-
- -
- 64.51% - Statements - 20/31 -
- - -
- 22.22% - Branches - 4/18 -
- - -
- 40% - Functions - 2/5 -
- - -
- 64.51% - Lines - 20/31 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
AuthenticationError.ts -
-
100%2/2100%0/0100%0/0100%2/2
HiveDriverError.ts -
-
100%1/1100%0/0100%0/0100%1/1
OperationStateError.ts -
-
75%9/1220%2/1050%1/275%9/12
ParameterError.ts -
-
100%1/1100%0/0100%0/0100%1/1
RetryError.ts -
-
62.5%5/8100%2/250%1/262.5%5/8
StagingError.ts -
-
100%1/1100%0/0100%0/0100%1/1
StatusError.ts -
-
16.66%1/60%0/60%0/116.66%1/6
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html deleted file mode 100644 index a037b54e..00000000 --- a/coverage/lcov-report/lib/hive/Commands/BaseCommand.ts.html +++ /dev/null @@ -1,295 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/BaseCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands BaseCommand.ts

-
- -
- 16.66% - Statements - 4/24 -
- - -
- 0% - Branches - 0/22 -
- - -
- 0% - Functions - 0/5 -
- - -
- 16.66% - Lines - 4/24 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -711x -1x -1x -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { Response } from 'node-fetch';
-import HiveDriverError from '../../errors/HiveDriverError';
-import RetryError, { RetryErrorCode } from '../../errors/RetryError';
-import IClientContext from '../../contracts/IClientContext';
- 
-export default abstract class BaseCommand<ClientType> {
-  protected client: ClientType;
- 
-  protected context: IClientContext;
- 
-  constructor(client: ClientType, context: IClientContext) {
-    this.client = client;
-    this.context = context;
-  }
- 
-  protected async executeCommand<Response>(request: object, command: Function | void): Promise<Response> {
-    try {
-      return await this.invokeCommand<Response>(request, command);
-    } catch (error) {
-      if (error instanceof RetryError) {
-        let statusCode: number | undefined;
-        if (
-          error.payload &&
-          typeof error.payload === 'object' &&
-          'response' in error.payload &&
-          error.payload.response instanceof Response
-        ) {
-          statusCode = error.payload.response.status;
-        }
- 
-        switch (error.errorCode) {
-          case RetryErrorCode.AttemptsExceeded:
-            throw new HiveDriverError(
-              `Hive driver: ${statusCode ?? 'Error'} when connecting to resource. Max retry count exceeded.`,
-            );
-          case RetryErrorCode.TimeoutExceeded:
-            throw new HiveDriverError(
-              `Hive driver: ${statusCode ?? 'Error'} when connecting to resource. Retry timeout exceeded.`,
-            );
-          // no default
-        }
-      }
- 
-      // Re-throw error we didn't handle
-      throw error;
-    }
-  }
- 
-  private invokeCommand<Response>(request: object, command: Function | void): Promise<Response> {
-    if (typeof command !== 'function') {
-      return Promise.reject(
-        new HiveDriverError('Hive driver: the operation does not exist, try to choose another Thrift file.'),
-      );
-    }
- 
-    return new Promise((resolve, reject) => {
-      try {
-        command.call(this.client, request, (err: Error, response: Response) => {
-          if (err) {
-            reject(err);
-          } else {
-            resolve(response);
-          }
-        });
-      } catch (error) {
-        reject(error);
-      }
-    });
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html deleted file mode 100644 index 9f074939..00000000 --- a/coverage/lcov-report/lib/hive/Commands/CancelDelegationTokenCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/CancelDelegationTokenCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands CancelDelegationTokenCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TCancelDelegationTokenReq, TCancelDelegationTokenResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'CancelDelegationToken'>;
- 
-export default class CancelDelegationTokenCommand extends BaseCommand<Client> {
-  execute(data: TCancelDelegationTokenReq): Promise<TCancelDelegationTokenResp> {
-    const request = new TCancelDelegationTokenReq(data);
- 
-    return this.executeCommand<TCancelDelegationTokenResp>(request, this.client.CancelDelegationToken);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html deleted file mode 100644 index 6ca660aa..00000000 --- a/coverage/lcov-report/lib/hive/Commands/CancelOperationCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/CancelOperationCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands CancelOperationCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TCancelOperationReq, TCancelOperationResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'CancelOperation'>;
- 
-export default class CancelOperationCommand extends BaseCommand<Client> {
-  execute(data: TCancelOperationReq): Promise<TCancelOperationResp> {
-    const request = new TCancelOperationReq(data);
- 
-    return this.executeCommand<TCancelOperationResp>(request, this.client.CancelOperation);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html deleted file mode 100644 index 5299771d..00000000 --- a/coverage/lcov-report/lib/hive/Commands/CloseOperationCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/CloseOperationCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands CloseOperationCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TCloseOperationReq, TCloseOperationResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'CloseOperation'>;
- 
-export default class CloseOperationCommand extends BaseCommand<Client> {
-  execute(data: TCloseOperationReq): Promise<TCloseOperationResp> {
-    const request = new TCloseOperationReq(data);
- 
-    return this.executeCommand<TCloseOperationResp>(request, this.client.CloseOperation);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html deleted file mode 100644 index d959e629..00000000 --- a/coverage/lcov-report/lib/hive/Commands/CloseSessionCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/CloseSessionCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands CloseSessionCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TCloseSessionReq, TCloseSessionResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'CloseSession'>;
- 
-export default class CloseSessionCommand extends BaseCommand<Client> {
-  execute(openSessionRequest: TCloseSessionReq): Promise<TCloseSessionResp> {
-    const request = new TCloseSessionReq(openSessionRequest);
- 
-    return this.executeCommand<TCloseSessionResp>(request, this.client.CloseSession);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html deleted file mode 100644 index 0a8bb12a..00000000 --- a/coverage/lcov-report/lib/hive/Commands/ExecuteStatementCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/ExecuteStatementCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands ExecuteStatementCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TExecuteStatementReq, TExecuteStatementResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'ExecuteStatement'>;
- 
-export default class ExecuteStatementCommand extends BaseCommand<Client> {
-  execute(executeStatementRequest: TExecuteStatementReq): Promise<TExecuteStatementResp> {
-    const request = new TExecuteStatementReq(executeStatementRequest);
- 
-    return this.executeCommand<TExecuteStatementResp>(request, this.client.ExecuteStatement);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html deleted file mode 100644 index 6c823598..00000000 --- a/coverage/lcov-report/lib/hive/Commands/FetchResultsCommand.ts.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/FetchResultsCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands FetchResultsCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -171x -1x -  -  -  -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TFetchResultsReq, TFetchResultsResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'FetchResults'>;
- 
-/**
- * TFetchResultsReq.fetchType - 0 represents Query output. 1 represents Log
- */
-export default class FetchResultsCommand extends BaseCommand<Client> {
-  execute(data: TFetchResultsReq): Promise<TFetchResultsResp> {
-    const request = new TFetchResultsReq(data);
- 
-    return this.executeCommand<TFetchResultsResp>(request, this.client.FetchResults);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html deleted file mode 100644 index b0fb30da..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetCatalogsCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetCatalogsCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetCatalogsCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetCatalogsReq, TGetCatalogsResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetCatalogs'>;
- 
-export default class GetCatalogsCommand extends BaseCommand<Client> {
-  execute(data: TGetCatalogsReq): Promise<TGetCatalogsResp> {
-    const request = new TGetCatalogsReq(data);
- 
-    return this.executeCommand<TGetCatalogsResp>(request, this.client.GetCatalogs);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html deleted file mode 100644 index 8c463f2a..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetColumnsCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetColumnsCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetColumnsCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetColumnsReq, TGetColumnsResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetColumns'>;
- 
-export default class GetColumnsCommand extends BaseCommand<Client> {
-  execute(data: TGetColumnsReq): Promise<TGetColumnsResp> {
-    const request = new TGetColumnsReq(data);
- 
-    return this.executeCommand<TGetColumnsResp>(request, this.client.GetColumns);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html deleted file mode 100644 index df48823b..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetCrossReferenceCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetCrossReferenceCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetCrossReferenceCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetCrossReferenceReq, TGetCrossReferenceResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetCrossReference'>;
- 
-export default class GetCrossReferenceCommand extends BaseCommand<Client> {
-  execute(data: TGetCrossReferenceReq): Promise<TGetCrossReferenceResp> {
-    const request = new TGetCrossReferenceReq(data);
- 
-    return this.executeCommand<TGetCrossReferenceResp>(request, this.client.GetCrossReference);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html deleted file mode 100644 index 1c3f4396..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetDelegationTokenCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetDelegationTokenCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetDelegationTokenCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetDelegationTokenReq, TGetDelegationTokenResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetDelegationToken'>;
- 
-export default class GetDelegationTokenCommand extends BaseCommand<Client> {
-  execute(data: TGetDelegationTokenReq): Promise<TGetDelegationTokenResp> {
-    const request = new TGetDelegationTokenReq(data);
- 
-    return this.executeCommand<TGetDelegationTokenResp>(request, this.client.GetDelegationToken);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html deleted file mode 100644 index 89a7a924..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetFunctionsCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetFunctionsCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetFunctionsCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetFunctionsReq, TGetFunctionsResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetFunctions'>;
- 
-export default class GetFunctionsCommand extends BaseCommand<Client> {
-  execute(data: TGetFunctionsReq): Promise<TGetFunctionsResp> {
-    const request = new TGetFunctionsReq(data);
- 
-    return this.executeCommand<TGetFunctionsResp>(request, this.client.GetFunctions);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html deleted file mode 100644 index d7bd6e61..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetInfoCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetInfoCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetInfoCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetInfoReq, TGetInfoResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetInfo'>;
- 
-export default class GetInfoCommand extends BaseCommand<Client> {
-  execute(data: TGetInfoReq): Promise<TGetInfoResp> {
-    const request = new TGetInfoReq(data);
- 
-    return this.executeCommand<TGetInfoResp>(request, this.client.GetInfo);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html deleted file mode 100644 index f64831b6..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetOperationStatusCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetOperationStatusCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetOperationStatusCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetOperationStatusReq, TGetOperationStatusResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetOperationStatus'>;
- 
-export default class GetOperationStatusCommand extends BaseCommand<Client> {
-  execute(data: TGetOperationStatusReq): Promise<TGetOperationStatusResp> {
-    const request = new TGetOperationStatusReq(data);
- 
-    return this.executeCommand<TGetOperationStatusResp>(request, this.client.GetOperationStatus);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html deleted file mode 100644 index b81a276e..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetPrimaryKeysCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetPrimaryKeysCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetPrimaryKeysCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetPrimaryKeysReq, TGetPrimaryKeysResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetPrimaryKeys'>;
- 
-export default class GetPrimaryKeysCommand extends BaseCommand<Client> {
-  execute(data: TGetPrimaryKeysReq): Promise<TGetPrimaryKeysResp> {
-    const request = new TGetPrimaryKeysReq(data);
- 
-    return this.executeCommand<TGetPrimaryKeysResp>(request, this.client.GetPrimaryKeys);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html deleted file mode 100644 index 69b89491..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetResultSetMetadataCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetResultSetMetadataCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetResultSetMetadataCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetResultSetMetadataReq, TGetResultSetMetadataResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetResultSetMetadata'>;
- 
-export default class GetResultSetMetadataCommand extends BaseCommand<Client> {
-  execute(getResultSetMetadataRequest: TGetResultSetMetadataReq): Promise<TGetResultSetMetadataResp> {
-    const request = new TGetResultSetMetadataReq(getResultSetMetadataRequest);
- 
-    return this.executeCommand<TGetResultSetMetadataResp>(request, this.client.GetResultSetMetadata);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html deleted file mode 100644 index f9caada1..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetSchemasCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetSchemasCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetSchemasCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetSchemasReq, TGetSchemasResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetSchemas'>;
- 
-export default class GetSchemasCommand extends BaseCommand<Client> {
-  execute(data: TGetSchemasReq): Promise<TGetSchemasResp> {
-    const request = new TGetSchemasReq(data);
- 
-    return this.executeCommand<TGetSchemasResp>(request, this.client.GetSchemas);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html deleted file mode 100644 index 2b61c3a0..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetTableTypesCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetTableTypesCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetTableTypesCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetTableTypesReq, TGetTableTypesResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetTableTypes'>;
- 
-export default class GetTableTypesCommand extends BaseCommand<Client> {
-  execute(data: TGetTableTypesReq): Promise<TGetTableTypesResp> {
-    const request = new TGetTableTypesReq(data);
- 
-    return this.executeCommand<TGetTableTypesResp>(request, this.client.GetTableTypes);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html deleted file mode 100644 index 816bef9e..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetTablesCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetTablesCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetTablesCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetTablesReq, TGetTablesResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetTables'>;
- 
-export default class GetTablesCommand extends BaseCommand<Client> {
-  execute(data: TGetTablesReq): Promise<TGetTablesResp> {
-    const request = new TGetTablesReq(data);
- 
-    return this.executeCommand<TGetTablesResp>(request, this.client.GetTables);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html deleted file mode 100644 index d2091647..00000000 --- a/coverage/lcov-report/lib/hive/Commands/GetTypeInfoCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/GetTypeInfoCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands GetTypeInfoCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TGetTypeInfoReq, TGetTypeInfoResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'GetTypeInfo'>;
- 
-export default class GetTypeInfoCommand extends BaseCommand<Client> {
-  execute(data: TGetTypeInfoReq): Promise<TGetTypeInfoResp> {
-    const request = new TGetTypeInfoReq(data);
- 
-    return this.executeCommand<TGetTypeInfoResp>(request, this.client.GetTypeInfo);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html deleted file mode 100644 index b3bf7066..00000000 --- a/coverage/lcov-report/lib/hive/Commands/OpenSessionCommand.ts.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/OpenSessionCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands OpenSessionCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -231x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TOpenSessionReq, TOpenSessionResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'OpenSession'>;
- 
-/**
- * For auth mechanism GSSAPI the host and service should be provided when session is opened.
- *
- * TOpenSessionReq.configuration: {
- *   krb_host?: string;
- *   krb_service?: string;
- *   [key: string]: any;
- * }
- */
-export default class OpenSessionCommand extends BaseCommand<Client> {
-  execute(openSessionRequest: TOpenSessionReq): Promise<TOpenSessionResp> {
-    const request = new TOpenSessionReq(openSessionRequest);
- 
-    return this.executeCommand<TOpenSessionResp>(request, this.client.OpenSession);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html b/coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html deleted file mode 100644 index 1564fca2..00000000 --- a/coverage/lcov-report/lib/hive/Commands/RenewDelegationTokenCommand.ts.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands/RenewDelegationTokenCommand.ts - - - - - - - - - -
-
-

All files / lib/hive/Commands RenewDelegationTokenCommand.ts

-
- -
- 60% - Statements - 3/5 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 60% - Lines - 3/5 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -141x -1x -  -  -  -  -1x -  -  -  -  -  -  - 
import BaseCommand from './BaseCommand';
-import { TRenewDelegationTokenReq, TRenewDelegationTokenResp } from '../../../thrift/TCLIService_types';
-import IThriftClient from '../../contracts/IThriftClient';
- 
-type Client = Pick<IThriftClient, 'RenewDelegationToken'>;
- 
-export default class RenewDelegationTokenCommand extends BaseCommand<Client> {
-  execute(data: TRenewDelegationTokenReq): Promise<TRenewDelegationTokenResp> {
-    const request = new TRenewDelegationTokenReq(data);
- 
-    return this.executeCommand<TRenewDelegationTokenResp>(request, this.client.RenewDelegationToken);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/Commands/index.html b/coverage/lcov-report/lib/hive/Commands/index.html deleted file mode 100644 index ad23a77a..00000000 --- a/coverage/lcov-report/lib/hive/Commands/index.html +++ /dev/null @@ -1,431 +0,0 @@ - - - - - - Code coverage report for lib/hive/Commands - - - - - - - - - -
-
-

All files lib/hive/Commands

-
- -
- 51.93% - Statements - 67/129 -
- - -
- 0% - Branches - 0/22 -
- - -
- 0% - Functions - 0/26 -
- - -
- 51.93% - Lines - 67/129 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
BaseCommand.ts -
-
16.66%4/240%0/220%0/516.66%4/24
CancelDelegationTokenCommand.ts -
-
60%3/5100%0/00%0/160%3/5
CancelOperationCommand.ts -
-
60%3/5100%0/00%0/160%3/5
CloseOperationCommand.ts -
-
60%3/5100%0/00%0/160%3/5
CloseSessionCommand.ts -
-
60%3/5100%0/00%0/160%3/5
ExecuteStatementCommand.ts -
-
60%3/5100%0/00%0/160%3/5
FetchResultsCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetCatalogsCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetColumnsCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetCrossReferenceCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetDelegationTokenCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetFunctionsCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetInfoCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetOperationStatusCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetPrimaryKeysCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetResultSetMetadataCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetSchemasCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetTableTypesCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetTablesCommand.ts -
-
60%3/5100%0/00%0/160%3/5
GetTypeInfoCommand.ts -
-
60%3/5100%0/00%0/160%3/5
OpenSessionCommand.ts -
-
60%3/5100%0/00%0/160%3/5
RenewDelegationTokenCommand.ts -
-
60%3/5100%0/00%0/160%3/5
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/HiveDriver.ts.html b/coverage/lcov-report/lib/hive/HiveDriver.ts.html deleted file mode 100644 index f7734430..00000000 --- a/coverage/lcov-report/lib/hive/HiveDriver.ts.html +++ /dev/null @@ -1,637 +0,0 @@ - - - - - - Code coverage report for lib/hive/HiveDriver.ts - - - - - - - - - -
-
-

All files / lib/hive HiveDriver.ts

-
- -
- 25.58% - Statements - 22/86 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/22 -
- - -
- 25.58% - Lines - 22/86 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import {
-  TOpenSessionReq,
-  TCloseSessionReq,
-  TExecuteStatementReq,
-  TGetResultSetMetadataReq,
-  TFetchResultsReq,
-  TGetInfoReq,
-  TGetTypeInfoReq,
-  TGetCatalogsReq,
-  TGetSchemasReq,
-  TGetTablesReq,
-  TGetTableTypesReq,
-  TGetColumnsReq,
-  TGetFunctionsReq,
-  TGetPrimaryKeysReq,
-  TGetCrossReferenceReq,
-  TGetOperationStatusReq,
-  TCancelOperationReq,
-  TCloseOperationReq,
-  TGetDelegationTokenReq,
-  TCancelDelegationTokenReq,
-  TRenewDelegationTokenReq,
-} from '../../thrift/TCLIService_types';
-import OpenSessionCommand from './Commands/OpenSessionCommand';
-import CloseSessionCommand from './Commands/CloseSessionCommand';
-import ExecuteStatementCommand from './Commands/ExecuteStatementCommand';
-import GetResultSetMetadataCommand from './Commands/GetResultSetMetadataCommand';
-import FetchResultsCommand from './Commands/FetchResultsCommand';
-import GetInfoCommand from './Commands/GetInfoCommand';
-import GetTypeInfoCommand from './Commands/GetTypeInfoCommand';
-import GetCatalogsCommand from './Commands/GetCatalogsCommand';
-import GetSchemasCommand from './Commands/GetSchemasCommand';
-import GetTablesCommand from './Commands/GetTablesCommand';
-import GetTableTypesCommand from './Commands/GetTableTypesCommand';
-import GetColumnsCommand from './Commands/GetColumnsCommand';
-import GetFunctionsCommand from './Commands/GetFunctionsCommand';
-import GetPrimaryKeysCommand from './Commands/GetPrimaryKeysCommand';
-import GetCrossReferenceCommand from './Commands/GetCrossReferenceCommand';
-import GetOperationStatusCommand from './Commands/GetOperationStatusCommand';
-import CancelOperationCommand from './Commands/CancelOperationCommand';
-import CloseOperationCommand from './Commands/CloseOperationCommand';
-import GetDelegationTokenCommand from './Commands/GetDelegationTokenCommand';
-import CancelDelegationTokenCommand from './Commands/CancelDelegationTokenCommand';
-import RenewDelegationTokenCommand from './Commands/RenewDelegationTokenCommand';
-import IDriver from '../contracts/IDriver';
-import IClientContext from '../contracts/IClientContext';
- 
-export interface HiveDriverOptions {
-  context: IClientContext;
-}
- 
-export default class HiveDriver implements IDriver {
-  private readonly context: IClientContext;
- 
-  constructor(options: HiveDriverOptions) {
-    this.context = options.context;
-  }
- 
-  async openSession(request: TOpenSessionReq) {
-    const client = await this.context.getClient();
-    const action = new OpenSessionCommand(client, this.context);
-    return action.execute(request);
-  }
- 
-  async closeSession(request: TCloseSessionReq) {
-    const client = await this.context.getClient();
-    const command = new CloseSessionCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async executeStatement(request: TExecuteStatementReq) {
-    const client = await this.context.getClient();
-    const command = new ExecuteStatementCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getResultSetMetadata(request: TGetResultSetMetadataReq) {
-    const client = await this.context.getClient();
-    const command = new GetResultSetMetadataCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async fetchResults(request: TFetchResultsReq) {
-    const client = await this.context.getClient();
-    const command = new FetchResultsCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getInfo(request: TGetInfoReq) {
-    const client = await this.context.getClient();
-    const command = new GetInfoCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getTypeInfo(request: TGetTypeInfoReq) {
-    const client = await this.context.getClient();
-    const command = new GetTypeInfoCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getCatalogs(request: TGetCatalogsReq) {
-    const client = await this.context.getClient();
-    const command = new GetCatalogsCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getSchemas(request: TGetSchemasReq) {
-    const client = await this.context.getClient();
-    const command = new GetSchemasCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getTables(request: TGetTablesReq) {
-    const client = await this.context.getClient();
-    const command = new GetTablesCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getTableTypes(request: TGetTableTypesReq) {
-    const client = await this.context.getClient();
-    const command = new GetTableTypesCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getColumns(request: TGetColumnsReq) {
-    const client = await this.context.getClient();
-    const command = new GetColumnsCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getFunctions(request: TGetFunctionsReq) {
-    const client = await this.context.getClient();
-    const command = new GetFunctionsCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getPrimaryKeys(request: TGetPrimaryKeysReq) {
-    const client = await this.context.getClient();
-    const command = new GetPrimaryKeysCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getCrossReference(request: TGetCrossReferenceReq) {
-    const client = await this.context.getClient();
-    const command = new GetCrossReferenceCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getOperationStatus(request: TGetOperationStatusReq) {
-    const client = await this.context.getClient();
-    const command = new GetOperationStatusCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async cancelOperation(request: TCancelOperationReq) {
-    const client = await this.context.getClient();
-    const command = new CancelOperationCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async closeOperation(request: TCloseOperationReq) {
-    const client = await this.context.getClient();
-    const command = new CloseOperationCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async getDelegationToken(request: TGetDelegationTokenReq) {
-    const client = await this.context.getClient();
-    const command = new GetDelegationTokenCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async cancelDelegationToken(request: TCancelDelegationTokenReq) {
-    const client = await this.context.getClient();
-    const command = new CancelDelegationTokenCommand(client, this.context);
-    return command.execute(request);
-  }
- 
-  async renewDelegationToken(request: TRenewDelegationTokenReq) {
-    const client = await this.context.getClient();
-    const command = new RenewDelegationTokenCommand(client, this.context);
-    return command.execute(request);
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/hive/index.html b/coverage/lcov-report/lib/hive/index.html deleted file mode 100644 index 3afd7322..00000000 --- a/coverage/lcov-report/lib/hive/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for lib/hive - - - - - - - - - -
-
-

All files lib/hive

-
- -
- 25.58% - Statements - 22/86 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/22 -
- - -
- 25.58% - Lines - 22/86 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
HiveDriver.ts -
-
25.58%22/86100%0/00%0/2225.58%22/86
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/index.html b/coverage/lcov-report/lib/index.html deleted file mode 100644 index 2eafd6dd..00000000 --- a/coverage/lcov-report/lib/index.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - Code coverage report for lib - - - - - - - - - -
-
-

All files lib

-
- -
- 14.44% - Statements - 78/540 -
- - -
- 0.5% - Branches - 2/397 -
- - -
- 2.43% - Functions - 2/82 -
- - -
- 14.44% - Lines - 78/540 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
DBSQLClient.ts -
-
17.92%19/1060%0/544.54%1/2217.92%19/106
DBSQLLogger.ts -
-
25%3/120%0/60%0/325%3/12
DBSQLOperation.ts -
-
9.58%16/1670%0/1650%0/249.58%16/167
DBSQLParameter.ts -
-
54.54%18/334.65%2/4333.33%1/354.54%18/33
DBSQLSession.ts -
-
10.19%21/2060%0/1150%0/2610.19%21/206
polyfills.ts -
-
0%0/150%0/140%0/40%0/15
version.ts -
-
100%1/1100%0/0100%0/0100%1/1
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/polyfills.ts.html b/coverage/lcov-report/lib/polyfills.ts.html deleted file mode 100644 index 1d6b3ade..00000000 --- a/coverage/lcov-report/lib/polyfills.ts.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - Code coverage report for lib/polyfills.ts - - - - - - - - - -
-
-

All files / lib polyfills.ts

-
- -
- 0% - Statements - 0/15 -
- - -
- 0% - Branches - 0/14 -
- - -
- 0% - Functions - 0/4 -
- - -
- 0% - Lines - 0/15 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
/* eslint-disable import/prefer-default-export */
- 
-// `Array.at` / `TypedArray.at` is supported only since Nodejs@16.6.0
-// These methods are massively used by `apache-arrow@13`, but we have
-// to use this version because older ones contain some other nasty bugs
- 
-// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tointegerorinfinity
-function toIntegerOrInfinity(value: unknown): number {
-  const result = Number(value);
- 
-  // Return `0` for NaN; return `+Infinity` / `-Infinity` as is
-  if (!Number.isFinite(result)) {
-    return Number.isNaN(result) ? 0 : result;
-  }
- 
-  return Math.trunc(result);
-}
- 
-// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tolength
-function toLength(value: unknown): number {
-  const result = toIntegerOrInfinity(value);
-  return result > 0 ? Math.min(result, Number.MAX_SAFE_INTEGER) : 0;
-}
- 
-// https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.at
-export function at<T>(this: ArrayLike<T>, index: unknown): T | undefined {
-  const length = toLength(this.length);
-  const relativeIndex = toIntegerOrInfinity(index);
-  const absoluteIndex = relativeIndex >= 0 ? relativeIndex : length + relativeIndex;
-  return absoluteIndex >= 0 && absoluteIndex < length ? this[absoluteIndex] : undefined;
-}
- 
-const ArrayConstructors = [
-  global.Array,
-  global.Int8Array,
-  global.Uint8Array,
-  global.Uint8ClampedArray,
-  global.Int16Array,
-  global.Uint16Array,
-  global.Int32Array,
-  global.Uint32Array,
-  global.Float32Array,
-  global.Float64Array,
-  global.BigInt64Array,
-  global.BigUint64Array,
-];
- 
-ArrayConstructors.forEach((ArrayConstructor) => {
-  if (typeof ArrayConstructor.prototype.at !== 'function') {
-    ArrayConstructor.prototype.at = at;
-  }
-});
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/ArrowResultConverter.ts.html b/coverage/lcov-report/lib/result/ArrowResultConverter.ts.html deleted file mode 100644 index cff8b838..00000000 --- a/coverage/lcov-report/lib/result/ArrowResultConverter.ts.html +++ /dev/null @@ -1,754 +0,0 @@ - - - - - - Code coverage report for lib/result/ArrowResultConverter.ts - - - - - - - - - -
-
-

All files / lib/result ArrowResultConverter.ts

-
- -
- 5.81% - Statements - 5/86 -
- - -
- 0% - Branches - 0/93 -
- - -
- 0% - Functions - 0/11 -
- - -
- 5.95% - Lines - 5/84 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -2241x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -1x -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { Buffer } from 'buffer';
-import {
-  Table,
-  Schema,
-  Field,
-  TypeMap,
-  DataType,
-  Type,
-  StructRow,
-  MapRow,
-  Vector,
-  RecordBatch,
-  RecordBatchReader,
-  util as arrowUtils,
-} from 'apache-arrow';
-import { TGetResultSetMetadataResp, TColumnDesc } from '../../thrift/TCLIService_types';
-import IClientContext from '../contracts/IClientContext';
-import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
-import { ArrowBatch, getSchemaColumns, convertThriftValue } from './utils';
- 
-const { isArrowBigNumSymbol, bigNumToBigInt } = arrowUtils;
- 
-type ArrowSchema = Schema<TypeMap>;
-type ArrowSchemaField = Field<DataType<Type, TypeMap>>;
- 
-export default class ArrowResultConverter implements IResultsProvider<Array<any>> {
-  private readonly context: IClientContext;
- 
-  private readonly source: IResultsProvider<ArrowBatch>;
- 
-  private readonly schema: Array<TColumnDesc>;
- 
-  private recordBatchReader?: IterableIterator<RecordBatch<TypeMap>>;
- 
-  // Remaining rows in current Arrow batch (not the record batch!)
-  private remainingRows: number = 0;
- 
-  // This is the next (!!) record batch to be read. It is unset only in two cases:
-  // - prior to the first call to `fetchNext`
-  // - when no more data available
-  // This field is primarily used by a `hasMore`, so it can tell if next `fetchNext` will
-  // actually return a non-empty result
-  private prefetchedRecordBatch?: RecordBatch<TypeMap>;
- 
-  constructor(context: IClientContext, source: IResultsProvider<ArrowBatch>, { schema }: TGetResultSetMetadataResp) {
-    this.context = context;
-    this.source = source;
-    this.schema = getSchemaColumns(schema);
-  }
- 
-  public async hasMore() {
-    if (this.schema.length === 0) {
-      return false;
-    }
-    if (this.prefetchedRecordBatch) {
-      return true;
-    }
-    return this.source.hasMore();
-  }
- 
-  public async fetchNext(options: ResultsProviderFetchNextOptions) {
-    if (this.schema.length === 0) {
-      return [];
-    }
- 
-    // It's not possible to know if iterator has more items until trying to get the next item.
-    // So each time we read one batch ahead and store it, but process the batch prefetched on
-    // a previous `fetchNext` call. Because we actually already have the next item - it's easy
-    // to tell if the subsequent `fetchNext` will be able to read anything, and `hasMore` logic
-    // becomes trivial
- 
-    // This prefetch handles a first call to `fetchNext`, when all the internal fields are not initialized yet.
-    // On subsequent calls to `fetchNext` it will do nothing
-    await this.prefetch(options);
- 
-    if (this.prefetchedRecordBatch) {
-      // Consume a record batch fetched during previous call to `fetchNext`
-      const table = new Table(this.prefetchedRecordBatch);
-      this.prefetchedRecordBatch = undefined;
-      // Get table rows, but not more than remaining count
-      const arrowRows = table.toArray().slice(0, this.remainingRows);
-      const result = this.getRows(table.schema, arrowRows);
- 
-      // Reduce remaining rows count by a count of rows we just processed.
-      // If the remaining count reached zero - we're done with current arrow
-      // batch, so discard the batch reader
-      this.remainingRows -= result.length;
-      if (this.remainingRows === 0) {
-        this.recordBatchReader = undefined;
-      }
- 
-      // Prefetch the next record batch
-      await this.prefetch(options);
- 
-      return result;
-    }
- 
-    return [];
-  }
- 
-  // This method tries to read one more record batch and store it in `prefetchedRecordBatch` field.
-  // If `prefetchedRecordBatch` is already non-empty - the method does nothing.
-  // This method pulls the next item from source if needed, initializes a record batch reader and
-  // gets the next item from it - until either reaches end of data or finds a non-empty record batch
-  private async prefetch(options: ResultsProviderFetchNextOptions) {
-    // This loop will be executed until a next non-empty record batch is retrieved
-    // Another implicit loop condition (end of data) is checked in the loop body
-    while (!this.prefetchedRecordBatch) {
-      // First, try to fetch next item from source and initialize record batch reader.
-      // If source has no more data - exit prematurely
-      if (!this.recordBatchReader) {
-        const sourceHasMore = await this.source.hasMore(); // eslint-disable-line no-await-in-loop
-        if (!sourceHasMore) {
-          return;
-        }
- 
-        const arrowBatch = await this.source.fetchNext(options); // eslint-disable-line no-await-in-loop
-        if (arrowBatch.batches.length > 0 && arrowBatch.rowCount > 0) {
-          const reader = RecordBatchReader.from<TypeMap>(arrowBatch.batches);
-          this.recordBatchReader = reader[Symbol.iterator]();
-          this.remainingRows = arrowBatch.rowCount;
-        }
-      }
- 
-      // Try to get a next item from current record batch reader. The reader may be unavailable at this point -
-      // in this case we fall back to a "done" state, and the `while` loop will do one more iteration attempting
-      // to create a new reader. Eventually it will either succeed or reach end of source. This scenario also
-      // handles readers which are already empty
-      const item = this.recordBatchReader?.next() ?? { done: true, value: undefined };
-      if (item.done || item.value === undefined) {
-        this.recordBatchReader = undefined;
-      } else {
-        // Skip empty batches
-        // eslint-disable-next-line no-lonely-if
-        if (item.value.numRows > 0) {
-          this.prefetchedRecordBatch = item.value;
-        }
-      }
-    }
-  }
- 
-  private getRows(schema: ArrowSchema, rows: Array<StructRow | MapRow>): Array<any> {
-    return rows.map((row) => {
-      // First, convert native Arrow values to corresponding plain JS objects
-      const record = this.convertArrowTypes(row, undefined, schema.fields);
-      // Second, cast all the values to original Thrift types
-      return this.convertThriftTypes(record);
-    });
-  }
- 
-  private convertArrowTypes(value: any, valueType: DataType | undefined, fields: Array<ArrowSchemaField> = []): any {
-    if (value === null) {
-      return value;
-    }
- 
-    const fieldsMap: Record<string, ArrowSchemaField> = {};
-    for (const field of fields) {
-      fieldsMap[field.name] = field;
-    }
- 
-    // Convert structures to plain JS object and process all its fields recursively
-    if (value instanceof StructRow) {
-      const result = value.toJSON();
-      for (const key of Object.keys(result)) {
-        const field: ArrowSchemaField | undefined = fieldsMap[key];
-        result[key] = this.convertArrowTypes(result[key], field?.type, field?.type.children || []);
-      }
-      return result;
-    }
-    if (value instanceof MapRow) {
-      const result = value.toJSON();
-      // Map type consists of its key and value types. We need only value type here, key will be cast to string anyway
-      const field = fieldsMap.entries?.type.children.find((item) => item.name === 'value');
-      for (const key of Object.keys(result)) {
-        result[key] = this.convertArrowTypes(result[key], field?.type, field?.type.children || []);
-      }
-      return result;
-    }
- 
-    // Convert lists to JS array and process items recursively
-    if (value instanceof Vector) {
-      const result = value.toJSON();
-      // Array type contains the only child which defines a type of each array's element
-      const field = fieldsMap.element;
-      return result.map((item) => this.convertArrowTypes(item, field?.type, field?.type.children || []));
-    }
- 
-    if (DataType.isTimestamp(valueType)) {
-      return new Date(value);
-    }
- 
-    // Convert big number values to BigInt
-    // Decimals are also represented as big numbers in Arrow, so additionally process them (convert to float)
-    if (value instanceof Object && value[isArrowBigNumSymbol]) {
-      const result = bigNumToBigInt(value);
-      if (DataType.isDecimal(valueType)) {
-        return Number(result) / 10 ** valueType.scale;
-      }
-      return result;
-    }
- 
-    // Convert binary data to Buffer
-    if (value instanceof Uint8Array) {
-      return Buffer.from(value);
-    }
- 
-    // Return other values as is
-    return typeof value === 'bigint' ? Number(value) : value;
-  }
- 
-  private convertThriftTypes(record: Record<string, any>): any {
-    const result: Record<string, any> = {};
- 
-    this.schema.forEach((column) => {
-      const typeDescriptor = column.typeDesc.types[0]?.primitiveEntry;
-      const field = column.columnName;
-      const value = record[field];
-      result[field] = value === null ? null : convertThriftValue(typeDescriptor, value);
-    });
- 
-    return result;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/ArrowResultHandler.ts.html b/coverage/lcov-report/lib/result/ArrowResultHandler.ts.html deleted file mode 100644 index 15272858..00000000 --- a/coverage/lcov-report/lib/result/ArrowResultHandler.ts.html +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - Code coverage report for lib/result/ArrowResultHandler.ts - - - - - - - - - -
-
-

All files / lib/result ArrowResultHandler.ts

-
- -
- 16% - Statements - 4/25 -
- - -
- 0% - Branches - 0/30 -
- - -
- 0% - Functions - 0/4 -
- - -
- 16% - Lines - 4/25 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73  -1x -  -  -1x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { TGetResultSetMetadataResp, TRowSet } from '../../thrift/TCLIService_types';
-import HiveDriverError from '../errors/HiveDriverError';
-import IClientContext from '../contracts/IClientContext';
-import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
-import { ArrowBatch, hiveSchemaToArrowSchema } from './utils';
-import { LZ4 } from '../utils';
- 
-export default class ArrowResultHandler implements IResultsProvider<ArrowBatch> {
-  private readonly context: IClientContext;
- 
-  private readonly source: IResultsProvider<TRowSet | undefined>;
- 
-  private readonly arrowSchema?: Buffer;
- 
-  private readonly isLZ4Compressed: boolean;
- 
-  constructor(
-    context: IClientContext,
-    source: IResultsProvider<TRowSet | undefined>,
-    { schema, arrowSchema, lz4Compressed }: TGetResultSetMetadataResp,
-  ) {
-    this.context = context;
-    this.source = source;
-    // Arrow schema is not available in old DBR versions, which also don't support native Arrow types,
-    // so it's possible to infer Arrow schema from Hive schema ignoring `useArrowNativeTypes` option
-    this.arrowSchema = arrowSchema ?? hiveSchemaToArrowSchema(schema);
-    this.isLZ4Compressed = lz4Compressed ?? false;
- 
-    if (this.isLZ4Compressed && !LZ4()) {
-      throw new HiveDriverError('Cannot handle LZ4 compressed result: module `lz4` not installed');
-    }
-  }
- 
-  public async hasMore() {
-    if (!this.arrowSchema) {
-      return false;
-    }
-    return this.source.hasMore();
-  }
- 
-  public async fetchNext(options: ResultsProviderFetchNextOptions) {
-    if (!this.arrowSchema) {
-      return {
-        batches: [],
-        rowCount: 0,
-      };
-    }
- 
-    const rowSet = await this.source.fetchNext(options);
- 
-    const batches: Array<Buffer> = [];
-    let totalRowCount = 0;
-    rowSet?.arrowBatches?.forEach(({ batch, rowCount }) => {
-      if (batch) {
-        batches.push(this.isLZ4Compressed ? LZ4()!.decode(batch) : batch);
-        totalRowCount += rowCount.toNumber(true);
-      }
-    });
- 
-    if (batches.length === 0) {
-      return {
-        batches: [],
-        rowCount: 0,
-      };
-    }
- 
-    return {
-      batches: [this.arrowSchema, ...batches],
-      rowCount: totalRowCount,
-    };
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html b/coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html deleted file mode 100644 index 3b6cf6ac..00000000 --- a/coverage/lcov-report/lib/result/CloudFetchResultHandler.ts.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - Code coverage report for lib/result/CloudFetchResultHandler.ts - - - - - - - - - -
-
-

All files / lib/result CloudFetchResultHandler.ts

-
- -
- 8.77% - Statements - 5/57 -
- - -
- 0% - Branches - 0/32 -
- - -
- 0% - Functions - 0/11 -
- - -
- 9.25% - Lines - 5/54 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -1281x -  -1x -  -  -  -1x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import fetch, { RequestInfo, RequestInit, Request } from 'node-fetch';
-import { TGetResultSetMetadataResp, TRowSet, TSparkArrowResultLink } from '../../thrift/TCLIService_types';
-import HiveDriverError from '../errors/HiveDriverError';
-import IClientContext from '../contracts/IClientContext';
-import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
-import { ArrowBatch } from './utils';
-import { LZ4 } from '../utils';
-import { LogLevel } from '../contracts/IDBSQLLogger';
- 
-export default class CloudFetchResultHandler implements IResultsProvider<ArrowBatch> {
-  private readonly context: IClientContext;
- 
-  private readonly source: IResultsProvider<TRowSet | undefined>;
- 
-  private readonly isLZ4Compressed: boolean;
- 
-  private pendingLinks: Array<TSparkArrowResultLink> = [];
- 
-  private downloadTasks: Array<Promise<ArrowBatch>> = [];
- 
-  constructor(
-    context: IClientContext,
-    source: IResultsProvider<TRowSet | undefined>,
-    { lz4Compressed }: TGetResultSetMetadataResp,
-  ) {
-    this.context = context;
-    this.source = source;
-    this.isLZ4Compressed = lz4Compressed ?? false;
- 
-    if (this.isLZ4Compressed && !LZ4()) {
-      throw new HiveDriverError('Cannot handle LZ4 compressed result: module `lz4` not installed');
-    }
-  }
- 
-  public async hasMore() {
-    if (this.pendingLinks.length > 0 || this.downloadTasks.length > 0) {
-      return true;
-    }
-    return this.source.hasMore();
-  }
- 
-  public async fetchNext(options: ResultsProviderFetchNextOptions) {
-    const data = await this.source.fetchNext(options);
- 
-    data?.resultLinks?.forEach((link) => {
-      this.pendingLinks.push(link);
-    });
- 
-    const clientConfig = this.context.getConfig();
-    const freeTaskSlotsCount = clientConfig.cloudFetchConcurrentDownloads - this.downloadTasks.length;
- 
-    if (freeTaskSlotsCount > 0) {
-      const links = this.pendingLinks.splice(0, freeTaskSlotsCount);
-      const tasks = links.map((link) => this.downloadLink(link));
-      this.downloadTasks.push(...tasks);
-    }
- 
-    const batch = await this.downloadTasks.shift();
-    if (!batch) {
-      return {
-        batches: [],
-        rowCount: 0,
-      };
-    }
- 
-    if (this.isLZ4Compressed) {
-      batch.batches = batch.batches.map((buffer) => LZ4()!.decode(buffer));
-    }
-    return batch;
-  }
- 
-  private logDownloadMetrics(url: string, fileSizeBytes: number, downloadTimeMs: number): void {
-    const speedMBps = fileSizeBytes / (1024 * 1024) / (downloadTimeMs / 1000);
-    const cleanUrl = url.split('?')[0];
- 
-    this.context
-      .getLogger()
-      .log(LogLevel.info, `Result File Download speed from cloud storage ${cleanUrl}: ${speedMBps.toFixed(4)} MB/s`);
- 
-    const speedThresholdMBps = this.context.getConfig().cloudFetchSpeedThresholdMBps;
-    if (speedMBps < speedThresholdMBps) {
-      this.context
-        .getLogger()
-        .log(
-          LogLevel.warn,
-          `Results download is slower than threshold speed of ${speedThresholdMBps.toFixed(
-            4,
-          )} MB/s: ${speedMBps.toFixed(4)} MB/s`,
-        );
-    }
-  }
- 
-  private async downloadLink(link: TSparkArrowResultLink): Promise<ArrowBatch> {
-    if (Date.now() >= link.expiryTime.toNumber()) {
-      throw new Error('CloudFetch link has expired');
-    }
- 
-    const startTime = Date.now();
-    const response = await this.fetch(link.fileLink, { headers: link.httpHeaders });
-    if (!response.ok) {
-      throw new Error(`CloudFetch HTTP error ${response.status} ${response.statusText}`);
-    }
- 
-    const result = await response.arrayBuffer();
-    const downloadTimeMs = Date.now() - startTime;
- 
-    this.logDownloadMetrics(link.fileLink, result.byteLength, downloadTimeMs);
- 
-    return {
-      batches: [Buffer.from(result)],
-      rowCount: link.rowCount.toNumber(true),
-    };
-  }
- 
-  private async fetch(url: RequestInfo, init?: RequestInit) {
-    const connectionProvider = await this.context.getConnectionProvider();
-    const agent = await connectionProvider.getAgent();
-    const retryPolicy = await connectionProvider.getRetryPolicy();
- 
-    const requestConfig: RequestInit = { agent, ...init };
-    const result = await retryPolicy.invokeWithRetry(() => {
-      const request = new Request(url, requestConfig);
-      return fetch(request).then((response) => ({ request, response }));
-    });
-    return result.response;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/JsonResultHandler.ts.html b/coverage/lcov-report/lib/result/JsonResultHandler.ts.html deleted file mode 100644 index f00443e4..00000000 --- a/coverage/lcov-report/lib/result/JsonResultHandler.ts.html +++ /dev/null @@ -1,328 +0,0 @@ - - - - - - Code coverage report for lib/result/JsonResultHandler.ts - - - - - - - - - -
-
-

All files / lib/result JsonResultHandler.ts

-
- -
- 6.45% - Statements - 2/31 -
- - -
- 0% - Branches - 0/18 -
- - -
- 0% - Functions - 0/9 -
- - -
- 6.45% - Lines - 2/31 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82  -  -  -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import { TGetResultSetMetadataResp, TRowSet, TColumn, TColumnDesc } from '../../thrift/TCLIService_types';
-import IClientContext from '../contracts/IClientContext';
-import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
-import { getSchemaColumns, convertThriftValue, getColumnValue } from './utils';
- 
-export default class JsonResultHandler implements IResultsProvider<Array<any>> {
-  private readonly context: IClientContext;
- 
-  private readonly source: IResultsProvider<TRowSet | undefined>;
- 
-  private readonly schema: Array<TColumnDesc>;
- 
-  constructor(
-    context: IClientContext,
-    source: IResultsProvider<TRowSet | undefined>,
-    { schema }: TGetResultSetMetadataResp,
-  ) {
-    this.context = context;
-    this.source = source;
-    this.schema = getSchemaColumns(schema);
-  }
- 
-  public async hasMore() {
-    return this.source.hasMore();
-  }
- 
-  public async fetchNext(options: ResultsProviderFetchNextOptions) {
-    if (this.schema.length === 0) {
-      return [];
-    }
- 
-    const data = await this.source.fetchNext(options);
-    if (!data) {
-      return [];
-    }
- 
-    const columns = data.columns || [];
-    return this.getRows(columns, this.schema);
-  }
- 
-  private getRows(columns: Array<TColumn>, descriptors: Array<TColumnDesc>): Array<any> {
-    return descriptors.reduce(
-      (rows, descriptor) =>
-        this.getSchemaValues(descriptor, columns[descriptor.position - 1]).reduce((result, value, i) => {
-          if (!result[i]) {
-            result[i] = {};
-          }
- 
-          const { columnName } = descriptor;
- 
-          result[i][columnName] = value;
- 
-          return result;
-        }, rows),
-      [],
-    );
-  }
- 
-  private getSchemaValues(descriptor: TColumnDesc, column?: TColumn): Array<any> {
-    const typeDescriptor = descriptor.typeDesc.types[0]?.primitiveEntry;
-    const columnValue = getColumnValue(column);
- 
-    if (!columnValue) {
-      return [];
-    }
- 
-    return columnValue.values.map((value: any, i: number) => {
-      if (columnValue.nulls && this.isNull(columnValue.nulls, i)) {
-        return null;
-      }
-      return convertThriftValue(typeDescriptor, value);
-    });
-  }
- 
-  private isNull(nulls: Buffer, i: number): boolean {
-    const byte = nulls[Math.floor(i / 8)];
-    const ofs = 2 ** (i % 8);
- 
-    return (byte & ofs) !== 0;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/ResultSlicer.ts.html b/coverage/lcov-report/lib/result/ResultSlicer.ts.html deleted file mode 100644 index 80e69a90..00000000 --- a/coverage/lcov-report/lib/result/ResultSlicer.ts.html +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - Code coverage report for lib/result/ResultSlicer.ts - - - - - - - - - -
-
-

All files / lib/result ResultSlicer.ts

-
- -
- 3.12% - Statements - 1/32 -
- - -
- 0% - Branches - 0/16 -
- - -
- 0% - Functions - 0/3 -
- - -
- 3.12% - Lines - 1/32 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import IClientContext from '../contracts/IClientContext';
-import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
- 
-export interface ResultSlicerFetchNextOptions extends ResultsProviderFetchNextOptions {
-  // Setting this to `true` will disable slicer, and it will return unprocessed chunks
-  // from underlying results provider
-  disableBuffering?: boolean;
-}
- 
-export default class ResultSlicer<T> implements IResultsProvider<Array<T>> {
-  private readonly context: IClientContext;
- 
-  private readonly source: IResultsProvider<Array<T>>;
- 
-  private remainingResults: Array<T> = [];
- 
-  constructor(context: IClientContext, source: IResultsProvider<Array<T>>) {
-    this.context = context;
-    this.source = source;
-  }
- 
-  public async hasMore(): Promise<boolean> {
-    if (this.remainingResults.length > 0) {
-      return true;
-    }
-    return this.source.hasMore();
-  }
- 
-  public async fetchNext(options: ResultSlicerFetchNextOptions): Promise<Array<T>> {
-    // If we're asked to not use buffer - first try to return whatever we have in buffer.
-    // If buffer is empty - just proxy the call to underlying results provider
-    if (options.disableBuffering) {
-      if (this.remainingResults.length > 0) {
-        const result = this.remainingResults;
-        this.remainingResults = [];
-        return result;
-      }
- 
-      return this.source.fetchNext(options);
-    }
- 
-    const result: Array<Array<T>> = [];
-    let resultsCount = 0;
- 
-    // First, use remaining items from the previous fetch
-    if (this.remainingResults.length > 0) {
-      result.push(this.remainingResults);
-      resultsCount += this.remainingResults.length;
-      this.remainingResults = [];
-    }
- 
-    // Fetch items from source results provider until we reach a requested count
-    while (resultsCount < options.limit) {
-      // eslint-disable-next-line no-await-in-loop
-      const hasMore = await this.source.hasMore();
-      if (!hasMore) {
-        break;
-      }
- 
-      // eslint-disable-next-line no-await-in-loop
-      const chunk = await this.source.fetchNext(options);
-      result.push(chunk);
-      resultsCount += chunk.length;
-    }
- 
-    // If we collected more results than requested, slice the excess items and store them for the next time
-    if (resultsCount > options.limit) {
-      const lastChunk = result.pop() ?? [];
-      const neededCount = options.limit - (resultsCount - lastChunk.length);
-      result.push(lastChunk.splice(0, neededCount));
-      this.remainingResults = lastChunk;
-    }
- 
-    return result.flat();
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/RowSetProvider.ts.html b/coverage/lcov-report/lib/result/RowSetProvider.ts.html deleted file mode 100644 index 79832b4f..00000000 --- a/coverage/lcov-report/lib/result/RowSetProvider.ts.html +++ /dev/null @@ -1,412 +0,0 @@ - - - - - - Code coverage report for lib/result/RowSetProvider.ts - - - - - - - - - -
-
-

All files / lib/result RowSetProvider.ts

-
- -
- 19.04% - Statements - 8/42 -
- - -
- 5.26% - Branches - 2/38 -
- - -
- 12.5% - Functions - 1/8 -
- - -
- 19.04% - Lines - 8/42 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -1101x -1x -1x -  -  -1x -  -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import Int64 from 'node-int64';
-import { TFetchOrientation, TFetchResultsResp, TOperationHandle, TRowSet } from '../../thrift/TCLIService_types';
-import Status from '../dto/Status';
-import IClientContext from '../contracts/IClientContext';
-import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
-import { getColumnValue } from './utils';
- 
-export enum FetchType {
-  Data = 0,
-  Logs = 1,
-}
- 
-function checkIfOperationHasMoreRows(response: TFetchResultsResp): boolean {
-  if (response.hasMoreRows) {
-    return true;
-  }
- 
-  const columns = response.results?.columns || [];
- 
-  const columnValue = getColumnValue(columns[0]);
-  return (columnValue?.values?.length ?? 0) > 0;
-}
- 
-export default class RowSetProvider implements IResultsProvider<TRowSet | undefined> {
-  private readonly context: IClientContext;
- 
-  private readonly operationHandle: TOperationHandle;
- 
-  private fetchOrientation: TFetchOrientation = TFetchOrientation.FETCH_FIRST;
- 
-  private prefetchedResults: TFetchResultsResp[] = [];
- 
-  private readonly returnOnlyPrefetchedResults: boolean;
- 
-  private hasMoreRowsFlag?: boolean = undefined;
- 
-  private get hasMoreRows(): boolean {
-    // `hasMoreRowsFlag` is populated only after fetching the first row set.
-    // Prior to that, we use a `operationHandle.hasResultSet` flag which
-    // is set if there are any data at all. Also, we have to choose appropriate
-    // flag in a getter because both `hasMoreRowsFlag` and `operationHandle.hasResultSet`
-    // may change between this getter calls
-    return this.hasMoreRowsFlag ?? this.operationHandle.hasResultSet;
-  }
- 
-  constructor(
-    context: IClientContext,
-    operationHandle: TOperationHandle,
-    prefetchedResults: Array<TFetchResultsResp | undefined>,
-    returnOnlyPrefetchedResults: boolean,
-  ) {
-    this.context = context;
-    this.operationHandle = operationHandle;
-    prefetchedResults.forEach((item) => {
-      if (item) {
-        this.prefetchedResults.push(item);
-      }
-    });
-    this.returnOnlyPrefetchedResults = returnOnlyPrefetchedResults;
-  }
- 
-  private processFetchResponse(response: TFetchResultsResp): TRowSet | undefined {
-    Status.assert(response.status);
-    this.fetchOrientation = TFetchOrientation.FETCH_NEXT;
-    this.hasMoreRowsFlag = checkIfOperationHasMoreRows(response);
-    return response.results;
-  }
- 
-  public async fetchNext({ limit }: ResultsProviderFetchNextOptions) {
-    const prefetchedResponse = this.prefetchedResults.shift();
-    if (prefetchedResponse) {
-      return this.processFetchResponse(prefetchedResponse);
-    }
- 
-    // We end up here if no more prefetched results available (checked above)
-    if (this.returnOnlyPrefetchedResults) {
-      return undefined;
-    }
- 
-    // Don't fetch next chunk if there are no more data available
-    if (!this.hasMoreRows) {
-      return undefined;
-    }
- 
-    const driver = await this.context.getDriver();
-    const response = await driver.fetchResults({
-      operationHandle: this.operationHandle,
-      orientation: this.fetchOrientation,
-      maxRows: new Int64(limit),
-      fetchType: FetchType.Data,
-    });
- 
-    return this.processFetchResponse(response);
-  }
- 
-  public async hasMore() {
-    // If there are prefetched results available - return `true` regardless of
-    // the actual state of `hasMoreRows` flag (because we actually have some data)
-    if (this.prefetchedResults.length > 0) {
-      return true;
-    }
-    // We end up here if no more prefetched results available (checked above)
-    if (this.returnOnlyPrefetchedResults) {
-      return false;
-    }
- 
-    return this.hasMoreRows;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/index.html b/coverage/lcov-report/lib/result/index.html deleted file mode 100644 index 3ed33e30..00000000 --- a/coverage/lcov-report/lib/result/index.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - Code coverage report for lib/result - - - - - - - - - -
-
-

All files lib/result

-
- -
- 10.49% - Statements - 34/324 -
- - -
- 0.64% - Branches - 2/308 -
- - -
- 1.81% - Functions - 1/55 -
- - -
- 10.69% - Lines - 34/318 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
ArrowResultConverter.ts -
-
5.81%5/860%0/930%0/115.95%5/84
ArrowResultHandler.ts -
-
16%4/250%0/300%0/416%4/25
CloudFetchResultHandler.ts -
-
8.77%5/570%0/320%0/119.25%5/54
JsonResultHandler.ts -
-
6.45%2/310%0/180%0/96.45%2/31
ResultSlicer.ts -
-
3.12%1/320%0/160%0/33.12%1/32
RowSetProvider.ts -
-
19.04%8/425.26%2/3812.5%1/819.04%8/42
utils.ts -
-
17.64%9/510%0/810%0/918%9/50
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/result/utils.ts.html b/coverage/lcov-report/lib/result/utils.ts.html deleted file mode 100644 index 77b41a4d..00000000 --- a/coverage/lcov-report/lib/result/utils.ts.html +++ /dev/null @@ -1,574 +0,0 @@ - - - - - - Code coverage report for lib/result/utils.ts - - - - - - - - - -
-
-

All files / lib/result utils.ts

-
- -
- 17.64% - Statements - 9/51 -
- - -
- 0% - Branches - 0/81 -
- - -
- 0% - Functions - 0/9 -
- - -
- 18% - Lines - 9/50 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -1641x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -1x -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import Int64 from 'node-int64';
-import {
-  Schema,
-  Field,
-  DataType,
-  Bool as ArrowBool,
-  Int8 as ArrowInt8,
-  Int16 as ArrowInt16,
-  Int32 as ArrowInt32,
-  Int64 as ArrowInt64,
-  Float32 as ArrowFloat32,
-  Float64 as ArrowFloat64,
-  Utf8 as ArrowString,
-  Date_ as ArrowDate,
-  Binary as ArrowBinary,
-  DateUnit,
-  RecordBatchWriter,
-} from 'apache-arrow';
-import { TTableSchema, TColumnDesc, TPrimitiveTypeEntry, TTypeId, TColumn } from '../../thrift/TCLIService_types';
-import HiveDriverError from '../errors/HiveDriverError';
- 
-export interface ArrowBatch {
-  batches: Array<Buffer>;
-  rowCount: number;
-}
- 
-export function getSchemaColumns(schema?: TTableSchema): Array<TColumnDesc> {
-  if (!schema) {
-    return [];
-  }
- 
-  return [...schema.columns].sort((c1, c2) => c1.position - c2.position);
-}
- 
-function isString(value: unknown): value is string {
-  return typeof value === 'string' || value instanceof String;
-}
- 
-function convertJSON(value: any, defaultValue: any): any {
-  if (!isString(value)) {
-    return value;
-  }
- 
-  try {
-    return JSON.parse(value);
-  } catch (e) {
-    return defaultValue;
-  }
-}
- 
-function convertBigInt(value: any): any {
-  if (typeof value === 'bigint') {
-    return Number(value);
-  }
-  if (value instanceof Int64) {
-    return value.toNumber();
-  }
-  return value;
-}
- 
-export function convertThriftValue(typeDescriptor: TPrimitiveTypeEntry | undefined, value: any): any {
-  if (!typeDescriptor) {
-    return value;
-  }
- 
-  switch (typeDescriptor.type) {
-    case TTypeId.DATE_TYPE:
-    case TTypeId.TIMESTAMP_TYPE:
-      return value;
-    case TTypeId.UNION_TYPE:
-    case TTypeId.USER_DEFINED_TYPE:
-      return String(value);
-    case TTypeId.DECIMAL_TYPE:
-      return Number(value);
-    case TTypeId.STRUCT_TYPE:
-    case TTypeId.MAP_TYPE:
-      return convertJSON(value, {});
-    case TTypeId.ARRAY_TYPE:
-      return convertJSON(value, []);
-    case TTypeId.BIGINT_TYPE:
-      return convertBigInt(value);
-    case TTypeId.NULL_TYPE:
-    case TTypeId.BINARY_TYPE:
-    case TTypeId.INTERVAL_YEAR_MONTH_TYPE:
-    case TTypeId.INTERVAL_DAY_TIME_TYPE:
-    case TTypeId.FLOAT_TYPE:
-    case TTypeId.DOUBLE_TYPE:
-    case TTypeId.INT_TYPE:
-    case TTypeId.SMALLINT_TYPE:
-    case TTypeId.TINYINT_TYPE:
-    case TTypeId.BOOLEAN_TYPE:
-    case TTypeId.STRING_TYPE:
-    case TTypeId.CHAR_TYPE:
-    case TTypeId.VARCHAR_TYPE:
-    default:
-      return value;
-  }
-}
- 
-// This type map corresponds to Arrow without native types support (most complex types are serialized as strings)
-const hiveTypeToArrowType: Record<TTypeId, DataType | null> = {
-  [TTypeId.BOOLEAN_TYPE]: new ArrowBool(),
-  [TTypeId.TINYINT_TYPE]: new ArrowInt8(),
-  [TTypeId.SMALLINT_TYPE]: new ArrowInt16(),
-  [TTypeId.INT_TYPE]: new ArrowInt32(),
-  [TTypeId.BIGINT_TYPE]: new ArrowInt64(),
-  [TTypeId.FLOAT_TYPE]: new ArrowFloat32(),
-  [TTypeId.DOUBLE_TYPE]: new ArrowFloat64(),
-  [TTypeId.STRING_TYPE]: new ArrowString(),
-  [TTypeId.TIMESTAMP_TYPE]: new ArrowString(),
-  [TTypeId.BINARY_TYPE]: new ArrowBinary(),
-  [TTypeId.ARRAY_TYPE]: new ArrowString(),
-  [TTypeId.MAP_TYPE]: new ArrowString(),
-  [TTypeId.STRUCT_TYPE]: new ArrowString(),
-  [TTypeId.UNION_TYPE]: new ArrowString(),
-  [TTypeId.USER_DEFINED_TYPE]: new ArrowString(),
-  [TTypeId.DECIMAL_TYPE]: new ArrowString(),
-  [TTypeId.NULL_TYPE]: null,
-  [TTypeId.DATE_TYPE]: new ArrowDate(DateUnit.DAY),
-  [TTypeId.VARCHAR_TYPE]: new ArrowString(),
-  [TTypeId.CHAR_TYPE]: new ArrowString(),
-  [TTypeId.INTERVAL_YEAR_MONTH_TYPE]: new ArrowString(),
-  [TTypeId.INTERVAL_DAY_TIME_TYPE]: new ArrowString(),
-};
- 
-export function hiveSchemaToArrowSchema(schema?: TTableSchema): Buffer | undefined {
-  if (!schema) {
-    return undefined;
-  }
- 
-  const columns = getSchemaColumns(schema);
- 
-  const arrowFields = columns.map((column) => {
-    const hiveType = column.typeDesc.types[0].primitiveEntry?.type ?? undefined;
-    const arrowType = hiveType !== undefined ? hiveTypeToArrowType[hiveType] : undefined;
-    if (!arrowType) {
-      throw new HiveDriverError(`Unsupported column type: ${hiveType ? TTypeId[hiveType] : 'undefined'}`);
-    }
-    return new Field(column.columnName, arrowType, true);
-  });
- 
-  const arrowSchema = new Schema(arrowFields);
-  const writer = new RecordBatchWriter();
-  writer.reset(undefined, arrowSchema);
-  writer.finish();
-  return Buffer.from(writer.toUint8Array(true));
-}
- 
-export function getColumnValue(column?: TColumn) {
-  if (!column) {
-    return undefined;
-  }
-  return (
-    column.binaryVal ??
-    column.boolVal ??
-    column.byteVal ??
-    column.doubleVal ??
-    column.i16Val ??
-    column.i32Val ??
-    column.i64Val ??
-    column.stringVal
-  );
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html b/coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html deleted file mode 100644 index 38a0463c..00000000 --- a/coverage/lcov-report/lib/telemetry/CircuitBreaker.ts.html +++ /dev/null @@ -1,793 +0,0 @@ - - - - - - Code coverage report for lib/telemetry/CircuitBreaker.ts - - - - - - - - - -
-
-

All files / lib/telemetry CircuitBreaker.ts

-
- -
- 100% - Statements - 61/61 -
- - -
- 100% - Branches - 18/18 -
- - -
- 100% - Functions - 13/13 -
- - -
- 100% - Lines - 61/61 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -1x -  -1x -  -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -50x -  -50x -  -50x -  -  -  -  -  -50x -50x -  -  -  -  -  -  -  -  -  -  -  -  -  -110x -  -  -110x -13x -5x -  -  -8x -8x -8x -  -  -105x -105x -20x -20x -  -85x -85x -  -  -  -  -  -  -  -27x -  -  -  -  -  -  -9x -  -  -  -  -  -  -5x -  -  -  -  -  -  -20x -  -  -20x -  -20x -11x -11x -  -  -  -  -11x -  -3x -3x -3x -3x -  -  -  -  -  -  -  -  -85x -  -85x -85x -  -85x -  -  -  -85x -18x -18x -18x -  -  -  -  -  -  -  -  -  -1x -  -  -27x -27x -  -  -  -  -  -  -  -  -  -  -34x -34x -31x -31x -31x -31x -  -34x -  -  -  -  -  -  -  -6x -  -  -  -  -  -  -  -  -  -3x -3x -3x -  -  -  -  -  -  -  -1x -  -  - 
/**
- * Copyright (c) 2025 Databricks Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-import IClientContext from '../contracts/IClientContext';
-import { LogLevel } from '../contracts/IDBSQLLogger';
- 
-/**
- * States of the circuit breaker.
- */
-export enum CircuitBreakerState {
-  /** Normal operation, requests pass through */
-  CLOSED = 'CLOSED',
-  /** After threshold failures, all requests rejected immediately */
-  OPEN = 'OPEN',
-  /** After timeout, allows test requests to check if endpoint recovered */
-  HALF_OPEN = 'HALF_OPEN',
-}
- 
-/**
- * Configuration for circuit breaker behavior.
- */
-export interface CircuitBreakerConfig {
-  /** Number of consecutive failures before opening the circuit */
-  failureThreshold: number;
-  /** Time in milliseconds to wait before attempting recovery */
-  timeout: number;
-  /** Number of consecutive successes in HALF_OPEN state to close the circuit */
-  successThreshold: number;
-}
- 
-/**
- * Default circuit breaker configuration.
- */
-export const DEFAULT_CIRCUIT_BREAKER_CONFIG: CircuitBreakerConfig = {
-  failureThreshold: 5,
-  timeout: 60000, // 1 minute
-  successThreshold: 2,
-};
- 
-/**
- * Circuit breaker for telemetry exporter.
- * Protects against failing telemetry endpoint with automatic recovery.
- *
- * States:
- * - CLOSED: Normal operation, requests pass through
- * - OPEN: After threshold failures, all requests rejected immediately
- * - HALF_OPEN: After timeout, allows test requests to check if endpoint recovered
- */
-export class CircuitBreaker {
-  private state: CircuitBreakerState = CircuitBreakerState.CLOSED;
- 
-  private failureCount = 0;
- 
-  private successCount = 0;
- 
-  private nextAttempt?: Date;
- 
-  private readonly config: CircuitBreakerConfig;
- 
-  constructor(private context: IClientContext, config?: Partial<CircuitBreakerConfig>) {
-    this.config = {
-      ...DEFAULT_CIRCUIT_BREAKER_CONFIG,
-      ...config,
-    };
-  }
- 
-  /**
-   * Executes an operation with circuit breaker protection.
-   *
-   * @param operation The operation to execute
-   * @returns Promise resolving to the operation result
-   * @throws Error if circuit is OPEN or operation fails
-   */
-  async execute<T>(operation: () => Promise<T>): Promise<T> {
-    const logger = this.context.getLogger();
- 
-    // Check if circuit is open
-    if (this.state === CircuitBreakerState.OPEN) {
-      if (this.nextAttempt && Date.now() < this.nextAttempt.getTime()) {
-        throw new Error('Circuit breaker OPEN');
-      }
-      // Timeout expired, transition to HALF_OPEN
-      this.state = CircuitBreakerState.HALF_OPEN;
-      this.successCount = 0;
-      logger.log(LogLevel.debug, 'Circuit breaker transitioned to HALF_OPEN');
-    }
- 
-    try {
-      const result = await operation();
-      this.onSuccess();
-      return result;
-    } catch (error) {
-      this.onFailure();
-      throw error;
-    }
-  }
- 
-  /**
-   * Gets the current state of the circuit breaker.
-   */
-  getState(): CircuitBreakerState {
-    return this.state;
-  }
- 
-  /**
-   * Gets the current failure count.
-   */
-  getFailureCount(): number {
-    return this.failureCount;
-  }
- 
-  /**
-   * Gets the current success count (relevant in HALF_OPEN state).
-   */
-  getSuccessCount(): number {
-    return this.successCount;
-  }
- 
-  /**
-   * Handles successful operation execution.
-   */
-  private onSuccess(): void {
-    const logger = this.context.getLogger();
- 
-    // Reset failure count on any success
-    this.failureCount = 0;
- 
-    if (this.state === CircuitBreakerState.HALF_OPEN) {
-      this.successCount += 1;
-      logger.log(
-        LogLevel.debug,
-        `Circuit breaker success in HALF_OPEN (${this.successCount}/${this.config.successThreshold})`,
-      );
- 
-      if (this.successCount >= this.config.successThreshold) {
-        // Transition to CLOSED
-        this.state = CircuitBreakerState.CLOSED;
-        this.successCount = 0;
-        this.nextAttempt = undefined;
-        logger.log(LogLevel.debug, 'Circuit breaker transitioned to CLOSED');
-      }
-    }
-  }
- 
-  /**
-   * Handles failed operation execution.
-   */
-  private onFailure(): void {
-    const logger = this.context.getLogger();
- 
-    this.failureCount += 1;
-    this.successCount = 0; // Reset success count on failure
- 
-    logger.log(LogLevel.debug, `Circuit breaker failure (${this.failureCount}/${this.config.failureThreshold})`);
- 
-    // In HALF_OPEN state, any failure immediately reopens the circuit.
-    // In CLOSED state, reopen only after failureThreshold consecutive failures.
-    if (this.state === CircuitBreakerState.HALF_OPEN || this.failureCount >= this.config.failureThreshold) {
-      this.state = CircuitBreakerState.OPEN;
-      this.nextAttempt = new Date(Date.now() + this.config.timeout);
-      logger.log(LogLevel.debug, `Circuit breaker transitioned to OPEN (will retry after ${this.config.timeout}ms)`);
-    }
-  }
-}
- 
-/**
- * Manages circuit breakers per host.
- * Ensures each host has its own isolated circuit breaker to prevent
- * failures on one host from affecting telemetry to other hosts.
- */
-export class CircuitBreakerRegistry {
-  private breakers: Map<string, CircuitBreaker>;
- 
-  constructor(private context: IClientContext) {
-    this.breakers = new Map();
-  }
- 
-  /**
-   * Gets or creates a circuit breaker for the specified host.
-   *
-   * @param host The host identifier (e.g., "workspace.cloud.databricks.com")
-   * @param config Optional configuration overrides
-   * @returns Circuit breaker for the host
-   */
-  getCircuitBreaker(host: string, config?: Partial<CircuitBreakerConfig>): CircuitBreaker {
-    let breaker = this.breakers.get(host);
-    if (!breaker) {
-      breaker = new CircuitBreaker(this.context, config);
-      this.breakers.set(host, breaker);
-      const logger = this.context.getLogger();
-      logger.log(LogLevel.debug, `Created circuit breaker for host: ${host}`);
-    }
-    return breaker;
-  }
- 
-  /**
-   * Gets all registered circuit breakers.
-   * Useful for testing and diagnostics.
-   */
-  getAllBreakers(): Map<string, CircuitBreaker> {
-    return new Map(this.breakers);
-  }
- 
-  /**
-   * Removes a circuit breaker for the specified host.
-   * Useful for cleanup when a host is no longer in use.
-   *
-   * @param host The host identifier
-   */
-  removeCircuitBreaker(host: string): void {
-    this.breakers.delete(host);
-    const logger = this.context.getLogger();
-    logger.log(LogLevel.debug, `Removed circuit breaker for host: ${host}`);
-  }
- 
-  /**
-   * Clears all circuit breakers.
-   * Useful for testing.
-   */
-  clear(): void {
-    this.breakers.clear();
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html b/coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html deleted file mode 100644 index 77c07e2c..00000000 --- a/coverage/lcov-report/lib/telemetry/DatabricksTelemetryExporter.ts.html +++ /dev/null @@ -1,1171 +0,0 @@ - - - - - - Code coverage report for lib/telemetry/DatabricksTelemetryExporter.ts - - - - - - - - - -
-
-

All files / lib/telemetry DatabricksTelemetryExporter.ts

-
- -
- 88.76% - Statements - 79/89 -
- - -
- 68.96% - Branches - 40/58 -
- - -
- 100% - Functions - 14/14 -
- - -
- 88.37% - Lines - 76/86 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -1x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -14x -14x -14x -  -  -14x -14x -  -  -14x -  -  -  -  -  -  -  -  -16x -1x -  -  -15x -  -15x -15x -13x -  -  -  -9x -2x -  -7x -  -  -  -  -  -  -  -  -  -13x -13x -13x -  -13x -  -  -13x -17x -17x -6x -  -11x -  -  -11x -2x -2x -  -  -  -9x -2x -2x -  -  -  -7x -3x -3x -  -  -  -4x -4x -4x -  -4x -  -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -17x -17x -  -  -17x -17x -  -  -  -  -18x -18x -  -17x -  -  -  -  -  -17x -  -  -  -  -  -  -  -17x -  -  -17x -17x -  -  -17x -  -  -  -  -  -  -  -  -  -  -15x -9x -9x -9x -  -  -6x -  -  -  -  -  -  -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -18x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -18x -  -  -  -  -  -  -18x -  -  -  -  -  -  -17x -1x -  -16x -  -  -  -  -  -  -18x -558x -558x -558x -  -  -  -  -  -  -  -14x -  -14x -  -  -  -  -  -  -  -  -  -4x -4x -  -  -  - 
/**
- * Copyright (c) 2025 Databricks Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-import fetch, { Response } from 'node-fetch';
-import IClientContext from '../contracts/IClientContext';
-import { LogLevel } from '../contracts/IDBSQLLogger';
-import { TelemetryMetric, DEFAULT_TELEMETRY_CONFIG } from './types';
-import { CircuitBreakerRegistry } from './CircuitBreaker';
-import ExceptionClassifier from './ExceptionClassifier';
- 
-/**
- * Databricks telemetry log format for export.
- */
-interface DatabricksTelemetryLog {
-  workspace_id?: string;
-  frontend_log_event_id: string;
-  context: {
-    client_context: {
-      timestamp_millis: number;
-      user_agent: string;
-    };
-  };
-  entry: {
-    sql_driver_log: {
-      session_id?: string;
-      sql_statement_id?: string;
-      system_configuration?: {
-        driver_version?: string;
-        runtime_name?: string;
-        runtime_version?: string;
-        runtime_vendor?: string;
-        os_name?: string;
-        os_version?: string;
-        os_arch?: string;
-        driver_name?: string;
-        client_app_name?: string;
-        locale_name?: string;
-        char_set_encoding?: string;
-        process_name?: string;
-      };
-      driver_connection_params?: any;
-      operation_latency_ms?: number;
-      sql_operation?: {
-        execution_result?: string;
-        chunk_details?: {
-          total_chunks_present?: number;
-          total_chunks_iterated?: number;
-          initial_chunk_latency_millis?: number;
-          slowest_chunk_latency_millis?: number;
-          sum_chunks_download_time_millis?: number;
-        };
-      };
-      error_info?: {
-        error_name: string;
-        stack_trace: string;
-      };
-    };
-  };
-}
- 
-/**
- * Payload format for Databricks telemetry export.
- * Matches JDBC TelemetryRequest format with protoLogs.
- */
-interface DatabricksTelemetryPayload {
-  uploadTime: number;
-  items: string[]; // Always empty - required field
-  protoLogs: string[]; // JSON-stringified DatabricksTelemetryLog objects
-}
- 
-/**
- * Exports telemetry metrics to Databricks telemetry service.
- *
- * Endpoints:
- * - Authenticated: /api/2.0/sql/telemetry-ext
- * - Unauthenticated: /api/2.0/sql/telemetry-unauth
- *
- * Features:
- * - Circuit breaker integration for endpoint protection
- * - Retry logic with exponential backoff for retryable errors
- * - Terminal error detection (no retry on 400, 401, 403, 404)
- * - CRITICAL: export() method NEVER throws - all exceptions swallowed
- * - CRITICAL: All logging at LogLevel.debug ONLY
- */
-export default class DatabricksTelemetryExporter {
-  private circuitBreaker;
- 
-  private readonly userAgent: string;
- 
-  private fetchFn: typeof fetch;
- 
-  constructor(
-    private context: IClientContext,
-    private host: string,
-    private circuitBreakerRegistry: CircuitBreakerRegistry,
-    fetchFunction?: typeof fetch,
-  ) {
-    this.circuitBreaker = circuitBreakerRegistry.getCircuitBreaker(host);
-    this.fetchFn = fetchFunction || fetch;
- 
-    // Get driver version for user agent
-    this.userAgent = `databricks-sql-nodejs/${this.getDriverVersion()}`;
-  }
- 
-  /**
-   * Export metrics to Databricks service. Never throws.
-   *
-   * @param metrics - Array of telemetry metrics to export
-   */
-  async export(metrics: TelemetryMetric[]): Promise<void> {
-    if (!metrics || metrics.length === 0) {
-      return;
-    }
- 
-    const logger = this.context.getLogger();
- 
-    try {
-      await this.circuitBreaker.execute(async () => {
-        await this.exportWithRetry(metrics);
-      });
-    } catch (error: any) {
-      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
-      if (error.message === 'Circuit breaker OPEN') {
-        logger.log(LogLevel.debug, 'Circuit breaker OPEN - dropping telemetry');
-      } else {
-        logger.log(LogLevel.debug, `Telemetry export error: ${error.message}`);
-      }
-    }
-  }
- 
-  /**
-   * Export metrics with retry logic for retryable errors.
-   * Implements exponential backoff with jitter.
-   */
-  private async exportWithRetry(metrics: TelemetryMetric[]): Promise<void> {
-    const config = this.context.getConfig();
-    const logger = this.context.getLogger();
-    const maxRetries = config.telemetryMaxRetries ?? DEFAULT_TELEMETRY_CONFIG.maxRetries;
- 
-    let lastError: Error | null = null;
- 
-    /* eslint-disable no-await-in-loop */
-    for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
-      try {
-        await this.exportInternal(metrics);
-        return; // Success
-      } catch (error: any) {
-        lastError = error;
- 
-        // Check if error is terminal (don't retry)
-        if (ExceptionClassifier.isTerminal(error)) {
-          logger.log(LogLevel.debug, `Terminal error - no retry: ${error.message}`);
-          throw error; // Terminal error, propagate to circuit breaker
-        }
- 
-        // Check if error is retryable
-        if (!ExceptionClassifier.isRetryable(error)) {
-          logger.log(LogLevel.debug, `Non-retryable error: ${error.message}`);
-          throw error; // Not retryable, propagate to circuit breaker
-        }
- 
-        // Last attempt reached
-        if (attempt >= maxRetries) {
-          logger.log(LogLevel.debug, `Max retries reached (${maxRetries}): ${error.message}`);
-          throw error; // Max retries exhausted, propagate to circuit breaker
-        }
- 
-        // Calculate backoff with exponential + jitter (100ms - 1000ms)
-        const baseDelay = Math.min(100 * 2 ** attempt, 1000);
-        const jitter = Math.random() * 100;
-        const delay = baseDelay + jitter;
- 
-        logger.log(
-          LogLevel.debug,
-          `Retrying telemetry export (attempt ${attempt + 1}/${maxRetries}) after ${Math.round(delay)}ms`,
-        );
- 
-        await this.sleep(delay);
-      }
-    }
-    /* eslint-enable no-await-in-loop */
- 
-    // Should not reach here, but just in case
-    if (lastError) {
-      throw lastError;
-    }
-  }
- 
-  /**
-   * Internal export implementation that makes the HTTP call.
-   */
-  private async exportInternal(metrics: TelemetryMetric[]): Promise<void> {
-    const config = this.context.getConfig();
-    const logger = this.context.getLogger();
- 
-    // Determine endpoint based on authentication mode
-    const authenticatedExport = config.telemetryAuthenticatedExport ?? DEFAULT_TELEMETRY_CONFIG.authenticatedExport;
-    const endpoint = authenticatedExport
-      ? this.buildUrl(this.host, '/telemetry-ext')
-      : this.buildUrl(this.host, '/telemetry-unauth');
- 
-    // Format payload - each log is JSON-stringified to match JDBC format
-    const telemetryLogs = metrics.map((m) => this.toTelemetryLog(m));
-    const protoLogs = telemetryLogs.map((log) => JSON.stringify(log));
- 
-    const payload: DatabricksTelemetryPayload = {
-      uploadTime: Date.now(),
-      items: [], // Required but unused
-      protoLogs,
-    };
- 
-    logger.log(
-      LogLevel.debug,
-      `Exporting ${metrics.length} telemetry metrics to ${
-        authenticatedExport ? 'authenticated' : 'unauthenticated'
-      } endpoint`,
-    );
- 
-    // Get authentication headers if using authenticated endpoint
-    const authHeaders = authenticatedExport ? await this.context.getAuthHeaders() : {};
- 
-    // Get agent with proxy settings (same pattern as CloudFetchResultHandler and DBSQLSession)
-    const connectionProvider = await this.context.getConnectionProvider();
-    const agent = await connectionProvider.getAgent();
- 
-    // Make HTTP POST request with authentication and proxy support
-    const response: Response = await this.fetchFn(endpoint, {
-      method: 'POST',
-      headers: {
-        ...authHeaders,
-        'Content-Type': 'application/json',
-        'User-Agent': this.userAgent,
-      },
-      body: JSON.stringify(payload),
-      agent, // Include agent for proxy support
-    });
- 
-    if (!response.ok) {
-      const error: any = new Error(`Telemetry export failed: ${response.status} ${response.statusText}`);
-      error.statusCode = response.status;
-      throw error;
-    }
- 
-    logger.log(LogLevel.debug, `Successfully exported ${metrics.length} telemetry metrics`);
-  }
- 
-  /**
-   * Convert TelemetryMetric to Databricks telemetry log format.
-   */
-  private toTelemetryLog(metric: TelemetryMetric): DatabricksTelemetryLog {
-    const log: DatabricksTelemetryLog = {
-      frontend_log_event_id: this.generateUUID(),
-      context: {
-        client_context: {
-          timestamp_millis: metric.timestamp,
-          user_agent: this.userAgent,
-        },
-      },
-      entry: {
-        sql_driver_log: {
-          session_id: metric.sessionId,
-          sql_statement_id: metric.statementId,
-        },
-      },
-    };
- 
-    // Add metric-specific fields based on proto definition
-    Iif (metric.metricType === 'connection' && metric.driverConfig) {
-      // Map driverConfig to system_configuration (snake_case as per proto)
-      log.entry.sql_driver_log.system_configuration = {
-        driver_version: metric.driverConfig.driverVersion,
-        driver_name: metric.driverConfig.driverName,
-        runtime_name: 'Node.js',
-        runtime_version: metric.driverConfig.nodeVersion,
-        runtime_vendor: metric.driverConfig.runtimeVendor,
-        os_name: metric.driverConfig.platform,
-        os_version: metric.driverConfig.osVersion,
-        os_arch: metric.driverConfig.osArch,
-        locale_name: metric.driverConfig.localeName,
-        char_set_encoding: metric.driverConfig.charSetEncoding,
-        process_name: metric.driverConfig.processName,
-      };
-    } else Iif (metric.metricType === 'statement') {
-      log.entry.sql_driver_log.operation_latency_ms = metric.latencyMs;
- 
-      if (metric.resultFormat || metric.chunkCount) {
-        log.entry.sql_driver_log.sql_operation = {
-          execution_result: metric.resultFormat,
-        };
- 
-        if (metric.chunkCount && metric.chunkCount > 0) {
-          log.entry.sql_driver_log.sql_operation.chunk_details = {
-            total_chunks_present: metric.chunkCount,
-            total_chunks_iterated: metric.chunkCount,
-          };
-        }
-      }
-    } else Iif (metric.metricType === 'error') {
-      log.entry.sql_driver_log.error_info = {
-        error_name: metric.errorName || 'UnknownError',
-        stack_trace: metric.errorMessage || '',
-      };
-    }
- 
-    return log;
-  }
- 
-  /**
-   * Build full URL from host and path, handling protocol correctly.
-   */
-  private buildUrl(host: string, path: string): string {
-    if (host.startsWith('http://') || host.startsWith('https://')) {
-      return `${host}${path}`;
-    }
-    return `https://${host}${path}`;
-  }
- 
-  /**
-   * Generate a UUID v4.
-   */
-  private generateUUID(): string {
-    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
-      const r = (Math.random() * 16) | 0;
-      const v = c === 'x' ? r : (r & 0x3) | 0x8;
-      return v.toString(16);
-    });
-  }
- 
-  /**
-   * Get driver version from package.json.
-   */
-  private getDriverVersion(): string {
-    try {
-      // In production, this would read from package.json
-      return '1.0.0';
-    } catch {
-      return 'unknown';
-    }
-  }
- 
-  /**
-   * Sleep for the specified number of milliseconds.
-   */
-  private sleep(ms: number): Promise<void> {
-    return new Promise((resolve) => {
-      setTimeout(resolve, ms);
-    });
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html b/coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html deleted file mode 100644 index a3d6c1ae..00000000 --- a/coverage/lcov-report/lib/telemetry/ExceptionClassifier.ts.html +++ /dev/null @@ -1,388 +0,0 @@ - - - - - - Code coverage report for lib/telemetry/ExceptionClassifier.ts - - - - - - - - - -
-
-

All files / lib/telemetry ExceptionClassifier.ts

-
- -
- 82.35% - Statements - 14/17 -
- - -
- 86.2% - Branches - 25/29 -
- - -
- 100% - Functions - 2/2 -
- - -
- 82.35% - Lines - 14/17 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -1x -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -11x -  -  -  -  -  -11x -  -11x -  -  -  -  -  -9x -  -  -  -2x -  -  -  -  -  -  -  -  -  -  -  -  -  -9x -  -  -  -  -9x -  -  -  -  -  -9x -  -9x -  -  -  -  -  -  -7x -  -  -  -2x -  -  - 
/**
- * Copyright (c) 2025 Databricks Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-import AuthenticationError from '../errors/AuthenticationError';
-import RetryError from '../errors/RetryError';
- 
-/**
- * Classifies exceptions as terminal (unrecoverable) vs retryable.
- *
- * Terminal exceptions should be flushed immediately to telemetry,
- * while retryable exceptions are buffered until statement completion.
- *
- * This follows the JDBC driver pattern of smart exception flushing
- * to optimize telemetry export efficiency while ensuring critical
- * errors are reported immediately.
- */
-export default class ExceptionClassifier {
-  /**
-   * Determines if an exception is terminal (non-retryable).
-   *
-   * Terminal exceptions indicate unrecoverable failures that should
-   * be reported immediately, such as authentication failures, invalid
-   * requests, or resource not found errors.
-   *
-   * @param error - The error to classify
-   * @returns true if the error is terminal, false otherwise
-   */
-  static isTerminal(error: Error): boolean {
-    // Check for AuthenticationError (terminal)
-    Iif (error instanceof AuthenticationError) {
-      return true;
-    }
- 
-    // Check for HTTP status codes in error properties
-    // Supporting both 'statusCode' and 'status' property names for flexibility
-    const statusCode = (error as any).statusCode ?? (error as any).status;
- 
-    if (typeof statusCode === 'number') {
-      // Terminal HTTP status codes:
-      // 400 - Bad Request (invalid request format)
-      // 401 - Unauthorized (authentication required)
-      // 403 - Forbidden (permission denied)
-      // 404 - Not Found (resource does not exist)
-      return statusCode === 400 || statusCode === 401 || statusCode === 403 || statusCode === 404;
-    }
- 
-    // Default to false for unknown error types
-    return false;
-  }
- 
-  /**
-   * Determines if an exception is retryable.
-   *
-   * Retryable exceptions indicate transient failures that may succeed
-   * on retry, such as rate limiting, server errors, or network timeouts.
-   *
-   * @param error - The error to classify
-   * @returns true if the error is retryable, false otherwise
-   */
-  static isRetryable(error: Error): boolean {
-    // Check for RetryError (explicitly retryable)
-    Iif (error instanceof RetryError) {
-      return true;
-    }
- 
-    // Check for network timeout errors
-    Iif (error.name === 'TimeoutError' || error.message.includes('timeout')) {
-      return true;
-    }
- 
-    // Check for HTTP status codes in error properties
-    // Supporting both 'statusCode' and 'status' property names for flexibility
-    const statusCode = (error as any).statusCode ?? (error as any).status;
- 
-    if (typeof statusCode === 'number') {
-      // Retryable HTTP status codes:
-      // 429 - Too Many Requests (rate limiting)
-      // 500 - Internal Server Error
-      // 502 - Bad Gateway
-      // 503 - Service Unavailable
-      // 504 - Gateway Timeout
-      return statusCode === 429 || statusCode === 500 || statusCode === 502 || statusCode === 503 || statusCode === 504;
-    }
- 
-    // Default to false for unknown error types
-    return false;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html b/coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html deleted file mode 100644 index d123ed82..00000000 --- a/coverage/lcov-report/lib/telemetry/FeatureFlagCache.ts.html +++ /dev/null @@ -1,688 +0,0 @@ - - - - - - Code coverage report for lib/telemetry/FeatureFlagCache.ts - - - - - - - - - -
-
-

All files / lib/telemetry FeatureFlagCache.ts

-
- -
- 0% - Statements - 0/63 -
- - -
- 0% - Branches - 0/33 -
- - -
- 0% - Functions - 0/8 -
- - -
- 0% - Lines - 0/62 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
/**
- * Copyright (c) 2025 Databricks Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-import fetch from 'node-fetch';
-import IClientContext from '../contracts/IClientContext';
-import { LogLevel } from '../contracts/IDBSQLLogger';
-import driverVersion from '../version';
- 
-/**
- * Context holding feature flag state for a specific host.
- */
-export interface FeatureFlagContext {
-  telemetryEnabled?: boolean;
-  lastFetched?: Date;
-  refCount: number;
-  cacheDuration: number; // 15 minutes in ms
-}
- 
-/**
- * Manages feature flag cache per host.
- * Prevents rate limiting by caching feature flag responses.
- * Instance-based, stored in DBSQLClient.
- */
-export default class FeatureFlagCache {
-  private contexts: Map<string, FeatureFlagContext>;
- 
-  private readonly CACHE_DURATION_MS = 15 * 60 * 1000; // 15 minutes
- 
-  private readonly FEATURE_FLAG_NAME = 'databricks.partnerplatform.clientConfigsFeatureFlags.enableTelemetryForNodeJs';
- 
-  constructor(private context: IClientContext) {
-    this.contexts = new Map();
-  }
- 
-  /**
-   * Gets or creates a feature flag context for the host.
-   * Increments reference count.
-   */
-  getOrCreateContext(host: string): FeatureFlagContext {
-    let ctx = this.contexts.get(host);
-    if (!ctx) {
-      ctx = {
-        refCount: 0,
-        cacheDuration: this.CACHE_DURATION_MS,
-      };
-      this.contexts.set(host, ctx);
-    }
-    ctx.refCount += 1;
-    return ctx;
-  }
- 
-  /**
-   * Decrements reference count for the host.
-   * Removes context when ref count reaches zero.
-   */
-  releaseContext(host: string): void {
-    const ctx = this.contexts.get(host);
-    if (ctx) {
-      ctx.refCount -= 1;
-      if (ctx.refCount <= 0) {
-        this.contexts.delete(host);
-      }
-    }
-  }
- 
-  /**
-   * Checks if telemetry is enabled for the host.
-   * Uses cached value if available and not expired.
-   */
-  async isTelemetryEnabled(host: string): Promise<boolean> {
-    const logger = this.context.getLogger();
-    const ctx = this.contexts.get(host);
- 
-    if (!ctx) {
-      return false;
-    }
- 
-    const isExpired = !ctx.lastFetched || Date.now() - ctx.lastFetched.getTime() > ctx.cacheDuration;
- 
-    if (isExpired) {
-      try {
-        // Fetch feature flag from server
-        ctx.telemetryEnabled = await this.fetchFeatureFlag(host);
-        ctx.lastFetched = new Date();
-      } catch (error: any) {
-        // Log at debug level only, never propagate exceptions
-        logger.log(LogLevel.debug, `Error fetching feature flag: ${error.message}`);
-      }
-    }
- 
-    return ctx.telemetryEnabled ?? false;
-  }
- 
-  /**
-   * Fetches feature flag from server using connector-service API.
-   * Calls GET /api/2.0/connector-service/feature-flags/OSS_NODEJS/{version}
-   *
-   * @param host The host to fetch feature flag for
-   * @returns true if feature flag is enabled, false otherwise
-   */
-  private async fetchFeatureFlag(host: string): Promise<boolean> {
-    const logger = this.context.getLogger();
- 
-    try {
-      // Get driver version for endpoint
-      const version = this.getDriverVersion();
- 
-      // Build feature flags endpoint for Node.js driver
-      const endpoint = this.buildUrl(host, `/api/2.0/connector-service/feature-flags/NODEJS/${version}`);
- 
-      // Get authentication headers
-      const authHeaders = await this.context.getAuthHeaders();
- 
-      logger.log(LogLevel.debug, `Fetching feature flags from ${endpoint}`);
- 
-      // Get agent with proxy settings (same pattern as CloudFetchResultHandler and DBSQLSession)
-      const connectionProvider = await this.context.getConnectionProvider();
-      const agent = await connectionProvider.getAgent();
- 
-      // Make HTTP GET request with authentication and proxy support
-      const response = await fetch(endpoint, {
-        method: 'GET',
-        headers: {
-          ...authHeaders,
-          'Content-Type': 'application/json',
-          'User-Agent': `databricks-sql-nodejs/${driverVersion}`,
-        },
-        agent, // Include agent for proxy support
-      });
- 
-      if (!response.ok) {
-        logger.log(LogLevel.debug, `Feature flag fetch failed: ${response.status} ${response.statusText}`);
-        return false;
-      }
- 
-      // Parse response JSON
-      const data: any = await response.json();
- 
-      // Response format: { flags: [{ name: string, value: string }], ttl_seconds?: number }
-      if (data && data.flags && Array.isArray(data.flags)) {
-        // Update cache duration if TTL provided
-        const ctx = this.contexts.get(host);
-        if (ctx && data.ttl_seconds) {
-          ctx.cacheDuration = data.ttl_seconds * 1000; // Convert to milliseconds
-          logger.log(LogLevel.debug, `Updated cache duration to ${data.ttl_seconds} seconds`);
-        }
- 
-        // Look for our specific feature flag
-        const flag = data.flags.find((f: any) => f.name === this.FEATURE_FLAG_NAME);
- 
-        if (flag) {
-          // Parse boolean value (can be string "true"/"false")
-          const value = String(flag.value).toLowerCase();
-          const enabled = value === 'true';
-          logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME}: ${enabled}`);
-          return enabled;
-        }
-      }
- 
-      // Feature flag not found in response, default to false
-      logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME} not found in response`);
-      return false;
-    } catch (error: any) {
-      // Log at debug level only, never propagate exceptions
-      logger.log(LogLevel.debug, `Error fetching feature flag from ${host}: ${error.message}`);
-      return false;
-    }
-  }
- 
-  /**
-   * Build full URL from host and path, handling protocol correctly.
-   */
-  private buildUrl(host: string, path: string): string {
-    if (host.startsWith('http://') || host.startsWith('https://')) {
-      return `${host}${path}`;
-    }
-    return `https://${host}${path}`;
-  }
- 
-  /**
-   * Gets the driver version without -oss suffix for API calls.
-   * Format: "1.12.0" from "1.12.0-oss"
-   */
-  private getDriverVersion(): string {
-    // Remove -oss suffix if present
-    return driverVersion.replace(/-oss$/, '');
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html b/coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html deleted file mode 100644 index 2a11f203..00000000 --- a/coverage/lcov-report/lib/telemetry/MetricsAggregator.ts.html +++ /dev/null @@ -1,1261 +0,0 @@ - - - - - - Code coverage report for lib/telemetry/MetricsAggregator.ts - - - - - - - - - -
-
-

All files / lib/telemetry MetricsAggregator.ts

-
- -
- 88.13% - Statements - 104/118 -
- - -
- 82.19% - Branches - 60/73 -
- - -
- 100% - Functions - 12/12 -
- - -
- 88.03% - Lines - 103/117 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -14x -  -14x -  -14x -  -  -  -  -  -  -  -14x -14x -14x -14x -14x -14x -  -  -14x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -21x -  -21x -  -21x -12x -12x -  -  -  -9x -2x -2x -  -  -  -7x -6x -  -  -  -  -  -  -  -  -  -  -  -12x -  -  -  -  -  -  -  -12x -  -  -  -  -  -  -2x -  -  -2x -2x -  -  -2x -  -2x -  -1x -  -  -1x -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -1x -  -  -  -1x -1x -  -1x -1x -  -  -  -  -  -  -  -6x -  -6x -  -3x -3x -3x -  -  -1x -1x -1x -1x -1x -1x -  -  -2x -2x -2x -2x -  -2x -  -  -  -  -  -  -  -  -  -  -  -7x -  -7x -4x -  -  -  -  -  -  -  -  -  -  -  -7x -  -  -  -  -  -  -  -  -5x -  -5x -5x -5x -1x -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -  -4x -  -  -4x -1x -  -  -  -  -  -  -  -  -1x -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -  -18x -  -  -18x -2x -2x -2x -2x -  -  -  -18x -1x -  -  -  -  -  -  -  -  -  -12x -  -12x -12x -2x -  -  -10x -10x -  -10x -  -  -10x -  -  -  -10x -7x -  -  -  -  -  -  -  -  -  -  -  -21x -  -21x -21x -7x -  -  -21x -  -2x -  -  -  -21x -  -  -  -  -  -  -  -  -  -  -3x -  -3x -  -3x -3x -3x -  -  -  -3x -1x -  -  -  -3x -  -  -  -  -  -  - 
/**
- * Copyright (c) 2025 Databricks Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-import IClientContext from '../contracts/IClientContext';
-import { LogLevel } from '../contracts/IDBSQLLogger';
-import { TelemetryEvent, TelemetryEventType, TelemetryMetric, DEFAULT_TELEMETRY_CONFIG } from './types';
-import DatabricksTelemetryExporter from './DatabricksTelemetryExporter';
-import ExceptionClassifier from './ExceptionClassifier';
- 
-/**
- * Per-statement telemetry details for aggregation
- */
-interface StatementTelemetryDetails {
-  statementId: string;
-  sessionId: string;
-  workspaceId?: string;
-  operationType?: string;
-  startTime: number;
-  executionLatencyMs?: number;
-  resultFormat?: string;
-  chunkCount: number;
-  bytesDownloaded: number;
-  pollCount: number;
-  compressionEnabled?: boolean;
-  errors: TelemetryEvent[];
-}
- 
-/**
- * Aggregates telemetry events by statement_id and manages batching/flushing.
- *
- * Features:
- * - Aggregates events by statement_id
- * - Connection events emitted immediately (no aggregation)
- * - Statement events buffered until completeStatement() called
- * - Terminal exceptions flushed immediately
- * - Retryable exceptions buffered until statement complete
- * - Batch size and periodic timer trigger flushes
- * - CRITICAL: All exceptions swallowed and logged at LogLevel.debug ONLY
- * - CRITICAL: NO console logging
- *
- * Follows JDBC TelemetryCollector.java:29-30 pattern.
- */
-export default class MetricsAggregator {
-  private statementMetrics: Map<string, StatementTelemetryDetails> = new Map();
- 
-  private pendingMetrics: TelemetryMetric[] = [];
- 
-  private flushTimer: NodeJS.Timeout | null = null;
- 
-  private batchSize: number;
- 
-  private flushIntervalMs: number;
- 
-  private maxPendingMetrics: number;
- 
-  constructor(private context: IClientContext, private exporter: DatabricksTelemetryExporter) {
-    try {
-      const config = context.getConfig();
-      this.batchSize = config.telemetryBatchSize ?? DEFAULT_TELEMETRY_CONFIG.batchSize;
-      this.flushIntervalMs = config.telemetryFlushIntervalMs ?? DEFAULT_TELEMETRY_CONFIG.flushIntervalMs;
-      this.maxPendingMetrics = config.telemetryMaxPendingMetrics ?? DEFAULT_TELEMETRY_CONFIG.maxPendingMetrics;
- 
-      // Start periodic flush timer
-      this.startFlushTimer();
-    } catch (error: any) {
-      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
-      const logger = this.context.getLogger();
-      logger.log(LogLevel.debug, `MetricsAggregator constructor error: ${error.message}`);
- 
-      // Initialize with default values
-      this.batchSize = DEFAULT_TELEMETRY_CONFIG.batchSize;
-      this.flushIntervalMs = DEFAULT_TELEMETRY_CONFIG.flushIntervalMs;
-      this.maxPendingMetrics = DEFAULT_TELEMETRY_CONFIG.maxPendingMetrics;
-    }
-  }
- 
-  /**
-   * Process a telemetry event. Never throws.
-   *
-   * @param event - The telemetry event to process
-   */
-  processEvent(event: TelemetryEvent): void {
-    const logger = this.context.getLogger();
- 
-    try {
-      // Connection events are emitted immediately (no aggregation)
-      if (event.eventType === TelemetryEventType.CONNECTION_OPEN) {
-        this.processConnectionEvent(event);
-        return;
-      }
- 
-      // Error events - check if terminal or retryable
-      if (event.eventType === TelemetryEventType.ERROR) {
-        this.processErrorEvent(event);
-        return;
-      }
- 
-      // Statement events - buffer until complete
-      if (event.statementId) {
-        this.processStatementEvent(event);
-      }
-    } catch (error: any) {
-      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
-      logger.log(LogLevel.debug, `MetricsAggregator.processEvent error: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Process connection event (emit immediately)
-   */
-  private processConnectionEvent(event: TelemetryEvent): void {
-    const metric: TelemetryMetric = {
-      metricType: 'connection',
-      timestamp: event.timestamp,
-      sessionId: event.sessionId,
-      workspaceId: event.workspaceId,
-      driverConfig: event.driverConfig,
-    };
- 
-    this.addPendingMetric(metric);
-  }
- 
-  /**
-   * Process error event (terminal errors flushed immediately, retryable buffered)
-   */
-  private processErrorEvent(event: TelemetryEvent): void {
-    const logger = this.context.getLogger();
- 
-    // Create error object for classification
-    const error: any = new Error(event.errorMessage || 'Unknown error');
-    error.name = event.errorName || 'UnknownError';
- 
-    // Check if terminal using isTerminal field or ExceptionClassifier
-    const isTerminal = event.isTerminal ?? ExceptionClassifier.isTerminal(error);
- 
-    if (isTerminal) {
-      // Terminal error - flush immediately
-      logger.log(LogLevel.debug, `Terminal error detected - flushing immediately`);
- 
-      // If associated with a statement, complete and flush it
-      Iif (event.statementId && this.statementMetrics.has(event.statementId)) {
-        const details = this.statementMetrics.get(event.statementId)!;
-        details.errors.push(event);
-        this.completeStatement(event.statementId);
-      } else {
-        // Standalone error - emit immediately
-        const metric: TelemetryMetric = {
-          metricType: 'error',
-          timestamp: event.timestamp,
-          sessionId: event.sessionId,
-          statementId: event.statementId,
-          workspaceId: event.workspaceId,
-          errorName: event.errorName,
-          errorMessage: event.errorMessage,
-        };
-        this.addPendingMetric(metric);
-      }
- 
-      // Flush immediately for terminal errors
-      this.flush();
-    } else Eif (event.statementId) {
-      // Retryable error - buffer until statement complete
-      const details = this.getOrCreateStatementDetails(event);
-      details.errors.push(event);
-    }
-  }
- 
-  /**
-   * Process statement event (buffer until complete)
-   */
-  private processStatementEvent(event: TelemetryEvent): void {
-    const details = this.getOrCreateStatementDetails(event);
- 
-    switch (event.eventType) {
-      case TelemetryEventType.STATEMENT_START:
-        details.operationType = event.operationType;
-        details.startTime = event.timestamp;
-        break;
- 
-      case TelemetryEventType.STATEMENT_COMPLETE:
-        details.executionLatencyMs = event.latencyMs;
-        details.resultFormat = event.resultFormat;
-        details.chunkCount = event.chunkCount ?? 0;
-        details.bytesDownloaded = event.bytesDownloaded ?? 0;
-        details.pollCount = event.pollCount ?? 0;
-        break;
- 
-      case TelemetryEventType.CLOUDFETCH_CHUNK:
-        details.chunkCount += 1;
-        details.bytesDownloaded += event.bytes ?? 0;
-        Eif (event.compressed !== undefined) {
-          details.compressionEnabled = event.compressed;
-        }
-        break;
- 
-      default:
-        // Unknown event type - ignore
-        break;
-    }
-  }
- 
-  /**
-   * Get or create statement details for the given event
-   */
-  private getOrCreateStatementDetails(event: TelemetryEvent): StatementTelemetryDetails {
-    const statementId = event.statementId!;
- 
-    if (!this.statementMetrics.has(statementId)) {
-      this.statementMetrics.set(statementId, {
-        statementId,
-        sessionId: event.sessionId!,
-        workspaceId: event.workspaceId,
-        startTime: event.timestamp,
-        chunkCount: 0,
-        bytesDownloaded: 0,
-        pollCount: 0,
-        errors: [],
-      });
-    }
- 
-    return this.statementMetrics.get(statementId)!;
-  }
- 
-  /**
-   * Complete a statement and prepare it for flushing. Never throws.
-   *
-   * @param statementId - The statement ID to complete
-   */
-  completeStatement(statementId: string): void {
-    const logger = this.context.getLogger();
- 
-    try {
-      const details = this.statementMetrics.get(statementId);
-      if (!details) {
-        return;
-      }
- 
-      // Create statement metric
-      const metric: TelemetryMetric = {
-        metricType: 'statement',
-        timestamp: details.startTime,
-        sessionId: details.sessionId,
-        statementId: details.statementId,
-        workspaceId: details.workspaceId,
-        latencyMs: details.executionLatencyMs,
-        resultFormat: details.resultFormat,
-        chunkCount: details.chunkCount,
-        bytesDownloaded: details.bytesDownloaded,
-        pollCount: details.pollCount,
-      };
- 
-      this.addPendingMetric(metric);
- 
-      // Add buffered error metrics
-      for (const errorEvent of details.errors) {
-        const errorMetric: TelemetryMetric = {
-          metricType: 'error',
-          timestamp: errorEvent.timestamp,
-          sessionId: details.sessionId,
-          statementId: details.statementId,
-          workspaceId: details.workspaceId,
-          errorName: errorEvent.errorName,
-          errorMessage: errorEvent.errorMessage,
-        };
-        this.addPendingMetric(errorMetric);
-      }
- 
-      // Remove from map
-      this.statementMetrics.delete(statementId);
-    } catch (error: any) {
-      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
-      logger.log(LogLevel.debug, `MetricsAggregator.completeStatement error: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Add a metric to pending batch and flush if batch size reached.
-   * Drops oldest metrics if the buffer exceeds maxPendingMetrics to prevent
-   * unbounded growth when the circuit breaker keeps failing exports.
-   */
-  private addPendingMetric(metric: TelemetryMetric): void {
-    this.pendingMetrics.push(metric);
- 
-    // Cap the buffer to avoid unbounded memory growth when exports keep failing
-    if (this.pendingMetrics.length > this.maxPendingMetrics) {
-      const dropped = this.pendingMetrics.length - this.maxPendingMetrics;
-      this.pendingMetrics = this.pendingMetrics.slice(dropped);
-      const logger = this.context.getLogger();
-      logger.log(LogLevel.debug, `Dropped ${dropped} oldest telemetry metrics (buffer full at ${this.maxPendingMetrics})`);
-    }
- 
-    // Check if batch size reached
-    if (this.pendingMetrics.length >= this.batchSize) {
-      this.flush();
-    }
-  }
- 
-  /**
-   * Flush all pending metrics to exporter. Never throws.
-   *
-   * @param resetTimer If true, resets the flush timer after flushing (default: true)
-   */
-  flush(resetTimer: boolean = true): void {
-    const logger = this.context.getLogger();
- 
-    try {
-      if (this.pendingMetrics.length === 0) {
-        return;
-      }
- 
-      const metricsToExport = [...this.pendingMetrics];
-      this.pendingMetrics = [];
- 
-      logger.log(LogLevel.debug, `Flushing ${metricsToExport.length} telemetry metrics`);
- 
-      // Export metrics (exporter.export never throws)
-      this.exporter.export(metricsToExport);
- 
-      // Reset timer to avoid rapid successive flushes (e.g., batch flush at 25s then timer flush at 30s)
-      // This ensures consistent spacing between exports and helps avoid rate limiting
-      if (resetTimer) {
-        this.startFlushTimer();
-      }
-    } catch (error: any) {
-      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
-      logger.log(LogLevel.debug, `MetricsAggregator.flush error: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Start the periodic flush timer
-   */
-  private startFlushTimer(): void {
-    const logger = this.context.getLogger();
- 
-    try {
-      if (this.flushTimer) {
-        clearInterval(this.flushTimer);
-      }
- 
-      this.flushTimer = setInterval(() => {
-        // Don't reset timer when flush is triggered by the timer itself
-        this.flush(false);
-      }, this.flushIntervalMs);
- 
-      // Prevent timer from keeping Node.js process alive
-      this.flushTimer.unref();
-    } catch (error: any) {
-      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
-      logger.log(LogLevel.debug, `MetricsAggregator.startFlushTimer error: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Close the aggregator and flush remaining metrics. Never throws.
-   */
-  close(): void {
-    const logger = this.context.getLogger();
- 
-    try {
-      // Stop flush timer
-      Eif (this.flushTimer) {
-        clearInterval(this.flushTimer);
-        this.flushTimer = null;
-      }
- 
-      // Complete any remaining statements
-      for (const statementId of this.statementMetrics.keys()) {
-        this.completeStatement(statementId);
-      }
- 
-      // Final flush - don't reset timer since we're closing
-      this.flush(false);
-    } catch (error: any) {
-      // CRITICAL: All exceptions swallowed and logged at debug level ONLY
-      logger.log(LogLevel.debug, `MetricsAggregator.close error: ${error.message}`);
-    }
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html b/coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html deleted file mode 100644 index c96d6b44..00000000 --- a/coverage/lcov-report/lib/telemetry/TelemetryEventEmitter.ts.html +++ /dev/null @@ -1,655 +0,0 @@ - - - - - - Code coverage report for lib/telemetry/TelemetryEventEmitter.ts - - - - - - - - - -
-
-

All files / lib/telemetry TelemetryEventEmitter.ts

-
- -
- 86.04% - Statements - 37/43 -
- - -
- 78.57% - Branches - 11/14 -
- - -
- 100% - Functions - 6/6 -
- - -
- 89.47% - Lines - 34/38 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -1x -1x -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -11x -11x -  -  -11x -11x -  -  -  -  -  -  -  -  -4x -  -3x -3x -3x -  -  -  -  -  -  -3x -  -  -2x -  -  -  -  -  -  -  -  -  -2x -  -1x -1x -1x -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -1x -1x -1x -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -3x -  -2x -2x -2x -  -  -  -  -  -  -  -  -2x -  -  -  -  -  -  - 
/**
- * Copyright (c) 2025 Databricks Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-import { EventEmitter } from 'events';
-import IClientContext from '../contracts/IClientContext';
-import { LogLevel } from '../contracts/IDBSQLLogger';
-import { TelemetryEvent, TelemetryEventType, DriverConfiguration } from './types';
- 
-/**
- * EventEmitter for driver telemetry.
- * Emits events at key driver operations.
- *
- * CRITICAL REQUIREMENT: ALL exceptions must be caught and logged at LogLevel.debug ONLY
- * (never warn/error) to avoid customer anxiety. NO console logging allowed - only IDBSQLLogger.
- *
- * All emit methods are wrapped in try-catch blocks that swallow exceptions completely.
- * Event emission respects the telemetryEnabled flag from context config.
- */
-export default class TelemetryEventEmitter extends EventEmitter {
-  private enabled: boolean;
- 
-  constructor(private context: IClientContext) {
-    super();
-    // Check if telemetry is enabled from config
-    // Default to false for safe rollout
-    const config = context.getConfig() as any;
-    this.enabled = config.telemetryEnabled ?? false;
-  }
- 
-  /**
-   * Emit a connection open event.
-   *
-   * @param data Connection event data including sessionId, workspaceId, and driverConfig
-   */
-  emitConnectionOpen(data: { sessionId: string; workspaceId: string; driverConfig: DriverConfiguration }): void {
-    if (!this.enabled) return;
- 
-    const logger = this.context.getLogger();
-    try {
-      const event: TelemetryEvent = {
-        eventType: TelemetryEventType.CONNECTION_OPEN,
-        timestamp: Date.now(),
-        sessionId: data.sessionId,
-        workspaceId: data.workspaceId,
-        driverConfig: data.driverConfig,
-      };
-      this.emit(TelemetryEventType.CONNECTION_OPEN, event);
-    } catch (error: any) {
-      // Swallow all exceptions - log at debug level only
-      logger.log(LogLevel.debug, `Error emitting connection event: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Emit a statement start event.
-   *
-   * @param data Statement start data including statementId, sessionId, and operationType
-   */
-  emitStatementStart(data: { statementId: string; sessionId: string; operationType?: string }): void {
-    if (!this.enabled) return;
- 
-    const logger = this.context.getLogger();
-    try {
-      const event: TelemetryEvent = {
-        eventType: TelemetryEventType.STATEMENT_START,
-        timestamp: Date.now(),
-        statementId: data.statementId,
-        sessionId: data.sessionId,
-        operationType: data.operationType,
-      };
-      this.emit(TelemetryEventType.STATEMENT_START, event);
-    } catch (error: any) {
-      // Swallow all exceptions - log at debug level only
-      logger.log(LogLevel.debug, `Error emitting statement start: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Emit a statement complete event.
-   *
-   * @param data Statement completion data including latency, result format, and metrics
-   */
-  emitStatementComplete(data: {
-    statementId: string;
-    sessionId: string;
-    latencyMs?: number;
-    resultFormat?: string;
-    chunkCount?: number;
-    bytesDownloaded?: number;
-    pollCount?: number;
-  }): void {
-    Iif (!this.enabled) return;
- 
-    const logger = this.context.getLogger();
-    try {
-      const event: TelemetryEvent = {
-        eventType: TelemetryEventType.STATEMENT_COMPLETE,
-        timestamp: Date.now(),
-        statementId: data.statementId,
-        sessionId: data.sessionId,
-        latencyMs: data.latencyMs,
-        resultFormat: data.resultFormat,
-        chunkCount: data.chunkCount,
-        bytesDownloaded: data.bytesDownloaded,
-        pollCount: data.pollCount,
-      };
-      this.emit(TelemetryEventType.STATEMENT_COMPLETE, event);
-    } catch (error: any) {
-      // Swallow all exceptions - log at debug level only
-      logger.log(LogLevel.debug, `Error emitting statement complete: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Emit a CloudFetch chunk download event.
-   *
-   * @param data CloudFetch chunk data including chunk index, latency, bytes, and compression
-   */
-  emitCloudFetchChunk(data: {
-    statementId: string;
-    chunkIndex: number;
-    latencyMs?: number;
-    bytes: number;
-    compressed?: boolean;
-  }): void {
-    Iif (!this.enabled) return;
- 
-    const logger = this.context.getLogger();
-    try {
-      const event: TelemetryEvent = {
-        eventType: TelemetryEventType.CLOUDFETCH_CHUNK,
-        timestamp: Date.now(),
-        statementId: data.statementId,
-        chunkIndex: data.chunkIndex,
-        latencyMs: data.latencyMs,
-        bytes: data.bytes,
-        compressed: data.compressed,
-      };
-      this.emit(TelemetryEventType.CLOUDFETCH_CHUNK, event);
-    } catch (error: any) {
-      // Swallow all exceptions - log at debug level only
-      logger.log(LogLevel.debug, `Error emitting cloudfetch chunk: ${error.message}`);
-    }
-  }
- 
-  /**
-   * Emit an error event.
-   *
-   * @param data Error event data including error details and terminal status
-   */
-  emitError(data: {
-    statementId?: string;
-    sessionId?: string;
-    errorName: string;
-    errorMessage: string;
-    isTerminal: boolean;
-  }): void {
-    if (!this.enabled) return;
- 
-    const logger = this.context.getLogger();
-    try {
-      const event: TelemetryEvent = {
-        eventType: TelemetryEventType.ERROR,
-        timestamp: Date.now(),
-        statementId: data.statementId,
-        sessionId: data.sessionId,
-        errorName: data.errorName,
-        errorMessage: data.errorMessage,
-        isTerminal: data.isTerminal,
-      };
-      this.emit(TelemetryEventType.ERROR, event);
-    } catch (error: any) {
-      // Swallow all exceptions - log at debug level only
-      logger.log(LogLevel.debug, `Error emitting error event: ${error.message}`);
-    }
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/index.html b/coverage/lcov-report/lib/telemetry/index.html deleted file mode 100644 index 7c3d2b8b..00000000 --- a/coverage/lcov-report/lib/telemetry/index.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - Code coverage report for lib/telemetry - - - - - - - - - -
-
-

All files lib/telemetry

-
- -
- 75.93% - Statements - 303/399 -
- - -
- 68.72% - Branches - 156/227 -
- - -
- 85.71% - Functions - 48/56 -
- - -
- 76.09% - Lines - 296/389 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
CircuitBreaker.ts -
-
100%61/61100%18/18100%13/13100%61/61
DatabricksTelemetryExporter.ts -
-
88.76%79/8968.96%40/58100%14/1488.37%76/86
ExceptionClassifier.ts -
-
82.35%14/1786.2%25/29100%2/282.35%14/17
FeatureFlagCache.ts -
-
0%0/630%0/330%0/80%0/62
MetricsAggregator.ts -
-
88.13%104/11882.19%60/73100%12/1288.03%103/117
TelemetryEventEmitter.ts -
-
86.04%37/4378.57%11/14100%6/689.47%34/38
types.ts -
-
100%8/8100%2/2100%1/1100%8/8
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/telemetry/types.ts.html b/coverage/lcov-report/lib/telemetry/types.ts.html deleted file mode 100644 index bc0805c1..00000000 --- a/coverage/lcov-report/lib/telemetry/types.ts.html +++ /dev/null @@ -1,916 +0,0 @@ - - - - - - Code coverage report for lib/telemetry/types.ts - - - - - - - - - -
-
-

All files / lib/telemetry types.ts

-
- -
- 100% - Statements - 8/8 -
- - -
- 100% - Branches - 2/2 -
- - -
- 100% - Functions - 1/1 -
- - -
- 100% - Lines - 8/8 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -1x -1x -1x -1x -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
/**
- * Copyright (c) 2025 Databricks Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-/**
- * Driver name constant for telemetry
- */
-export const DRIVER_NAME = 'nodejs-sql-driver';
- 
-/**
- * Event types emitted by the telemetry system
- */
-export enum TelemetryEventType {
-  CONNECTION_OPEN = 'connection.open',
-  STATEMENT_START = 'statement.start',
-  STATEMENT_COMPLETE = 'statement.complete',
-  CLOUDFETCH_CHUNK = 'cloudfetch.chunk',
-  ERROR = 'error',
-}
- 
-/**
- * Configuration for telemetry components
- */
-export interface TelemetryConfiguration {
-  /** Whether telemetry is enabled */
-  enabled?: boolean;
- 
-  /** Maximum number of metrics to batch before flushing */
-  batchSize?: number;
- 
-  /** Interval in milliseconds to flush metrics */
-  flushIntervalMs?: number;
- 
-  /** Maximum retry attempts for export */
-  maxRetries?: number;
- 
-  /** Whether to use authenticated export endpoint */
-  authenticatedExport?: boolean;
- 
-  /** Circuit breaker failure threshold */
-  circuitBreakerThreshold?: number;
- 
-  /** Circuit breaker timeout in milliseconds */
-  circuitBreakerTimeout?: number;
- 
-  /** Maximum number of pending metrics buffered before dropping oldest (prevents unbounded growth when export keeps failing) */
-  maxPendingMetrics?: number;
-}
- 
-/**
- * Default telemetry configuration values
- */
-export const DEFAULT_TELEMETRY_CONFIG: Required<TelemetryConfiguration> = {
-  enabled: false, // Initially disabled for safe rollout
-  batchSize: 100,
-  flushIntervalMs: 5000,
-  maxRetries: 3,
-  authenticatedExport: true,
-  circuitBreakerThreshold: 5,
-  circuitBreakerTimeout: 60000, // 1 minute
-  maxPendingMetrics: 500,
-};
- 
-/**
- * Runtime telemetry event emitted by the driver
- */
-export interface TelemetryEvent {
-  /** Type of the event */
-  eventType: TelemetryEventType | string;
- 
-  /** Timestamp when the event occurred (milliseconds since epoch) */
-  timestamp: number;
- 
-  /** Session ID for correlation */
-  sessionId?: string;
- 
-  /** Statement ID for correlation */
-  statementId?: string;
- 
-  // Connection-specific fields
-  /** Workspace ID */
-  workspaceId?: string;
- 
-  /** Driver configuration */
-  driverConfig?: DriverConfiguration;
- 
-  // Statement-specific fields
-  /** Type of operation (SELECT, INSERT, etc.) */
-  operationType?: string;
- 
-  /** Execution latency in milliseconds */
-  latencyMs?: number;
- 
-  /** Result format (inline, cloudfetch, arrow) */
-  resultFormat?: string;
- 
-  /** Number of result chunks */
-  chunkCount?: number;
- 
-  /** Total bytes downloaded */
-  bytesDownloaded?: number;
- 
-  /** Number of poll operations */
-  pollCount?: number;
- 
-  // CloudFetch-specific fields
-  /** Chunk index in the result set */
-  chunkIndex?: number;
- 
-  /** Number of bytes in this chunk */
-  bytes?: number;
- 
-  /** Whether compression was used */
-  compressed?: boolean;
- 
-  // Error-specific fields
-  /** Error name/type */
-  errorName?: string;
- 
-  /** Error message */
-  errorMessage?: string;
- 
-  /** Whether the error is terminal (non-retryable) */
-  isTerminal?: boolean;
-}
- 
-/**
- * Aggregated telemetry metric for export to Databricks
- */
-export interface TelemetryMetric {
-  /** Type of metric */
-  metricType: 'connection' | 'statement' | 'error';
- 
-  /** Timestamp when the metric was created (milliseconds since epoch) */
-  timestamp: number;
- 
-  /** Session ID for correlation */
-  sessionId?: string;
- 
-  /** Statement ID for correlation */
-  statementId?: string;
- 
-  /** Workspace ID */
-  workspaceId?: string;
- 
-  /** Driver configuration (for connection metrics) */
-  driverConfig?: DriverConfiguration;
- 
-  /** Execution latency in milliseconds */
-  latencyMs?: number;
- 
-  /** Result format (inline, cloudfetch, arrow) */
-  resultFormat?: string;
- 
-  /** Number of result chunks */
-  chunkCount?: number;
- 
-  /** Total bytes downloaded */
-  bytesDownloaded?: number;
- 
-  /** Number of poll operations */
-  pollCount?: number;
- 
-  /** Error name/type */
-  errorName?: string;
- 
-  /** Error message */
-  errorMessage?: string;
-}
- 
-/**
- * Driver configuration metadata collected once per connection
- */
-export interface DriverConfiguration {
-  /** Driver version */
-  driverVersion: string;
- 
-  /** Driver name */
-  driverName: string;
- 
-  /** Node.js version */
-  nodeVersion: string;
- 
-  /** Platform (linux, darwin, win32) */
-  platform: string;
- 
-  /** OS version */
-  osVersion: string;
- 
-  /** OS architecture (x64, arm64, etc.) */
-  osArch: string;
- 
-  /** Runtime vendor (Node.js Foundation) */
-  runtimeVendor: string;
- 
-  /** Locale name (e.g., en_US) */
-  localeName: string;
- 
-  /** Character set encoding (e.g., UTF-8) */
-  charSetEncoding: string;
- 
-  /** Process name */
-  processName: string;
- 
-  // Feature flags
-  /** Whether CloudFetch is enabled */
-  cloudFetchEnabled: boolean;
- 
-  /** Whether LZ4 compression is enabled */
-  lz4Enabled: boolean;
- 
-  /** Whether Arrow format is enabled */
-  arrowEnabled: boolean;
- 
-  /** Whether direct results are enabled */
-  directResultsEnabled: boolean;
- 
-  // Configuration values
-  /** Socket timeout in milliseconds */
-  socketTimeout: number;
- 
-  /** Maximum retry attempts */
-  retryMaxAttempts: number;
- 
-  /** Number of concurrent CloudFetch downloads */
-  cloudFetchConcurrentDownloads: number;
-}
- 
-/**
- * Per-statement metrics aggregated from multiple events
- */
-export interface StatementMetrics {
-  /** Statement ID */
-  statementId: string;
- 
-  /** Session ID */
-  sessionId: string;
- 
-  /** Type of operation */
-  operationType?: string;
- 
-  /** Start timestamp (milliseconds since epoch) */
-  startTime: number;
- 
-  /** Total execution latency in milliseconds */
-  executionLatencyMs?: number;
- 
-  /** Number of poll operations */
-  pollCount: number;
- 
-  /** Total poll latency in milliseconds */
-  pollLatencyMs: number;
- 
-  /** Result format (inline, cloudfetch, arrow) */
-  resultFormat?: string;
- 
-  /** Number of CloudFetch chunks downloaded */
-  chunkCount: number;
- 
-  /** Total bytes downloaded */
-  totalBytesDownloaded: number;
- 
-  /** Whether compression was used */
-  compressionEnabled?: boolean;
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/CloseableCollection.ts.html b/coverage/lcov-report/lib/utils/CloseableCollection.ts.html deleted file mode 100644 index 85bf1fa7..00000000 --- a/coverage/lcov-report/lib/utils/CloseableCollection.ts.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - Code coverage report for lib/utils/CloseableCollection.ts - - - - - - - - - -
-
-

All files / lib/utils CloseableCollection.ts

-
- -
- 7.69% - Statements - 1/13 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/5 -
- - -
- 7.69% - Lines - 1/13 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
export interface ICloseable {
-  onClose?: () => void;
- 
-  close(): Promise<unknown>;
-}
- 
-export default class CloseableCollection<T extends ICloseable> {
-  private items = new Set<T>();
- 
-  public add(item: T) {
-    item.onClose = () => {
-      this.delete(item);
-    };
-    this.items.add(item);
-  }
- 
-  public delete(item: T) {
-    if (this.items.has(item)) {
-      item.onClose = undefined;
-    }
-    this.items.delete(item);
-  }
- 
-  public async closeAll() {
-    const items = [...this.items];
-    for (const item of items) {
-      await item.close(); // eslint-disable-line no-await-in-loop
-      item.onClose = undefined;
-      this.items.delete(item);
-    }
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/OperationIterator.ts.html b/coverage/lcov-report/lib/utils/OperationIterator.ts.html deleted file mode 100644 index 55a147a2..00000000 --- a/coverage/lcov-report/lib/utils/OperationIterator.ts.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - Code coverage report for lib/utils/OperationIterator.ts - - - - - - - - - -
-
-

All files / lib/utils OperationIterator.ts

-
- -
- 6.66% - Statements - 2/30 -
- - -
- 0% - Branches - 0/20 -
- - -
- 0% - Functions - 0/7 -
- - -
- 6.66% - Lines - 2/30 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import IOperation, { IOperationChunksIterator, IOperationRowsIterator, IteratorOptions } from '../contracts/IOperation';
- 
-abstract class OperationIterator<R> implements AsyncIterableIterator<R> {
-  public readonly operation: IOperation;
- 
-  protected readonly options?: IteratorOptions;
- 
-  constructor(operation: IOperation, options?: IteratorOptions) {
-    this.operation = operation;
-    this.options = options;
-  }
- 
-  protected abstract getNext(): Promise<IteratorResult<R>>;
- 
-  public [Symbol.asyncIterator]() {
-    return this;
-  }
- 
-  public async next() {
-    const result = await this.getNext();
- 
-    if (result.done && this.options?.autoClose) {
-      await this.operation.close();
-    }
- 
-    return result;
-  }
- 
-  // This method is intended for a cleanup when the caller does not intend to make any more
-  // reads from iterator (e.g. when using `break` in a `for ... of` loop)
-  public async return(value?: any) {
-    if (this.options?.autoClose) {
-      await this.operation.close();
-    }
- 
-    return { done: true, value };
-  }
-}
- 
-export class OperationChunksIterator extends OperationIterator<Array<object>> implements IOperationChunksIterator {
-  protected async getNext(): Promise<IteratorResult<Array<object>>> {
-    const hasMoreRows = await this.operation.hasMoreRows();
-    if (hasMoreRows) {
-      const value = await this.operation.fetchChunk(this.options);
-      return { done: false, value };
-    }
- 
-    return { done: true, value: undefined };
-  }
-}
- 
-export class OperationRowsIterator extends OperationIterator<object> implements IOperationRowsIterator {
-  private chunk: Array<object> = [];
- 
-  private index: number = 0;
- 
-  constructor(operation: IOperation, options?: IteratorOptions) {
-    super(operation, {
-      ...options,
-      // Tell slicer to return raw chunks. We're going to process rows one by one anyway,
-      // so no need to additionally buffer and slice chunks returned by server
-      disableBuffering: true,
-    });
-  }
- 
-  protected async getNext(): Promise<IteratorResult<object>> {
-    if (this.index < this.chunk.length) {
-      const value = this.chunk[this.index];
-      this.index += 1;
-      return { done: false, value };
-    }
- 
-    const hasMoreRows = await this.operation.hasMoreRows();
-    if (hasMoreRows) {
-      this.chunk = await this.operation.fetchChunk(this.options);
-      this.index = 0;
-      // Note: this call is not really a recursion. Since this method is
-      // async - the call will be actually scheduled for processing on
-      // the next event loop cycle
-      return this.getNext();
-    }
- 
-    return { done: true, value: undefined };
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/buildUserAgentString.ts.html b/coverage/lcov-report/lib/utils/buildUserAgentString.ts.html deleted file mode 100644 index dd0474ef..00000000 --- a/coverage/lcov-report/lib/utils/buildUserAgentString.ts.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - Code coverage report for lib/utils/buildUserAgentString.ts - - - - - - - - - -
-
-

All files / lib/utils buildUserAgentString.ts

-
- -
- 26.66% - Statements - 4/15 -
- - -
- 0% - Branches - 0/4 -
- - -
- 0% - Functions - 0/4 -
- - -
- 26.66% - Lines - 4/15 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -321x -1x -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  - 
import os from 'os';
-import packageVersion from '../version';
- 
-const productName = 'NodejsDatabricksSqlConnector';
- 
-function getNodeVersion(): string {
-  return `Node.js ${process.versions.node}`;
-}
- 
-function getOperatingSystemVersion(): string {
-  return `${os.type()} ${os.release()}`;
-}
- 
-function redactInternalToken(userAgentEntry: string): string {
-  const internalTokenPrefixes = ['dkea', 'dskea', 'dapi', 'dsapi', 'dose'];
-  for (const prefix of internalTokenPrefixes) {
-    if (userAgentEntry.startsWith(prefix)) {
-      return '<REDACTED>';
-    }
-  }
-  return userAgentEntry;
-}
- 
-export default function buildUserAgentString(userAgentEntry?: string): string {
-  if (userAgentEntry) {
-    userAgentEntry = redactInternalToken(userAgentEntry);
-  }
- 
-  const extra = [userAgentEntry, getNodeVersion(), getOperatingSystemVersion()].filter(Boolean);
-  return `${productName}/${packageVersion} (${extra.join('; ')})`;
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/definedOrError.ts.html b/coverage/lcov-report/lib/utils/definedOrError.ts.html deleted file mode 100644 index fd231774..00000000 --- a/coverage/lcov-report/lib/utils/definedOrError.ts.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - Code coverage report for lib/utils/definedOrError.ts - - - - - - - - - -
-
-

All files / lib/utils definedOrError.ts

-
- -
- 25% - Statements - 1/4 -
- - -
- 0% - Branches - 0/2 -
- - -
- 0% - Functions - 0/1 -
- - -
- 25% - Lines - 1/4 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -71x -  -  -  -  -  - 
export default function definedOrError<T>(value: T | undefined): T {
-  if (value === undefined) {
-    throw new TypeError('Value is undefined');
-  }
-  return value;
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/formatProgress.ts.html b/coverage/lcov-report/lib/utils/formatProgress.ts.html deleted file mode 100644 index d9754f4f..00000000 --- a/coverage/lcov-report/lib/utils/formatProgress.ts.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - Code coverage report for lib/utils/formatProgress.ts - - - - - - - - - -
-
-

All files / lib/utils formatProgress.ts

-
- -
- 16.66% - Statements - 2/12 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/6 -
- - -
- 20% - Lines - 2/10 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  - 
import { TProgressUpdateResp } from '../../thrift/TCLIService_types';
- 
-export class ProgressUpdateTransformer {
-  private progressUpdate: TProgressUpdateResp;
- 
-  private rowWidth: number = 10;
- 
-  constructor(progressUpdate: TProgressUpdateResp) {
-    this.progressUpdate = progressUpdate;
-  }
- 
-  formatRow(row: Array<string>): string {
-    return row.map((cell) => cell.padEnd(this.rowWidth, ' ')).join('|');
-  }
- 
-  toString() {
-    const header = this.formatRow(this.progressUpdate.headerNames);
-    const footer = this.progressUpdate.footerSummary;
-    const rows = this.progressUpdate.rows.map((row: Array<string>) => this.formatRow(row));
- 
-    return [header, ...rows, footer].join('\n');
-  }
-}
- 
-export default function formatProgress(progressUpdate: TProgressUpdateResp): string {
-  return String(new ProgressUpdateTransformer(progressUpdate));
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/index.html b/coverage/lcov-report/lib/utils/index.html deleted file mode 100644 index 9dc2afec..00000000 --- a/coverage/lcov-report/lib/utils/index.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - Code coverage report for lib/utils - - - - - - - - - -
-
-

All files lib/utils

-
- -
- 25.83% - Statements - 31/120 -
- - -
- 0% - Branches - 0/48 -
- - -
- 0% - Functions - 0/34 -
- - -
- 23% - Lines - 26/113 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
CloseableCollection.ts -
-
7.69%1/130%0/20%0/57.69%1/13
OperationIterator.ts -
-
6.66%2/300%0/200%0/76.66%2/30
buildUserAgentString.ts -
-
26.66%4/150%0/40%0/426.66%4/15
definedOrError.ts -
-
25%1/40%0/20%0/125%1/4
formatProgress.ts -
-
16.66%2/12100%0/00%0/620%2/10
index.ts -
-
100%11/11100%0/00%0/1100%6/6
lz4.ts -
-
6.25%1/160%0/160%0/26.25%1/16
protocolVersion.ts -
-
47.36%9/190%0/40%0/847.36%9/19
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/index.ts.html b/coverage/lcov-report/lib/utils/index.ts.html deleted file mode 100644 index e0e80f29..00000000 --- a/coverage/lcov-report/lib/utils/index.ts.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - Code coverage report for lib/utils/index.ts - - - - - - - - - -
-
-

All files / lib/utils index.ts

-
- -
- 100% - Statements - 11/11 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 100% - Lines - 6/6 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -81x -1x -1x -1x -1x -  -1x - 
import definedOrError from './definedOrError';
-import buildUserAgentString from './buildUserAgentString';
-import formatProgress, { ProgressUpdateTransformer } from './formatProgress';
-import LZ4 from './lz4';
-import * as ProtocolVersion from './protocolVersion';
- 
-export { definedOrError, buildUserAgentString, formatProgress, ProgressUpdateTransformer, LZ4, ProtocolVersion };
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/lz4.ts.html b/coverage/lcov-report/lib/utils/lz4.ts.html deleted file mode 100644 index 5cf41703..00000000 --- a/coverage/lcov-report/lib/utils/lz4.ts.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - Code coverage report for lib/utils/lz4.ts - - - - - - - - - -
-
-

All files / lib/utils lz4.ts

-
- -
- 6.25% - Statements - 1/16 -
- - -
- 0% - Branches - 0/16 -
- - -
- 0% - Functions - 0/2 -
- - -
- 6.25% - Lines - 1/16 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x - 
import type LZ4Namespace from 'lz4';
- 
-type LZ4Module = typeof LZ4Namespace;
- 
-function tryLoadLZ4Module(): LZ4Module | undefined {
-  try {
-    return require('lz4'); // eslint-disable-line global-require
-  } catch (err) {
-    if (!(err instanceof Error) || !('code' in err)) {
-      // eslint-disable-next-line no-console
-      console.warn('Unexpected error loading LZ4 module: Invalid error object', err);
-      return undefined;
-    }
- 
-    if (err.code === 'MODULE_NOT_FOUND') {
-      return undefined;
-    }
- 
-    if (err.code === 'ERR_DLOPEN_FAILED') {
-      // eslint-disable-next-line no-console
-      console.warn('LZ4 native module failed to load: Architecture or version mismatch', err);
-      return undefined;
-    }
- 
-    // If it's not a known error, return undefined
-    // eslint-disable-next-line no-console
-    console.warn('Unknown error loading LZ4 module: Unhandled error code', err);
-    return undefined;
-  }
-}
- 
-// The null is already tried resolving that failed
-let resolvedModule: LZ4Module | null | undefined;
- 
-function getResolvedModule() {
-  if (resolvedModule === undefined) {
-    resolvedModule = tryLoadLZ4Module() ?? null;
-  }
-  return resolvedModule === null ? undefined : resolvedModule;
-}
- 
-export default getResolvedModule;
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/utils/protocolVersion.ts.html b/coverage/lcov-report/lib/utils/protocolVersion.ts.html deleted file mode 100644 index fc11d771..00000000 --- a/coverage/lcov-report/lib/utils/protocolVersion.ts.html +++ /dev/null @@ -1,370 +0,0 @@ - - - - - - Code coverage report for lib/utils/protocolVersion.ts - - - - - - - - - -
-
-

All files / lib/utils protocolVersion.ts

-
- -
- 47.36% - Statements - 9/19 -
- - -
- 0% - Branches - 0/4 -
- - -
- 0% - Functions - 0/8 -
- - -
- 47.36% - Lines - 9/19 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -961x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -1x -  -  -  -  -  -  -  -  -  -1x -  -  - 
import { TProtocolVersion } from '../../thrift/TCLIService_types';
- 
-/**
- * Protocol version information from Thrift TCLIService
- * Each version adds certain features to the Spark/Hive API
- *
- * Databricks only supports SPARK_CLI_SERVICE_PROTOCOL_V1 (0xA501) or higher
- */
- 
-/**
- * Check if the current protocol version supports a specific feature
- * @param serverProtocolVersion The protocol version received from server in TOpenSessionResp
- * @param requiredVersion The minimum protocol version required for a feature
- * @returns boolean indicating if the feature is supported
- */
-export function isFeatureSupported(
-  serverProtocolVersion: TProtocolVersion | undefined | null,
-  requiredVersion: TProtocolVersion,
-): boolean {
-  if (serverProtocolVersion === undefined || serverProtocolVersion === null) {
-    return false;
-  }
- 
-  return serverProtocolVersion >= requiredVersion;
-}
- 
-/**
- * Check if parameterized queries are supported
- * (Requires SPARK_CLI_SERVICE_PROTOCOL_V8 or higher)
- * @param serverProtocolVersion The protocol version from server
- * @returns boolean indicating if parameterized queries are supported
- */
-export function supportsParameterizedQueries(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
-  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V8);
-}
- 
-/**
- * Check if async metadata operations are supported
- * (Requires SPARK_CLI_SERVICE_PROTOCOL_V6 or higher)
- * @param serverProtocolVersion The protocol version from server
- * @returns boolean indicating if async metadata operations are supported
- */
-export function supportsAsyncMetadataOperations(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
-  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V6);
-}
- 
-/**
- * Check if result persistence mode is supported
- * (Requires SPARK_CLI_SERVICE_PROTOCOL_V7 or higher)
- * @param serverProtocolVersion The protocol version from server
- * @returns boolean indicating if result persistence mode is supported
- */
-export function supportsResultPersistenceMode(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
-  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V7);
-}
- 
-/**
- * Check if Arrow compression is supported
- * (Requires SPARK_CLI_SERVICE_PROTOCOL_V6 or higher)
- * @param serverProtocolVersion The protocol version from server
- * @returns boolean indicating if compressed Arrow batches are supported
- */
-export function supportsArrowCompression(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
-  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V6);
-}
- 
-/**
- * Check if Arrow metadata is supported
- * (Requires SPARK_CLI_SERVICE_PROTOCOL_V5 or higher)
- * @param serverProtocolVersion The protocol version from server
- * @returns boolean indicating if Arrow metadata is supported
- */
-export function supportsArrowMetadata(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
-  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V5);
-}
- 
-/**
- * Check if multiple catalogs are supported
- * (Requires SPARK_CLI_SERVICE_PROTOCOL_V4 or higher)
- * @param serverProtocolVersion The protocol version from server
- * @returns boolean indicating if multiple catalogs are supported
- */
-export function supportsMultipleCatalogs(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
-  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V4);
-}
- 
-/**
- * Check if cloud object storage fetching is supported
- * (Requires SPARK_CLI_SERVICE_PROTOCOL_V3 or higher)
- * @param serverProtocolVersion The protocol version from server
- * @returns boolean indicating if cloud fetching is supported
- */
-export function supportsCloudFetch(serverProtocolVersion: TProtocolVersion | undefined | null): boolean {
-  return isFeatureSupported(serverProtocolVersion, TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V3);
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/lib/version.ts.html b/coverage/lcov-report/lib/version.ts.html deleted file mode 100644 index fcb6f96d..00000000 --- a/coverage/lcov-report/lib/version.ts.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - Code coverage report for lib/version.ts - - - - - - - - - -
-
-

All files / lib version.ts

-
- -
- 100% - Statements - 1/1 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 0/0 -
- - -
- 100% - Lines - 1/1 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -21x - 
export default '1.10.0';
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/prettify.css b/coverage/lcov-report/prettify.css deleted file mode 100644 index b317a7cd..00000000 --- a/coverage/lcov-report/prettify.css +++ /dev/null @@ -1 +0,0 @@ -.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/coverage/lcov-report/prettify.js b/coverage/lcov-report/prettify.js deleted file mode 100644 index b3225238..00000000 --- a/coverage/lcov-report/prettify.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable */ -window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/coverage/lcov-report/sort-arrow-sprite.png b/coverage/lcov-report/sort-arrow-sprite.png deleted file mode 100644 index 6ed68316eb3f65dec9063332d2f69bf3093bbfab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc diff --git a/coverage/lcov-report/sorter.js b/coverage/lcov-report/sorter.js deleted file mode 100644 index 2bb296a8..00000000 --- a/coverage/lcov-report/sorter.js +++ /dev/null @@ -1,196 +0,0 @@ -/* eslint-disable */ -var addSorting = (function() { - 'use strict'; - var cols, - currentSort = { - index: 0, - desc: false - }; - - // returns the summary table element - function getTable() { - return document.querySelector('.coverage-summary'); - } - // returns the thead element of the summary table - function getTableHeader() { - return getTable().querySelector('thead tr'); - } - // returns the tbody element of the summary table - function getTableBody() { - return getTable().querySelector('tbody'); - } - // returns the th element for nth column - function getNthColumn(n) { - return getTableHeader().querySelectorAll('th')[n]; - } - - function onFilterInput() { - const searchValue = document.getElementById('fileSearch').value; - const rows = document.getElementsByTagName('tbody')[0].children; - for (let i = 0; i < rows.length; i++) { - const row = rows[i]; - if ( - row.textContent - .toLowerCase() - .includes(searchValue.toLowerCase()) - ) { - row.style.display = ''; - } else { - row.style.display = 'none'; - } - } - } - - // loads the search box - function addSearchBox() { - var template = document.getElementById('filterTemplate'); - var templateClone = template.content.cloneNode(true); - templateClone.getElementById('fileSearch').oninput = onFilterInput; - template.parentElement.appendChild(templateClone); - } - - // loads all columns - function loadColumns() { - var colNodes = getTableHeader().querySelectorAll('th'), - colNode, - cols = [], - col, - i; - - for (i = 0; i < colNodes.length; i += 1) { - colNode = colNodes[i]; - col = { - key: colNode.getAttribute('data-col'), - sortable: !colNode.getAttribute('data-nosort'), - type: colNode.getAttribute('data-type') || 'string' - }; - cols.push(col); - if (col.sortable) { - col.defaultDescSort = col.type === 'number'; - colNode.innerHTML = - colNode.innerHTML + ''; - } - } - return cols; - } - // attaches a data attribute to every tr element with an object - // of data values keyed by column name - function loadRowData(tableRow) { - var tableCols = tableRow.querySelectorAll('td'), - colNode, - col, - data = {}, - i, - val; - for (i = 0; i < tableCols.length; i += 1) { - colNode = tableCols[i]; - col = cols[i]; - val = colNode.getAttribute('data-value'); - if (col.type === 'number') { - val = Number(val); - } - data[col.key] = val; - } - return data; - } - // loads all row data - function loadData() { - var rows = getTableBody().querySelectorAll('tr'), - i; - - for (i = 0; i < rows.length; i += 1) { - rows[i].data = loadRowData(rows[i]); - } - } - // sorts the table using the data for the ith column - function sortByIndex(index, desc) { - var key = cols[index].key, - sorter = function(a, b) { - a = a.data[key]; - b = b.data[key]; - return a < b ? -1 : a > b ? 1 : 0; - }, - finalSorter = sorter, - tableBody = document.querySelector('.coverage-summary tbody'), - rowNodes = tableBody.querySelectorAll('tr'), - rows = [], - i; - - if (desc) { - finalSorter = function(a, b) { - return -1 * sorter(a, b); - }; - } - - for (i = 0; i < rowNodes.length; i += 1) { - rows.push(rowNodes[i]); - tableBody.removeChild(rowNodes[i]); - } - - rows.sort(finalSorter); - - for (i = 0; i < rows.length; i += 1) { - tableBody.appendChild(rows[i]); - } - } - // removes sort indicators for current column being sorted - function removeSortIndicators() { - var col = getNthColumn(currentSort.index), - cls = col.className; - - cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); - col.className = cls; - } - // adds sort indicators for current column being sorted - function addSortIndicators() { - getNthColumn(currentSort.index).className += currentSort.desc - ? ' sorted-desc' - : ' sorted'; - } - // adds event listeners for all sorter widgets - function enableUI() { - var i, - el, - ithSorter = function ithSorter(i) { - var col = cols[i]; - - return function() { - var desc = col.defaultDescSort; - - if (currentSort.index === i) { - desc = !currentSort.desc; - } - sortByIndex(i, desc); - removeSortIndicators(); - currentSort.index = i; - currentSort.desc = desc; - addSortIndicators(); - }; - }; - for (i = 0; i < cols.length; i += 1) { - if (cols[i].sortable) { - // add the click event handler on the th so users - // dont have to click on those tiny arrows - el = getNthColumn(i).querySelector('.sorter').parentElement; - if (el.addEventListener) { - el.addEventListener('click', ithSorter(i)); - } else { - el.attachEvent('onclick', ithSorter(i)); - } - } - } - } - // adds sorting functionality to the UI - return function() { - if (!getTable()) { - return; - } - cols = loadColumns(); - loadData(); - addSearchBox(); - addSortIndicators(); - enableUI(); - }; -})(); - -window.addEventListener('load', addSorting); diff --git a/coverage/lcov.info b/coverage/lcov.info deleted file mode 100644 index 6f55ac90..00000000 --- a/coverage/lcov.info +++ /dev/null @@ -1,5240 +0,0 @@ -TN: -SF:lib/DBSQLClient.ts -FN:36,prependSlash -FN:43,getInitialNamespaceOptions -FN:79,(anonymous_9) -FN:86,(anonymous_10) -FN:108,(anonymous_11) -FN:115,(anonymous_12) -FN:129,(anonymous_13) -FN:193,(anonymous_14) -FN:212,(anonymous_15) -FN:225,(anonymous_16) -FN:248,(anonymous_17) -FN:260,(anonymous_18) -FN:265,(anonymous_19) -FN:270,(anonymous_20) -FN:287,(anonymous_21) -FN:313,(anonymous_22) -FN:321,(anonymous_23) -FN:325,(anonymous_24) -FN:329,(anonymous_25) -FN:337,(anonymous_26) -FN:353,(anonymous_27) -FN:357,(anonymous_28) -FNF:22 -FNH:1 -FNDA:0,prependSlash -FNDA:0,getInitialNamespaceOptions -FNDA:0,(anonymous_9) -FNDA:55,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -FNDA:0,(anonymous_27) -FNDA:0,(anonymous_28) -DA:1,1 -DA:2,1 -DA:4,1 -DA:6,1 -DA:7,1 -DA:12,1 -DA:13,1 -DA:16,1 -DA:18,1 -DA:19,1 -DA:20,1 -DA:21,1 -DA:22,1 -DA:23,1 -DA:31,1 -DA:32,1 -DA:33,1 -DA:37,0 -DA:38,0 -DA:40,0 -DA:44,0 -DA:45,0 -DA:48,0 -DA:58,1 -DA:69,0 -DA:75,0 -DA:77,0 -DA:80,0 -DA:81,0 -DA:83,0 -DA:87,55 -DA:109,0 -DA:110,0 -DA:111,0 -DA:112,0 -DA:116,0 -DA:130,0 -DA:131,0 -DA:134,0 -DA:137,0 -DA:143,0 -DA:154,0 -DA:156,0 -DA:166,0 -DA:176,0 -DA:200,0 -DA:203,0 -DA:204,0 -DA:209,0 -DA:213,0 -DA:226,0 -DA:227,0 -DA:228,0 -DA:232,0 -DA:233,0 -DA:238,0 -DA:239,0 -DA:242,0 -DA:244,0 -DA:246,0 -DA:248,0 -DA:251,0 -DA:252,0 -DA:253,0 -DA:260,0 -DA:261,0 -DA:262,0 -DA:265,0 -DA:266,0 -DA:267,0 -DA:270,0 -DA:271,0 -DA:272,0 -DA:275,0 -DA:289,0 -DA:292,0 -DA:293,0 -DA:296,0 -DA:303,0 -DA:304,0 -DA:309,0 -DA:310,0 -DA:314,0 -DA:316,0 -DA:317,0 -DA:318,0 -DA:322,0 -DA:326,0 -DA:330,0 -DA:331,0 -DA:334,0 -DA:338,0 -DA:340,0 -DA:341,0 -DA:342,0 -DA:345,0 -DA:346,0 -DA:347,0 -DA:350,0 -DA:354,0 -DA:358,0 -DA:359,0 -DA:360,0 -DA:362,0 -DA:363,0 -DA:366,0 -LF:106 -LH:19 -BRDA:37,0,0,0 -BRDA:37,0,1,0 -BRDA:37,1,0,0 -BRDA:37,1,1,0 -BRDA:44,2,0,0 -BRDA:44,2,1,0 -BRDA:44,3,0,0 -BRDA:44,3,1,0 -BRDA:80,4,0,0 -BRDA:80,4,1,0 -BRDA:111,5,0,0 -BRDA:111,5,1,0 -BRDA:111,6,0,0 -BRDA:111,6,1,0 -BRDA:111,7,0,0 -BRDA:111,7,1,0 -BRDA:111,8,0,0 -BRDA:111,8,1,0 -BRDA:118,9,0,0 -BRDA:118,9,1,0 -BRDA:130,10,0,0 -BRDA:130,10,1,0 -BRDA:134,11,0,0 -BRDA:134,11,1,0 -BRDA:134,11,2,0 -BRDA:134,11,3,0 -BRDA:134,11,4,0 -BRDA:134,11,5,0 -BRDA:134,11,6,0 -BRDA:144,12,0,0 -BRDA:144,12,1,0 -BRDA:203,13,0,0 -BRDA:203,13,1,0 -BRDA:227,14,0,0 -BRDA:227,14,1,0 -BRDA:232,15,0,0 -BRDA:232,15,1,0 -BRDA:238,16,0,0 -BRDA:238,16,1,0 -BRDA:251,17,0,0 -BRDA:251,17,1,0 -BRDA:287,18,0,0 -BRDA:289,19,0,0 -BRDA:289,19,1,0 -BRDA:292,20,0,0 -BRDA:292,20,1,0 -BRDA:330,21,0,0 -BRDA:330,21,1,0 -BRDA:340,22,0,0 -BRDA:340,22,1,0 -BRDA:345,23,0,0 -BRDA:345,23,1,0 -BRDA:358,24,0,0 -BRDA:358,24,1,0 -BRF:54 -BRH:0 -end_of_record -TN: -SF:lib/DBSQLLogger.ts -FN:12,(anonymous_1) -FN:25,(anonymous_2) -FN:29,(anonymous_3) -FNF:3 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -DA:1,1 -DA:2,1 -DA:4,1 -DA:13,0 -DA:16,0 -DA:19,0 -DA:20,0 -DA:21,0 -DA:26,0 -DA:30,0 -DA:31,0 -DA:32,0 -LF:12 -LH:3 -BRDA:12,0,0,0 -BRDA:12,1,0,0 -BRDA:19,2,0,0 -BRDA:19,2,1,0 -BRDA:31,3,0,0 -BRDA:31,3,1,0 -BRF:6 -BRH:0 -end_of_record -TN: -SF:lib/DBSQLOperation.ts -FN:44,delay -FN:45,(anonymous_8) -FN:46,(anonymous_9) -FN:79,(anonymous_10) -FN:100,(anonymous_11) -FN:104,(anonymous_12) -FN:108,(anonymous_13) -FN:125,(anonymous_14) -FN:139,(anonymous_15) -FN:168,(anonymous_16) -FN:194,(anonymous_17) -FN:220,(anonymous_18) -FN:241,(anonymous_19) -FN:265,(anonymous_20) -FN:286,(anonymous_21) -FN:291,(anonymous_22) -FN:308,(anonymous_23) -FN:322,(anonymous_24) -FN:328,(anonymous_25) -FN:337,(anonymous_26) -FN:393,(anonymous_27) -FN:405,(anonymous_28) -FN:423,(anonymous_29) -FN:463,(anonymous_30) -FNF:24 -FNH:0 -FNDA:0,delay -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -FNDA:0,(anonymous_27) -FNDA:0,(anonymous_28) -FNDA:0,(anonymous_29) -FNDA:0,(anonymous_30) -DA:1,1 -DA:2,1 -DA:13,1 -DA:23,1 -DA:24,1 -DA:25,1 -DA:27,1 -DA:28,1 -DA:29,1 -DA:30,1 -DA:31,1 -DA:32,1 -DA:33,1 -DA:34,1 -DA:35,1 -DA:45,0 -DA:46,0 -DA:47,0 -DA:52,1 -DA:63,0 -DA:65,0 -DA:71,0 -DA:80,0 -DA:81,0 -DA:83,0 -DA:85,0 -DA:86,0 -DA:89,0 -DA:90,0 -DA:96,0 -DA:97,0 -DA:101,0 -DA:105,0 -DA:111,0 -DA:113,0 -DA:114,0 -DA:116,0 -DA:117,0 -DA:119,0 -DA:122,0 -DA:126,0 -DA:127,0 -DA:140,0 -DA:142,0 -DA:149,0 -DA:151,0 -DA:152,0 -DA:154,0 -DA:156,0 -DA:169,0 -DA:171,0 -DA:172,0 -DA:175,0 -DA:177,0 -DA:178,0 -DA:194,0 -DA:195,0 -DA:198,0 -DA:200,0 -DA:204,0 -DA:206,0 -DA:212,0 -DA:221,0 -DA:222,0 -DA:224,0 -DA:225,0 -DA:228,0 -DA:229,0 -DA:234,0 -DA:242,0 -DA:243,0 -DA:246,0 -DA:248,0 -DA:249,0 -DA:252,0 -DA:253,0 -DA:254,0 -DA:257,0 -DA:258,0 -DA:266,0 -DA:267,0 -DA:270,0 -DA:272,0 -DA:274,0 -DA:278,0 -DA:279,0 -DA:280,0 -DA:282,0 -DA:283,0 -DA:287,0 -DA:288,0 -DA:293,0 -DA:294,0 -DA:299,0 -DA:300,0 -DA:304,0 -DA:305,0 -DA:309,0 -DA:311,0 -DA:312,0 -DA:315,0 -DA:317,0 -DA:318,0 -DA:319,0 -DA:323,0 -DA:324,0 -DA:325,0 -DA:329,0 -DA:330,0 -DA:332,0 -DA:333,0 -DA:338,0 -DA:339,0 -DA:342,0 -DA:344,0 -DA:346,0 -DA:348,0 -DA:350,0 -DA:353,0 -DA:358,0 -DA:362,0 -DA:363,0 -DA:367,0 -DA:368,0 -DA:372,0 -DA:373,0 -DA:377,0 -DA:379,0 -DA:382,0 -DA:386,0 -DA:388,0 -DA:395,0 -DA:396,0 -DA:400,0 -DA:401,0 -DA:405,0 -DA:406,0 -DA:407,0 -DA:410,0 -DA:411,0 -DA:412,0 -DA:415,0 -DA:416,0 -DA:419,0 -DA:424,0 -DA:425,0 -DA:427,0 -DA:430,0 -DA:432,0 -DA:433,0 -DA:435,0 -DA:440,0 -DA:442,0 -DA:447,0 -DA:451,0 -DA:452,0 -DA:456,0 -DA:457,0 -DA:460,0 -DA:464,0 -DA:466,0 -DA:468,0 -DA:469,0 -DA:472,0 -DA:478,0 -DA:479,0 -DA:482,0 -LF:167 -LH:16 -BRDA:83,0,0,0 -BRDA:83,0,1,0 -BRDA:83,1,0,0 -BRDA:83,1,1,0 -BRDA:85,2,0,0 -BRDA:85,2,1,0 -BRDA:85,3,0,0 -BRDA:85,3,1,0 -BRDA:85,4,0,0 -BRDA:85,4,1,0 -BRDA:89,5,0,0 -BRDA:89,5,1,0 -BRDA:89,6,0,0 -BRDA:89,6,1,0 -BRDA:93,7,0,0 -BRDA:93,7,1,0 -BRDA:93,8,0,0 -BRDA:93,8,1,0 -BRDA:96,9,0,0 -BRDA:96,9,1,0 -BRDA:96,10,0,0 -BRDA:96,10,1,0 -BRDA:111,11,0,0 -BRDA:111,11,1,0 -BRDA:111,11,2,0 -BRDA:111,12,0,0 -BRDA:111,12,1,0 -BRDA:111,13,0,0 -BRDA:111,13,1,0 -BRDA:111,14,0,0 -BRDA:111,14,1,0 -BRDA:111,15,0,0 -BRDA:111,15,1,0 -BRDA:113,16,0,0 -BRDA:113,16,1,0 -BRDA:113,17,0,0 -BRDA:113,17,1,0 -BRDA:116,18,0,0 -BRDA:116,18,1,0 -BRDA:116,19,0,0 -BRDA:116,19,1,0 -BRDA:119,20,0,0 -BRDA:119,20,1,0 -BRDA:119,21,0,0 -BRDA:119,21,1,0 -BRDA:122,22,0,0 -BRDA:122,22,1,0 -BRDA:122,23,0,0 -BRDA:122,23,1,0 -BRDA:126,24,0,0 -BRDA:126,24,1,0 -BRDA:126,25,0,0 -BRDA:126,25,1,0 -BRDA:126,26,0,0 -BRDA:126,26,1,0 -BRDA:126,27,0,0 -BRDA:126,27,1,0 -BRDA:127,28,0,0 -BRDA:127,28,1,0 -BRDA:171,29,0,0 -BRDA:171,29,1,0 -BRDA:201,30,0,0 -BRDA:201,30,1,0 -BRDA:201,31,0,0 -BRDA:201,31,1,0 -BRDA:201,32,0,0 -BRDA:201,32,1,0 -BRDA:201,33,0,0 -BRDA:201,33,1,0 -BRDA:202,34,0,0 -BRDA:202,34,1,0 -BRDA:202,35,0,0 -BRDA:202,35,1,0 -BRDA:210,36,0,0 -BRDA:210,36,1,0 -BRDA:210,37,0,0 -BRDA:210,37,1,0 -BRDA:210,38,0,0 -BRDA:210,38,1,0 -BRDA:210,39,0,0 -BRDA:210,39,1,0 -BRDA:220,40,0,0 -BRDA:224,41,0,0 -BRDA:224,41,1,0 -BRDA:242,42,0,0 -BRDA:242,42,1,0 -BRDA:242,43,0,0 -BRDA:242,43,1,0 -BRDA:257,44,0,0 -BRDA:257,44,1,0 -BRDA:257,45,0,0 -BRDA:257,45,1,0 -BRDA:266,46,0,0 -BRDA:266,46,1,0 -BRDA:266,47,0,0 -BRDA:266,47,1,0 -BRDA:274,48,0,0 -BRDA:274,48,1,0 -BRDA:274,49,0,0 -BRDA:274,49,1,0 -BRDA:282,50,0,0 -BRDA:282,50,1,0 -BRDA:282,51,0,0 -BRDA:282,51,1,0 -BRDA:293,52,0,0 -BRDA:293,52,1,0 -BRDA:293,53,0,0 -BRDA:293,53,1,0 -BRDA:299,54,0,0 -BRDA:299,54,1,0 -BRDA:311,55,0,0 -BRDA:311,55,1,0 -BRDA:319,56,0,0 -BRDA:319,56,1,0 -BRDA:319,57,0,0 -BRDA:319,57,1,0 -BRDA:329,58,0,0 -BRDA:329,58,1,0 -BRDA:332,59,0,0 -BRDA:332,59,1,0 -BRDA:338,60,0,0 -BRDA:338,60,1,0 -BRDA:346,61,0,0 -BRDA:346,61,1,0 -BRDA:346,62,0,0 -BRDA:346,62,1,0 -BRDA:348,63,0,0 -BRDA:348,63,1,0 -BRDA:348,64,0,0 -BRDA:348,64,1,0 -BRDA:348,65,0,0 -BRDA:348,65,1,0 -BRDA:353,66,0,0 -BRDA:353,66,1,0 -BRDA:353,66,2,0 -BRDA:353,66,3,0 -BRDA:353,66,4,0 -BRDA:353,66,5,0 -BRDA:353,66,6,0 -BRDA:353,66,7,0 -BRDA:353,66,8,0 -BRDA:353,66,9,0 -BRDA:386,67,0,0 -BRDA:386,67,1,0 -BRDA:395,68,0,0 -BRDA:395,68,1,0 -BRDA:400,69,0,0 -BRDA:400,69,1,0 -BRDA:427,70,0,0 -BRDA:427,70,1,0 -BRDA:430,71,0,0 -BRDA:430,71,1,0 -BRDA:430,71,2,0 -BRDA:451,72,0,0 -BRDA:451,72,1,0 -BRDA:456,73,0,0 -BRDA:456,73,1,0 -BRDA:466,74,0,0 -BRDA:466,74,1,0 -BRDA:466,75,0,0 -BRDA:466,75,1,0 -BRDA:468,76,0,0 -BRDA:468,76,1,0 -BRDA:478,77,0,0 -BRDA:478,77,1,0 -BRF:165 -BRH:0 -end_of_record -TN: -SF:lib/DBSQLParameter.ts -FN:6,(anonymous_1) -FN:37,(anonymous_2) -FN:42,(anonymous_3) -FNF:3 -FNH:1 -FNDA:1,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -DA:1,1 -DA:2,1 -DA:6,1 -DA:7,1 -DA:8,1 -DA:9,1 -DA:10,1 -DA:11,1 -DA:12,1 -DA:13,1 -DA:14,1 -DA:15,1 -DA:16,1 -DA:17,1 -DA:18,1 -DA:19,1 -DA:20,1 -DA:32,1 -DA:38,0 -DA:39,0 -DA:44,0 -DA:45,0 -DA:49,0 -DA:50,0 -DA:53,0 -DA:54,0 -DA:63,0 -DA:64,0 -DA:73,0 -DA:74,0 -DA:83,0 -DA:84,0 -DA:93,0 -LF:33 -LH:18 -BRDA:6,0,0,1 -BRDA:6,0,1,1 -BRDA:42,1,0,0 -BRDA:44,2,0,0 -BRDA:44,2,1,0 -BRDA:49,3,0,0 -BRDA:49,3,1,0 -BRDA:49,4,0,0 -BRDA:49,4,1,0 -BRDA:53,5,0,0 -BRDA:53,5,1,0 -BRDA:56,6,0,0 -BRDA:56,6,1,0 -BRDA:56,7,0,0 -BRDA:56,7,1,0 -BRDA:58,8,0,0 -BRDA:58,8,1,0 -BRDA:63,9,0,0 -BRDA:63,9,1,0 -BRDA:66,10,0,0 -BRDA:66,10,1,0 -BRDA:66,11,0,0 -BRDA:66,11,1,0 -BRDA:66,12,0,0 -BRDA:66,12,1,0 -BRDA:73,13,0,0 -BRDA:73,13,1,0 -BRDA:73,14,0,0 -BRDA:73,14,1,0 -BRDA:76,15,0,0 -BRDA:76,15,1,0 -BRDA:76,16,0,0 -BRDA:76,16,1,0 -BRDA:83,17,0,0 -BRDA:83,17,1,0 -BRDA:86,18,0,0 -BRDA:86,18,1,0 -BRDA:86,19,0,0 -BRDA:86,19,1,0 -BRDA:95,20,0,0 -BRDA:95,20,1,0 -BRDA:95,21,0,0 -BRDA:95,21,1,0 -BRF:43 -BRH:2 -end_of_record -TN: -SF:lib/DBSQLSession.ts -FN:52,numberToInt64 -FN:67,getDirectResultsOptions -FN:79,getArrowOptions -FN:106,getQueryParameters -FN:165,(anonymous_11) -FN:169,(anonymous_12) -FN:178,(anonymous_13) -FN:191,(anonymous_14) -FN:212,(anonymous_15) -FN:261,(anonymous_16) -FN:308,(anonymous_17) -FN:330,(anonymous_18) -FN:347,(anonymous_19) -FN:383,(anonymous_20) -FN:403,(anonymous_21) -FN:423,(anonymous_22) -FN:445,(anonymous_23) -FN:469,(anonymous_24) -FN:489,(anonymous_25) -FN:513,(anonymous_26) -FN:530,(anonymous_27) -FN:553,(anonymous_28) -FN:578,(anonymous_29) -FN:601,(anonymous_30) -FN:615,(anonymous_31) -FN:621,(anonymous_32) -FNF:26 -FNH:0 -FNDA:0,numberToInt64 -FNDA:0,getDirectResultsOptions -FNDA:0,getArrowOptions -FNDA:0,getQueryParameters -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -FNDA:0,(anonymous_27) -FNDA:0,(anonymous_28) -FNDA:0,(anonymous_29) -FNDA:0,(anonymous_30) -FNDA:0,(anonymous_31) -FNDA:0,(anonymous_32) -DA:1,1 -DA:2,1 -DA:3,1 -DA:4,1 -DA:5,1 -DA:6,1 -DA:7,1 -DA:8,1 -DA:31,1 -DA:32,1 -DA:33,1 -DA:34,1 -DA:35,1 -DA:36,1 -DA:37,1 -DA:38,1 -DA:39,1 -DA:40,1 -DA:44,1 -DA:52,1 -DA:53,0 -DA:54,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:64,0 -DA:68,0 -DA:69,0 -DA:72,0 -DA:86,0 -DA:88,0 -DA:89,0 -DA:94,0 -DA:110,0 -DA:111,0 -DA:113,0 -DA:114,0 -DA:117,0 -DA:118,0 -DA:121,0 -DA:123,0 -DA:124,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:131,0 -DA:132,0 -DA:133,0 -DA:134,0 -DA:138,0 -DA:147,1 -DA:152,0 -DA:158,0 -DA:166,0 -DA:170,0 -DA:171,0 -DA:173,0 -DA:174,0 -DA:175,0 -DA:179,0 -DA:180,0 -DA:192,0 -DA:193,0 -DA:194,0 -DA:198,0 -DA:199,0 -DA:200,0 -DA:213,0 -DA:214,0 -DA:215,0 -DA:217,0 -DA:226,0 -DA:227,0 -DA:230,0 -DA:231,0 -DA:234,0 -DA:235,0 -DA:238,0 -DA:239,0 -DA:240,0 -DA:249,0 -DA:250,0 -DA:251,0 -DA:252,0 -DA:255,0 -DA:258,0 -DA:268,0 -DA:269,0 -DA:270,0 -DA:272,0 -DA:275,0 -DA:276,0 -DA:278,0 -DA:279,0 -DA:281,0 -DA:282,0 -DA:286,0 -DA:287,0 -DA:291,0 -DA:293,0 -DA:295,0 -DA:296,0 -DA:298,0 -DA:299,0 -DA:301,0 -DA:302,0 -DA:304,0 -DA:313,0 -DA:314,0 -DA:317,0 -DA:318,0 -DA:320,0 -DA:321,0 -DA:322,0 -DA:325,0 -DA:327,0 -DA:331,0 -DA:332,0 -DA:334,0 -DA:342,0 -DA:343,0 -DA:352,0 -DA:353,0 -DA:356,0 -DA:357,0 -DA:359,0 -DA:360,0 -DA:362,0 -DA:372,0 -DA:373,0 -DA:384,0 -DA:385,0 -DA:386,0 -DA:388,0 -DA:393,0 -DA:394,0 -DA:404,0 -DA:405,0 -DA:406,0 -DA:408,0 -DA:413,0 -DA:414,0 -DA:424,0 -DA:425,0 -DA:426,0 -DA:428,0 -DA:435,0 -DA:436,0 -DA:446,0 -DA:447,0 -DA:448,0 -DA:450,0 -DA:459,0 -DA:460,0 -DA:470,0 -DA:471,0 -DA:472,0 -DA:474,0 -DA:479,0 -DA:480,0 -DA:490,0 -DA:491,0 -DA:492,0 -DA:494,0 -DA:503,0 -DA:504,0 -DA:514,0 -DA:515,0 -DA:516,0 -DA:518,0 -DA:526,0 -DA:527,0 -DA:531,0 -DA:532,0 -DA:533,0 -DA:535,0 -DA:543,0 -DA:544,0 -DA:554,0 -DA:555,0 -DA:556,0 -DA:558,0 -DA:569,0 -DA:570,0 -DA:579,0 -DA:580,0 -DA:584,0 -DA:586,0 -DA:587,0 -DA:591,0 -DA:594,0 -DA:595,0 -DA:597,0 -DA:598,0 -DA:602,0 -DA:603,0 -DA:604,0 -DA:610,0 -DA:612,0 -DA:616,0 -DA:617,0 -DA:625,0 -DA:626,0 -DA:627,0 -LF:206 -LH:21 -BRDA:53,0,0,0 -BRDA:53,0,1,0 -BRDA:57,1,0,0 -BRDA:57,1,1,0 -BRDA:68,2,0,0 -BRDA:68,2,1,0 -BRDA:74,3,0,0 -BRDA:74,3,1,0 -BRDA:74,4,0,0 -BRDA:74,4,1,0 -BRDA:86,5,0,0 -BRDA:86,6,0,0 -BRDA:88,7,0,0 -BRDA:88,7,1,0 -BRDA:88,8,0,0 -BRDA:88,8,1,0 -BRDA:110,9,0,0 -BRDA:110,9,1,0 -BRDA:111,10,0,0 -BRDA:111,10,1,0 -BRDA:113,11,0,0 -BRDA:113,11,1,0 -BRDA:113,12,0,0 -BRDA:113,12,1,0 -BRDA:117,13,0,0 -BRDA:117,13,1,0 -BRDA:117,14,0,0 -BRDA:117,14,1,0 -BRDA:123,15,0,0 -BRDA:123,15,1,0 -BRDA:126,16,0,0 -BRDA:126,16,1,0 -BRDA:131,17,0,0 -BRDA:131,17,1,0 -BRDA:133,18,0,0 -BRDA:133,18,1,0 -BRDA:166,19,0,0 -BRDA:166,19,1,0 -BRDA:179,20,0,0 -BRDA:179,20,1,0 -BRDA:179,21,0,0 -BRDA:179,21,1,0 -BRDA:179,22,0,0 -BRDA:179,22,1,0 -BRDA:179,23,0,0 -BRDA:179,23,1,0 -BRDA:180,24,0,0 -BRDA:180,24,1,0 -BRDA:212,25,0,0 -BRDA:220,26,0,0 -BRDA:220,26,1,0 -BRDA:226,27,0,0 -BRDA:226,27,1,0 -BRDA:230,28,0,0 -BRDA:230,28,1,0 -BRDA:231,29,0,0 -BRDA:231,29,1,0 -BRDA:231,30,0,0 -BRDA:231,30,1,0 -BRDA:234,31,0,0 -BRDA:234,31,1,0 -BRDA:234,32,0,0 -BRDA:234,32,1,0 -BRDA:235,33,0,0 -BRDA:235,33,1,0 -BRDA:235,34,0,0 -BRDA:235,34,1,0 -BRDA:235,35,0,0 -BRDA:235,35,1,0 -BRDA:249,36,0,0 -BRDA:249,36,1,0 -BRDA:251,37,0,0 -BRDA:251,37,1,0 -BRDA:252,38,0,0 -BRDA:252,38,1,0 -BRDA:269,39,0,0 -BRDA:269,39,1,0 -BRDA:275,40,0,0 -BRDA:275,40,1,0 -BRDA:281,41,0,0 -BRDA:281,41,1,0 -BRDA:281,42,0,0 -BRDA:281,42,1,0 -BRDA:286,43,0,0 -BRDA:286,43,1,0 -BRDA:293,44,0,0 -BRDA:293,44,1,0 -BRDA:293,44,2,0 -BRDA:293,44,3,0 -BRDA:313,45,0,0 -BRDA:313,45,1,0 -BRDA:321,46,0,0 -BRDA:321,46,1,0 -BRDA:342,47,0,0 -BRDA:342,47,1,0 -BRDA:342,48,0,0 -BRDA:342,48,1,0 -BRDA:352,49,0,0 -BRDA:352,49,1,0 -BRDA:372,50,0,0 -BRDA:372,50,1,0 -BRDA:383,51,0,0 -BRDA:403,52,0,0 -BRDA:423,53,0,0 -BRDA:445,54,0,0 -BRDA:469,55,0,0 -BRDA:489,56,0,0 -BRDA:579,57,0,0 -BRDA:579,57,1,0 -BRDA:594,58,0,0 -BRDA:594,58,1,0 -BRDA:594,59,0,0 -BRDA:594,59,1,0 -BRDA:616,60,0,0 -BRDA:616,60,1,0 -BRF:115 -BRH:0 -end_of_record -TN: -SF:lib/polyfills.ts -FN:8,toIntegerOrInfinity -FN:20,toLength -FN:26,at -FN:48,(anonymous_3) -FNF:4 -FNH:0 -FNDA:0,toIntegerOrInfinity -FNDA:0,toLength -FNDA:0,at -FNDA:0,(anonymous_3) -DA:9,0 -DA:12,0 -DA:13,0 -DA:16,0 -DA:21,0 -DA:22,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:33,0 -DA:48,0 -DA:49,0 -DA:50,0 -LF:15 -LH:0 -BRDA:12,0,0,0 -BRDA:12,0,1,0 -BRDA:13,1,0,0 -BRDA:13,1,1,0 -BRDA:22,2,0,0 -BRDA:22,2,1,0 -BRDA:29,3,0,0 -BRDA:29,3,1,0 -BRDA:30,4,0,0 -BRDA:30,4,1,0 -BRDA:30,5,0,0 -BRDA:30,5,1,0 -BRDA:49,6,0,0 -BRDA:49,6,1,0 -BRF:14 -BRH:0 -end_of_record -TN: -SF:lib/version.ts -FNF:0 -FNH:0 -DA:1,1 -LF:1 -LH:1 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/PlainHttpAuthentication.ts -FN:21,(anonymous_0) -FN:28,(anonymous_1) -FNF:2 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -DA:12,1 -DA:22,0 -DA:23,0 -DA:24,0 -DA:25,0 -DA:29,0 -LF:6 -LH:1 -BRDA:23,0,0,0 -BRDA:23,0,1,0 -BRDA:23,1,0,0 -BRDA:23,1,1,0 -BRDA:23,2,0,0 -BRDA:23,2,1,0 -BRDA:24,3,0,0 -BRDA:24,3,1,0 -BRDA:24,4,0,0 -BRDA:24,4,1,0 -BRDA:24,5,0,0 -BRDA:24,5,1,0 -BRDA:24,6,0,0 -BRDA:24,6,1,0 -BRDA:25,7,0,0 -BRDA:25,7,1,0 -BRDA:25,8,0,0 -BRDA:25,8,1,0 -BRDA:25,9,0,0 -BRDA:25,9,1,0 -BRF:20 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts -FN:20,defaultOpenAuthUrl -FN:39,(anonymous_2) -FN:45,(anonymous_3) -FN:52,(anonymous_4) -FN:91,(anonymous_5) -FN:100,(anonymous_6) -FN:109,(anonymous_7) -FN:110,(anonymous_8) -FN:125,(anonymous_9) -FN:129,(anonymous_10) -FN:136,(anonymous_11) -FN:137,(anonymous_12) -FN:143,(anonymous_13) -FN:150,(anonymous_14) -FN:155,(anonymous_15) -FN:156,(anonymous_16) -FN:162,(anonymous_17) -FN:169,(anonymous_18) -FNF:18 -FNH:0 -FNDA:0,defaultOpenAuthUrl -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -DA:1,1 -DA:2,1 -DA:3,1 -DA:4,1 -DA:5,1 -DA:7,1 -DA:21,0 -DA:30,1 -DA:35,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:58,0 -DA:60,0 -DA:61,0 -DA:65,0 -DA:66,0 -DA:76,0 -DA:77,0 -DA:78,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:83,0 -DA:85,0 -DA:88,0 -DA:92,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:100,0 -DA:101,0 -DA:102,0 -DA:105,0 -DA:109,0 -DA:110,0 -DA:114,0 -DA:115,0 -DA:117,0 -DA:122,0 -DA:126,0 -DA:134,0 -DA:136,0 -DA:137,0 -DA:138,0 -DA:139,0 -DA:142,0 -DA:143,0 -DA:144,0 -DA:145,0 -DA:151,0 -DA:152,0 -DA:155,0 -DA:156,0 -DA:157,0 -DA:158,0 -DA:161,0 -DA:162,0 -DA:163,0 -DA:164,0 -DA:170,0 -DA:172,0 -LF:72 -LH:7 -BRDA:54,0,0,0 -BRDA:54,0,1,0 -BRDA:76,1,0,0 -BRDA:76,1,1,0 -BRDA:76,2,0,0 -BRDA:76,2,1,0 -BRDA:80,3,0,0 -BRDA:80,3,1,0 -BRDA:80,4,0,0 -BRDA:80,4,1,0 -BRDA:81,5,0,0 -BRDA:81,5,1,0 -BRDA:81,6,0,0 -BRDA:81,6,1,0 -BRDA:81,7,0,0 -BRDA:81,7,1,0 -BRDA:114,8,0,0 -BRDA:114,8,1,0 -BRDA:114,9,0,0 -BRDA:114,9,1,0 -BRDA:114,9,2,0 -BRDA:151,10,0,0 -BRDA:151,10,1,0 -BRF:23 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/DatabricksOAuth/OAuthManager.ts -FN:10,(anonymous_1) -FN:26,getDatabricksOIDCUrl -FN:43,(anonymous_3) -FN:58,(anonymous_4) -FN:64,(anonymous_5) -FN:103,(anonymous_6) -FN:123,(anonymous_7) -FN:127,(anonymous_8) -FN:147,(anonymous_9) -FN:173,(anonymous_10) -FN:193,(anonymous_11) -FN:203,(anonymous_12) -FN:208,(anonymous_13) -FN:215,(anonymous_14) -FN:223,(anonymous_15) -FN:230,(anonymous_16) -FN:247,(anonymous_17) -FN:251,(anonymous_18) -FN:255,(anonymous_19) -FN:259,(anonymous_20) -FN:263,(anonymous_21) -FN:279,(anonymous_22) -FN:283,(anonymous_23) -FN:287,(anonymous_24) -FN:291,(anonymous_25) -FN:295,(anonymous_26) -FNF:26 -FNH:1 -FNDA:1,(anonymous_1) -FNDA:0,getDatabricksOIDCUrl -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -DA:2,1 -DA:3,1 -DA:4,1 -DA:5,1 -DA:6,1 -DA:7,1 -DA:10,1 -DA:11,1 -DA:12,1 -DA:27,0 -DA:28,0 -DA:29,0 -DA:32,1 -DA:44,0 -DA:45,0 -DA:61,0 -DA:62,0 -DA:64,0 -DA:68,0 -DA:75,0 -DA:78,0 -DA:82,0 -DA:87,0 -DA:90,0 -DA:91,0 -DA:97,0 -DA:100,0 -DA:104,0 -DA:105,0 -DA:106,0 -DA:107,0 -DA:111,0 -DA:115,0 -DA:116,0 -DA:117,0 -DA:118,0 -DA:120,0 -DA:124,0 -DA:128,0 -DA:129,0 -DA:131,0 -DA:134,0 -DA:135,0 -DA:138,0 -DA:140,0 -DA:142,0 -DA:148,0 -DA:150,0 -DA:156,0 -DA:158,0 -DA:160,0 -DA:167,0 -DA:168,0 -DA:170,0 -DA:174,0 -DA:176,0 -DA:182,0 -DA:187,0 -DA:188,0 -DA:190,0 -DA:194,0 -DA:196,0 -DA:198,0 -DA:205,0 -DA:207,0 -DA:208,0 -DA:209,0 -DA:211,0 -DA:214,0 -DA:215,0 -DA:216,0 -DA:218,0 -DA:221,0 -DA:222,0 -DA:223,0 -DA:224,0 -DA:226,0 -DA:229,0 -DA:230,0 -DA:231,0 -DA:233,0 -DA:237,0 -DA:242,1 -DA:243,1 -DA:245,1 -DA:248,0 -DA:252,0 -DA:256,0 -DA:260,0 -DA:264,0 -DA:266,0 -DA:268,0 -DA:272,1 -DA:273,1 -DA:275,1 -DA:277,1 -DA:280,0 -DA:284,0 -DA:288,0 -DA:292,0 -DA:297,0 -DA:299,0 -DA:301,0 -DA:303,0 -DA:304,0 -DA:306,0 -DA:307,0 -DA:311,0 -DA:312,0 -DA:315,0 -LF:110 -LH:17 -BRDA:10,0,0,1 -BRDA:10,0,1,1 -BRDA:27,1,0,0 -BRDA:27,1,1,0 -BRDA:28,2,0,0 -BRDA:28,2,1,0 -BRDA:68,3,0,0 -BRDA:68,3,1,0 -BRDA:90,4,0,0 -BRDA:90,4,1,0 -BRDA:94,5,0,0 -BRDA:94,5,1,0 -BRDA:104,6,0,0 -BRDA:104,6,1,0 -BRDA:117,7,0,0 -BRDA:117,7,1,0 -BRDA:117,8,0,0 -BRDA:117,8,1,0 -BRDA:124,9,0,0 -BRDA:124,9,1,0 -BRDA:124,10,0,0 -BRDA:124,10,1,0 -BRDA:129,11,0,0 -BRDA:129,11,1,0 -BRDA:138,12,0,0 -BRDA:138,12,1,0 -BRDA:167,13,0,0 -BRDA:167,13,1,0 -BRDA:187,14,0,0 -BRDA:187,14,1,0 -BRDA:194,15,0,0 -BRDA:194,15,1,0 -BRDA:209,16,0,0 -BRDA:209,16,1,0 -BRDA:216,17,0,0 -BRDA:216,17,1,0 -BRDA:221,18,0,0 -BRDA:221,18,1,0 -BRDA:224,19,0,0 -BRDA:224,19,1,0 -BRDA:231,20,0,0 -BRDA:231,20,1,0 -BRDA:256,21,0,0 -BRDA:256,21,1,0 -BRDA:256,22,0,0 -BRDA:256,22,1,0 -BRDA:260,23,0,0 -BRDA:260,23,1,0 -BRDA:260,24,0,0 -BRDA:260,24,1,0 -BRDA:264,25,0,0 -BRDA:264,25,1,0 -BRDA:288,26,0,0 -BRDA:288,26,1,0 -BRDA:288,27,0,0 -BRDA:288,27,1,0 -BRDA:292,28,0,0 -BRDA:292,28,1,0 -BRDA:292,29,0,0 -BRDA:292,29,1,0 -BRDA:297,30,0,0 -BRDA:297,30,1,0 -BRDA:297,31,0,0 -BRDA:297,31,1,0 -BRDA:301,32,0,0 -BRDA:301,32,1,0 -BRDA:311,33,0,0 -BRDA:311,33,1,0 -BRF:68 -BRH:2 -end_of_record -TN: -SF:lib/connection/auth/DatabricksOAuth/OAuthPersistence.ts -FN:9,(anonymous_0) -FN:12,(anonymous_1) -FN:16,(anonymous_2) -FNF:3 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -DA:9,1 -DA:10,0 -DA:13,0 -DA:17,0 -LF:4 -LH:1 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/DatabricksOAuth/OAuthScope.ts -FN:1,(anonymous_0) -FNF:1 -FNH:1 -FNDA:1,(anonymous_0) -DA:1,1 -DA:2,1 -DA:3,1 -DA:4,1 -DA:9,1 -DA:11,1 -LF:6 -LH:6 -BRDA:1,0,0,1 -BRDA:1,0,1,1 -BRF:2 -BRH:2 -end_of_record -TN: -SF:lib/connection/auth/DatabricksOAuth/OAuthToken.ts -FN:12,(anonymous_0) -FN:18,(anonymous_1) -FN:22,(anonymous_2) -FN:26,(anonymous_3) -FN:30,(anonymous_4) -FN:43,(anonymous_5) -FNF:6 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -DA:3,1 -DA:13,0 -DA:14,0 -DA:15,0 -DA:19,0 -DA:23,0 -DA:27,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:40,0 -DA:44,0 -DA:45,0 -LF:14 -LH:1 -BRDA:35,0,0,0 -BRDA:35,0,1,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/DatabricksOAuth/index.ts -FN:8,(anonymous_6) -FN:25,(anonymous_7) -FN:30,(anonymous_8) -FN:49,(anonymous_9) -FNF:4 -FNH:0 -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -DA:3,1 -DA:4,1 -DA:5,1 -DA:8,1 -DA:16,1 -DA:23,0 -DA:26,0 -DA:27,0 -DA:31,0 -DA:33,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:40,0 -DA:41,0 -DA:43,0 -DA:50,0 -DA:51,0 -DA:53,0 -LF:19 -LH:5 -BRDA:33,0,0,0 -BRDA:33,0,1,0 -BRDA:33,1,0,0 -BRDA:33,1,1,0 -BRDA:36,2,0,0 -BRDA:36,2,1,0 -BRDA:37,3,0,0 -BRDA:37,3,1,0 -BRDA:37,4,0,0 -BRDA:37,4,1,0 -BRDA:50,5,0,0 -BRDA:50,5,1,0 -BRF:12 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/CachedTokenProvider.ts -FN:29,(anonymous_0) -FN:39,(anonymous_1) -FN:61,(anonymous_2) -FN:68,(anonymous_3) -FN:77,(anonymous_4) -FN:93,(anonymous_5) -FNF:6 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -DA:8,1 -DA:14,1 -DA:19,0 -DA:21,0 -DA:35,0 -DA:36,0 -DA:41,0 -DA:42,0 -DA:46,0 -DA:47,0 -DA:51,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:57,0 -DA:62,0 -DA:69,0 -DA:79,0 -DA:80,0 -DA:83,0 -DA:84,0 -DA:85,0 -DA:87,0 -DA:94,0 -DA:95,0 -DA:96,0 -LF:26 -LH:2 -BRDA:36,0,0,0 -BRDA:36,0,1,0 -BRDA:36,1,0,0 -BRDA:36,1,1,0 -BRDA:36,2,0,0 -BRDA:36,2,1,0 -BRDA:36,3,0,0 -BRDA:36,3,1,0 -BRDA:41,4,0,0 -BRDA:41,4,1,0 -BRDA:41,5,0,0 -BRDA:41,5,1,0 -BRDA:46,6,0,0 -BRDA:46,6,1,0 -BRDA:79,7,0,0 -BRDA:79,7,1,0 -BRF:16 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/ExternalTokenProvider.ts -FN:27,(anonymous_1) -FN:39,(anonymous_2) -FN:49,(anonymous_3) -FNF:3 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -DA:2,1 -DA:13,1 -DA:34,0 -DA:35,0 -DA:36,0 -DA:40,0 -DA:42,0 -DA:43,0 -DA:46,0 -DA:50,0 -LF:10 -LH:2 -BRDA:35,0,0,0 -BRDA:35,0,1,0 -BRDA:35,1,0,0 -BRDA:35,1,1,0 -BRDA:35,2,0,0 -BRDA:35,2,1,0 -BRDA:35,3,0,0 -BRDA:35,3,1,0 -BRDA:36,4,0,0 -BRDA:36,4,1,0 -BRDA:36,5,0,0 -BRDA:36,5,1,0 -BRDA:36,6,0,0 -BRDA:36,6,1,0 -BRDA:36,7,0,0 -BRDA:36,7,1,0 -BRDA:42,8,0,0 -BRDA:42,8,1,0 -BRF:18 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/FederationProvider.ts -FN:52,(anonymous_1) -FN:81,(anonymous_2) -FN:95,(anonymous_3) -FN:115,(anonymous_4) -FN:124,(anonymous_5) -FN:146,(anonymous_6) -FN:154,(anonymous_7) -FN:157,(anonymous_8) -FN:206,(anonymous_9) -FN:229,(anonymous_10) -FN:240,(anonymous_11) -FN:253,(anonymous_12) -FNF:12 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -DA:1,1 -DA:3,1 -DA:4,1 -DA:9,1 -DA:14,1 -DA:19,1 -DA:24,1 -DA:29,1 -DA:34,1 -DA:39,1 -DA:44,1 -DA:53,0 -DA:54,0 -DA:55,0 -DA:64,1 -DA:89,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:96,0 -DA:99,0 -DA:100,0 -DA:104,0 -DA:105,0 -DA:107,0 -DA:109,0 -DA:111,0 -DA:116,0 -DA:125,0 -DA:128,0 -DA:129,0 -DA:133,0 -DA:134,0 -DA:137,0 -DA:147,0 -DA:155,0 -DA:156,0 -DA:157,0 -DA:159,0 -DA:160,0 -DA:169,0 -DA:170,0 -DA:171,0 -DA:175,0 -DA:178,0 -DA:184,0 -DA:185,0 -DA:190,0 -DA:191,0 -DA:194,0 -DA:199,0 -DA:207,0 -DA:214,0 -DA:215,0 -DA:218,0 -DA:219,0 -DA:221,0 -DA:223,0 -DA:224,0 -DA:228,0 -DA:229,0 -DA:230,0 -DA:233,0 -DA:241,0 -DA:242,0 -DA:244,0 -DA:245,0 -DA:247,0 -DA:254,0 -DA:257,0 -DA:258,0 -DA:262,0 -DA:263,0 -DA:266,0 -LF:74 -LH:12 -BRDA:91,0,0,0 -BRDA:91,0,1,0 -BRDA:91,1,0,0 -BRDA:91,1,1,0 -BRDA:92,2,0,0 -BRDA:92,2,1,0 -BRDA:92,3,0,0 -BRDA:92,3,1,0 -BRDA:92,4,0,0 -BRDA:92,4,1,0 -BRDA:92,5,0,0 -BRDA:92,5,1,0 -BRDA:99,6,0,0 -BRDA:99,6,1,0 -BRDA:107,7,0,0 -BRDA:107,7,1,0 -BRDA:128,8,0,0 -BRDA:128,8,1,0 -BRDA:133,9,0,0 -BRDA:133,9,1,0 -BRDA:169,10,0,0 -BRDA:169,10,1,0 -BRDA:184,11,0,0 -BRDA:184,11,1,0 -BRDA:190,12,0,0 -BRDA:190,12,1,0 -BRDA:195,13,0,0 -BRDA:195,13,1,0 -BRDA:195,14,0,0 -BRDA:195,14,1,0 -BRDA:214,15,0,0 -BRDA:214,15,1,0 -BRDA:221,16,0,0 -BRDA:221,16,1,0 -BRDA:223,17,0,0 -BRDA:223,17,1,0 -BRDA:241,18,0,0 -BRDA:241,18,1,0 -BRDA:244,19,0,0 -BRDA:244,19,1,0 -BRDA:245,20,0,0 -BRDA:245,20,1,0 -BRDA:257,21,0,0 -BRDA:257,21,1,0 -BRDA:262,22,0,0 -BRDA:262,22,1,0 -BRF:46 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/StaticTokenProvider.ts -FN:16,(anonymous_1) -FN:26,(anonymous_2) -FN:36,(anonymous_3) -FN:40,(anonymous_4) -FNF:4 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -DA:2,1 -DA:8,1 -DA:17,0 -DA:27,0 -DA:28,0 -DA:37,0 -DA:41,0 -LF:7 -LH:2 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/Token.ts -FN:43,(anonymous_0) -FN:54,(anonymous_1) -FN:61,(anonymous_2) -FN:68,(anonymous_3) -FN:75,(anonymous_4) -FN:82,(anonymous_5) -FN:90,(anonymous_6) -FN:104,(anonymous_7) -FN:120,(anonymous_8) -FN:148,(anonymous_9) -FNF:10 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -DA:7,1 -DA:32,1 -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:55,0 -DA:62,0 -DA:69,0 -DA:76,0 -DA:83,0 -DA:91,0 -DA:92,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:105,0 -DA:123,0 -DA:124,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:128,0 -DA:129,0 -DA:137,0 -DA:149,0 -LF:27 -LH:2 -BRDA:45,0,0,0 -BRDA:45,0,1,0 -BRDA:45,1,0,0 -BRDA:45,1,1,0 -BRDA:45,2,0,0 -BRDA:45,2,1,0 -BRDA:45,3,0,0 -BRDA:45,3,1,0 -BRDA:46,4,0,0 -BRDA:46,4,1,0 -BRDA:46,5,0,0 -BRDA:46,5,1,0 -BRDA:47,6,0,0 -BRDA:47,6,1,0 -BRDA:47,7,0,0 -BRDA:47,7,1,0 -BRDA:48,8,0,0 -BRDA:48,8,1,0 -BRDA:48,9,0,0 -BRDA:48,9,1,0 -BRDA:91,10,0,0 -BRDA:91,10,1,0 -BRDA:125,11,0,0 -BRDA:125,11,1,0 -BRDA:128,12,0,0 -BRDA:128,12,1,0 -BRDA:138,13,0,0 -BRDA:138,13,1,0 -BRDA:138,14,0,0 -BRDA:138,14,1,0 -BRDA:140,15,0,0 -BRDA:140,15,1,0 -BRDA:140,16,0,0 -BRDA:140,16,1,0 -BRDA:141,17,0,0 -BRDA:141,17,1,0 -BRDA:141,18,0,0 -BRDA:141,18,1,0 -BRDA:152,19,0,0 -BRDA:152,19,1,0 -BRDA:152,20,0,0 -BRDA:152,20,1,0 -BRF:42 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/TokenProviderAuthenticator.ts -FN:24,(anonymous_0) -FN:30,(anonymous_1) -FNF:2 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -DA:5,1 -DA:11,1 -DA:25,0 -DA:26,0 -DA:27,0 -DA:31,0 -DA:32,0 -DA:34,0 -DA:36,0 -DA:38,0 -DA:39,0 -DA:44,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:49,0 -DA:53,0 -LF:17 -LH:2 -BRDA:27,0,0,0 -BRDA:27,0,1,0 -BRDA:27,1,0,0 -BRDA:27,1,1,0 -BRDA:38,2,0,0 -BRDA:38,2,1,0 -BRDA:46,3,0,0 -BRDA:46,3,1,0 -BRF:8 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/index.ts -FN:2,(anonymous_1) -FN:3,(anonymous_2) -FN:4,(anonymous_3) -FN:5,(anonymous_4) -FN:6,(anonymous_5) -FN:7,(anonymous_6) -FN:8,(anonymous_7) -FN:8,(anonymous_8) -FN:8,(anonymous_9) -FNF:9 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -DA:2,1 -DA:3,1 -DA:4,1 -DA:5,1 -DA:6,1 -DA:7,1 -DA:8,1 -LF:7 -LH:7 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/connection/auth/tokenProvider/utils.ts -FN:8,decodeJWT -FN:27,getJWTIssuer -FN:42,extractHostname -FN:67,isSameHost -FNF:4 -FNH:0 -FNDA:0,decodeJWT -FNDA:0,getJWTIssuer -FNDA:0,extractHostname -FNDA:0,isSameHost -DA:8,1 -DA:9,0 -DA:10,0 -DA:11,0 -DA:12,0 -DA:14,0 -DA:15,0 -DA:17,0 -DA:27,1 -DA:28,0 -DA:29,0 -DA:30,0 -DA:32,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:50,0 -DA:51,0 -DA:52,0 -DA:56,0 -DA:67,1 -DA:68,0 -DA:69,0 -DA:70,0 -DA:72,0 -DA:73,0 -DA:75,0 -DA:77,0 -LF:28 -LH:3 -BRDA:11,0,0,0 -BRDA:11,0,1,0 -BRDA:29,1,0,0 -BRDA:29,1,1,0 -BRDA:29,2,0,0 -BRDA:29,2,1,0 -BRDA:44,3,0,0 -BRDA:44,3,1,0 -BRDA:51,4,0,0 -BRDA:51,4,1,0 -BRDA:72,5,0,0 -BRDA:72,5,1,0 -BRDA:72,6,0,0 -BRDA:72,6,1,0 -BRF:14 -BRH:0 -end_of_record -TN: -SF:lib/connection/connections/HttpConnection.ts -FN:26,(anonymous_1) -FN:31,(anonymous_2) -FN:39,(anonymous_3) -FN:51,(anonymous_4) -FN:62,(anonymous_5) -FN:67,(anonymous_6) -FN:79,(anonymous_7) -FN:87,(anonymous_8) -FN:93,(anonymous_9) -FN:106,(anonymous_10) -FN:122,(anonymous_11) -FNF:11 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -DA:1,1 -DA:2,1 -DA:3,1 -DA:5,1 -DA:11,1 -DA:13,1 -DA:15,1 -DA:20,0 -DA:27,0 -DA:28,0 -DA:32,0 -DA:33,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:44,0 -DA:48,0 -DA:52,0 -DA:54,0 -DA:63,0 -DA:64,0 -DA:68,0 -DA:76,0 -DA:80,0 -DA:83,0 -DA:85,0 -DA:87,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:97,0 -DA:99,0 -DA:106,0 -DA:119,0 -DA:123,0 -LF:35 -LH:7 -BRDA:33,0,0,0 -BRDA:33,0,1,0 -BRDA:33,1,0,0 -BRDA:33,1,1,0 -BRDA:40,2,0,0 -BRDA:40,2,1,0 -BRDA:41,3,0,0 -BRDA:41,3,1,0 -BRDA:44,4,0,0 -BRDA:44,4,1,0 -BRDA:58,5,0,0 -BRDA:58,5,1,0 -BRDA:58,6,0,0 -BRDA:58,6,1,0 -BRDA:80,7,0,0 -BRDA:80,7,1,0 -BRDA:80,8,0,0 -BRDA:80,8,1,0 -BRDA:80,9,0,0 -BRDA:80,9,1,0 -BRDA:81,10,0,0 -BRDA:81,10,1,0 -BRDA:81,11,0,0 -BRDA:81,11,1,0 -BRDA:81,12,0,0 -BRDA:81,12,1,0 -BRDA:81,13,0,0 -BRDA:81,13,1,0 -BRDA:94,14,0,0 -BRDA:94,14,1,0 -BRDA:101,15,0,0 -BRDA:101,15,1,0 -BRDA:102,16,0,0 -BRDA:102,16,1,0 -BRDA:102,17,0,0 -BRDA:102,17,1,0 -BRDA:110,18,0,0 -BRDA:110,18,1,0 -BRDA:110,19,0,0 -BRDA:110,19,1,0 -BRF:40 -BRH:0 -end_of_record -TN: -SF:lib/connection/connections/HttpRetryPolicy.ts -FN:6,delay -FN:7,(anonymous_7) -FN:8,(anonymous_8) -FN:19,(anonymous_9) -FN:25,(anonymous_10) -FN:57,(anonymous_11) -FN:68,(anonymous_12) -FN:82,(anonymous_13) -FN:98,(anonymous_14) -FNF:9 -FNH:0 -FNDA:0,delay -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -DA:4,1 -DA:7,0 -DA:8,0 -DA:12,1 -DA:20,0 -DA:21,0 -DA:22,0 -DA:26,0 -DA:27,0 -DA:30,0 -DA:31,0 -DA:32,0 -DA:35,0 -DA:38,0 -DA:39,0 -DA:40,0 -DA:44,0 -DA:45,0 -DA:51,0 -DA:54,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:64,0 -DA:69,0 -DA:73,0 -DA:79,0 -DA:88,0 -DA:89,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:95,0 -DA:99,0 -DA:100,0 -LF:37 -LH:2 -BRDA:26,0,0,0 -BRDA:26,0,1,0 -BRDA:31,1,0,0 -BRDA:31,1,1,0 -BRDA:39,2,0,0 -BRDA:39,2,1,0 -BRDA:47,3,0,0 -BRDA:47,3,1,0 -BRDA:47,4,0,0 -BRDA:47,4,1,0 -BRDA:61,5,0,0 -BRDA:61,5,1,0 -BRDA:73,6,0,0 -BRDA:73,6,1,0 -BRDA:73,6,2,0 -BRDA:73,6,3,0 -BRDA:88,7,0,0 -BRDA:88,7,1,0 -BRDA:89,8,0,0 -BRDA:89,8,1,0 -BRDA:91,9,0,0 -BRDA:91,9,1,0 -BRDA:91,10,0,0 -BRDA:91,10,1,0 -BRF:24 -BRH:0 -end_of_record -TN: -SF:lib/connection/connections/NullRetryPolicy.ts -FN:5,(anonymous_0) -FN:9,(anonymous_1) -FNF:2 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -DA:3,1 -DA:6,0 -DA:11,0 -LF:3 -LH:1 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/connection/connections/ThriftHttpConnection.ts -FN:21,(anonymous_7) -FN:86,(anonymous_8) -FN:95,(anonymous_9) -FN:104,(anonymous_10) -FN:111,(anonymous_11) -FN:125,(anonymous_12) -FN:126,(anonymous_13) -FN:127,(anonymous_14) -FN:129,(anonymous_15) -FN:133,(anonymous_16) -FN:140,(anonymous_17) -FN:141,(anonymous_18) -FN:143,(anonymous_19) -FN:153,(anonymous_20) -FN:167,(anonymous_21) -FN:168,(anonymous_22) -FN:170,(anonymous_23) -FN:184,(anonymous_24) -FN:198,(anonymous_25) -FN:203,(anonymous_26) -FNF:20 -FNH:0 -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -FNDA:0,(anonymous_23) -FNDA:0,(anonymous_24) -FNDA:0,(anonymous_25) -FNDA:0,(anonymous_26) -DA:7,1 -DA:8,1 -DA:9,1 -DA:11,1 -DA:14,1 -DA:16,1 -DA:22,0 -DA:26,0 -DA:27,0 -DA:52,1 -DA:70,1 -DA:87,0 -DA:88,0 -DA:89,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:97,0 -DA:98,0 -DA:101,0 -DA:105,0 -DA:112,0 -DA:124,0 -DA:125,0 -DA:127,0 -DA:128,0 -DA:129,0 -DA:131,0 -DA:134,0 -DA:135,0 -DA:138,0 -DA:141,0 -DA:144,0 -DA:145,0 -DA:146,0 -DA:153,0 -DA:154,0 -DA:157,0 -DA:158,0 -DA:159,0 -DA:160,0 -DA:162,0 -DA:168,0 -DA:169,0 -DA:170,0 -DA:171,0 -DA:172,0 -DA:173,0 -DA:174,0 -DA:177,0 -DA:179,0 -DA:185,0 -DA:186,0 -DA:189,0 -DA:190,0 -DA:191,0 -DA:193,0 -DA:194,0 -DA:195,0 -DA:196,0 -DA:198,0 -DA:199,0 -DA:200,0 -DA:201,0 -DA:202,0 -DA:203,0 -DA:204,0 -DA:209,0 -DA:210,0 -DA:212,0 -DA:213,0 -DA:220,0 -DA:221,0 -DA:223,0 -LF:74 -LH:8 -BRDA:86,0,0,0 -BRDA:91,1,0,0 -BRDA:91,1,1,0 -BRDA:91,2,0,0 -BRDA:91,2,1,0 -BRDA:92,3,0,0 -BRDA:92,3,1,0 -BRDA:92,4,0,0 -BRDA:92,4,1,0 -BRDA:97,5,0,0 -BRDA:97,5,1,0 -BRDA:97,6,0,0 -BRDA:97,6,1,0 -BRDA:134,7,0,0 -BRDA:134,7,1,0 -BRDA:144,8,0,0 -BRDA:144,8,1,0 -BRDA:145,9,0,0 -BRDA:145,9,1,0 -BRDA:157,10,0,0 -BRDA:157,10,1,0 -BRDA:158,11,0,0 -BRDA:158,11,1,0 -BRDA:158,12,0,0 -BRDA:158,12,1,0 -BRDA:185,13,0,0 -BRDA:185,13,1,0 -BRDA:202,14,0,0 -BRDA:202,14,1,0 -BRDA:209,15,0,0 -BRDA:209,15,1,0 -BRDA:220,16,0,0 -BRDA:220,16,1,0 -BRF:33 -BRH:0 -end_of_record -TN: -SF:lib/contracts/IDBSQLLogger.ts -FN:10,(anonymous_0) -FNF:1 -FNH:1 -FNDA:1,(anonymous_0) -DA:10,1 -DA:11,1 -DA:12,1 -DA:13,1 -DA:14,1 -LF:5 -LH:5 -BRDA:10,0,0,1 -BRDA:10,0,1,1 -BRF:2 -BRH:2 -end_of_record -TN: -SF:lib/dto/InfoValue.ts -FN:9,(anonymous_0) -FN:13,(anonymous_1) -FNF:2 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -DA:6,1 -DA:10,0 -DA:14,0 -DA:16,0 -DA:17,0 -DA:19,0 -DA:20,0 -DA:22,0 -DA:23,0 -DA:25,0 -DA:26,0 -DA:28,0 -DA:29,0 -DA:31,0 -LF:14 -LH:1 -BRDA:16,0,0,0 -BRDA:16,0,1,0 -BRDA:19,1,0,0 -BRDA:19,1,1,0 -BRDA:22,2,0,0 -BRDA:22,2,1,0 -BRDA:25,3,0,0 -BRDA:25,3,1,0 -BRDA:28,4,0,0 -BRDA:28,4,1,0 -BRF:10 -BRH:0 -end_of_record -TN: -SF:lib/dto/Status.ts -FN:7,(anonymous_1) -FN:11,(anonymous_2) -FN:16,(anonymous_3) -FN:21,(anonymous_4) -FN:26,(anonymous_5) -FN:30,(anonymous_6) -FN:37,(anonymous_7) -FNF:7 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -DA:1,1 -DA:2,1 -DA:4,1 -DA:8,0 -DA:12,0 -DA:13,0 -DA:17,0 -DA:18,0 -DA:22,0 -DA:23,0 -DA:27,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:38,0 -LF:15 -LH:3 -BRDA:13,0,0,0 -BRDA:13,0,1,0 -BRDA:23,1,0,0 -BRDA:23,1,1,0 -BRDA:27,2,0,0 -BRDA:27,2,1,0 -BRDA:32,3,0,0 -BRDA:32,3,1,0 -BRDA:37,4,0,0 -BRDA:39,5,0,0 -BRDA:39,5,1,0 -BRF:11 -BRH:0 -end_of_record -TN: -SF:lib/errors/AuthenticationError.ts -FNF:0 -FNH:0 -DA:1,1 -DA:3,1 -LF:2 -LH:2 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/errors/HiveDriverError.ts -FNF:0 -FNH:0 -DA:1,1 -LF:1 -LH:1 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/errors/OperationStateError.ts -FN:4,(anonymous_1) -FN:25,(anonymous_2) -FNF:2 -FNH:1 -FNDA:1,(anonymous_1) -FNDA:0,(anonymous_2) -DA:1,1 -DA:4,1 -DA:5,1 -DA:6,1 -DA:7,1 -DA:8,1 -DA:9,1 -DA:12,1 -DA:20,1 -DA:26,0 -DA:28,0 -DA:29,0 -LF:12 -LH:9 -BRDA:4,0,0,1 -BRDA:4,0,1,1 -BRDA:26,1,0,0 -BRDA:26,1,1,0 -BRDA:26,2,0,0 -BRDA:26,2,1,0 -BRDA:26,3,0,0 -BRDA:26,3,1,0 -BRDA:26,4,0,0 -BRDA:26,4,1,0 -BRF:10 -BRH:2 -end_of_record -TN: -SF:lib/errors/ParameterError.ts -FNF:0 -FNH:0 -DA:1,1 -LF:1 -LH:1 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/errors/RetryError.ts -FN:1,(anonymous_0) -FN:16,(anonymous_1) -FNF:2 -FNH:1 -FNDA:1,(anonymous_0) -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:3,1 -DA:6,1 -DA:11,1 -DA:17,0 -DA:18,0 -DA:19,0 -LF:8 -LH:5 -BRDA:1,0,0,1 -BRDA:1,0,1,1 -BRF:2 -BRH:2 -end_of_record -TN: -SF:lib/errors/StagingError.ts -FNF:0 -FNH:0 -DA:1,1 -LF:1 -LH:1 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/errors/StatusError.ts -FN:12,(anonymous_0) -FNF:1 -FNH:0 -FNDA:0,(anonymous_0) -DA:3,1 -DA:13,0 -DA:14,0 -DA:15,0 -DA:17,0 -DA:18,0 -LF:6 -LH:1 -BRDA:14,0,0,0 -BRDA:14,0,1,0 -BRDA:15,1,0,0 -BRDA:15,1,1,0 -BRDA:17,2,0,0 -BRDA:17,2,1,0 -BRF:6 -BRH:0 -end_of_record -TN: -SF:lib/hive/HiveDriver.ts -FN:55,(anonymous_1) -FN:59,(anonymous_2) -FN:65,(anonymous_3) -FN:71,(anonymous_4) -FN:77,(anonymous_5) -FN:83,(anonymous_6) -FN:89,(anonymous_7) -FN:95,(anonymous_8) -FN:101,(anonymous_9) -FN:107,(anonymous_10) -FN:113,(anonymous_11) -FN:119,(anonymous_12) -FN:125,(anonymous_13) -FN:131,(anonymous_14) -FN:137,(anonymous_15) -FN:143,(anonymous_16) -FN:149,(anonymous_17) -FN:155,(anonymous_18) -FN:161,(anonymous_19) -FN:167,(anonymous_20) -FN:173,(anonymous_21) -FN:179,(anonymous_22) -FNF:22 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -FNDA:0,(anonymous_18) -FNDA:0,(anonymous_19) -FNDA:0,(anonymous_20) -FNDA:0,(anonymous_21) -FNDA:0,(anonymous_22) -DA:24,1 -DA:25,1 -DA:26,1 -DA:27,1 -DA:28,1 -DA:29,1 -DA:30,1 -DA:31,1 -DA:32,1 -DA:33,1 -DA:34,1 -DA:35,1 -DA:36,1 -DA:37,1 -DA:38,1 -DA:39,1 -DA:40,1 -DA:41,1 -DA:42,1 -DA:43,1 -DA:44,1 -DA:52,1 -DA:56,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:72,0 -DA:73,0 -DA:74,0 -DA:78,0 -DA:79,0 -DA:80,0 -DA:84,0 -DA:85,0 -DA:86,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:96,0 -DA:97,0 -DA:98,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:108,0 -DA:109,0 -DA:110,0 -DA:114,0 -DA:115,0 -DA:116,0 -DA:120,0 -DA:121,0 -DA:122,0 -DA:126,0 -DA:127,0 -DA:128,0 -DA:132,0 -DA:133,0 -DA:134,0 -DA:138,0 -DA:139,0 -DA:140,0 -DA:144,0 -DA:145,0 -DA:146,0 -DA:150,0 -DA:151,0 -DA:152,0 -DA:156,0 -DA:157,0 -DA:158,0 -DA:162,0 -DA:163,0 -DA:164,0 -DA:168,0 -DA:169,0 -DA:170,0 -DA:174,0 -DA:175,0 -DA:176,0 -DA:180,0 -DA:181,0 -DA:182,0 -LF:86 -LH:22 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/BaseCommand.ts -FN:11,(anonymous_7) -FN:16,(anonymous_8) -FN:49,(anonymous_9) -FN:56,(anonymous_10) -FN:58,(anonymous_11) -FNF:5 -FNH:0 -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -DA:1,1 -DA:2,1 -DA:3,1 -DA:6,1 -DA:12,0 -DA:13,0 -DA:17,0 -DA:18,0 -DA:20,0 -DA:22,0 -DA:28,0 -DA:31,0 -DA:33,0 -DA:37,0 -DA:45,0 -DA:50,0 -DA:51,0 -DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:62,0 -DA:66,0 -LF:24 -LH:4 -BRDA:20,0,0,0 -BRDA:20,0,1,0 -BRDA:22,1,0,0 -BRDA:22,1,1,0 -BRDA:23,2,0,0 -BRDA:23,2,1,0 -BRDA:23,2,2,0 -BRDA:23,2,3,0 -BRDA:31,3,0,0 -BRDA:31,3,1,0 -BRDA:34,4,0,0 -BRDA:34,4,1,0 -BRDA:34,5,0,0 -BRDA:34,5,1,0 -BRDA:38,6,0,0 -BRDA:38,6,1,0 -BRDA:38,7,0,0 -BRDA:38,7,1,0 -BRDA:50,8,0,0 -BRDA:50,8,1,0 -BRDA:59,9,0,0 -BRDA:59,9,1,0 -BRF:22 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/CancelDelegationTokenCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/CancelOperationCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/CloseOperationCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/CloseSessionCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/ExecuteStatementCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/FetchResultsCommand.ts -FN:11,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:10,1 -DA:12,0 -DA:14,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetCatalogsCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetColumnsCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetCrossReferenceCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetDelegationTokenCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetFunctionsCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetInfoCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetOperationStatusCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetPrimaryKeysCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetResultSetMetadataCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetSchemasCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetTableTypesCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetTablesCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/GetTypeInfoCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/OpenSessionCommand.ts -FN:17,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:16,1 -DA:18,0 -DA:20,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/hive/Commands/RenewDelegationTokenCommand.ts -FN:8,(anonymous_1) -FNF:1 -FNH:0 -FNDA:0,(anonymous_1) -DA:1,1 -DA:2,1 -DA:7,1 -DA:9,0 -DA:11,0 -LF:5 -LH:3 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/result/ArrowResultConverter.ts -FN:45,(anonymous_0) -FN:51,(anonymous_1) -FN:61,(anonymous_2) -FN:105,(anonymous_3) -FN:142,(anonymous_4) -FN:143,(anonymous_5) -FN:151,(anonymous_6) -FN:173,(anonymous_7) -FN:185,(anonymous_8) -FN:211,(anonymous_9) -FN:214,(anonymous_10) -FNF:11 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -DA:1,1 -DA:2,1 -DA:19,1 -DA:21,1 -DA:26,1 -DA:36,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:52,0 -DA:53,0 -DA:55,0 -DA:56,0 -DA:58,0 -DA:62,0 -DA:63,0 -DA:74,0 -DA:76,0 -DA:78,0 -DA:79,0 -DA:81,0 -DA:82,0 -DA:87,0 -DA:88,0 -DA:89,0 -DA:93,0 -DA:95,0 -DA:98,0 -DA:108,0 -DA:111,0 -DA:112,0 -DA:113,0 -DA:114,0 -DA:117,0 -DA:118,0 -DA:119,0 -DA:120,0 -DA:121,0 -DA:129,0 -DA:130,0 -DA:131,0 -DA:135,0 -DA:136,0 -DA:143,0 -DA:145,0 -DA:147,0 -DA:152,0 -DA:153,0 -DA:156,0 -DA:157,0 -DA:158,0 -DA:162,0 -DA:163,0 -DA:164,0 -DA:165,0 -DA:166,0 -DA:168,0 -DA:170,0 -DA:171,0 -DA:173,0 -DA:174,0 -DA:175,0 -DA:177,0 -DA:181,0 -DA:182,0 -DA:184,0 -DA:185,0 -DA:188,0 -DA:189,0 -DA:194,0 -DA:195,0 -DA:196,0 -DA:197,0 -DA:199,0 -DA:203,0 -DA:204,0 -DA:208,0 -DA:212,0 -DA:214,0 -DA:215,0 -DA:216,0 -DA:217,0 -DA:218,0 -DA:221,0 -LF:84 -LH:5 -BRDA:52,0,0,0 -BRDA:52,0,1,0 -BRDA:55,1,0,0 -BRDA:55,1,1,0 -BRDA:62,2,0,0 -BRDA:62,2,1,0 -BRDA:76,3,0,0 -BRDA:76,3,1,0 -BRDA:88,4,0,0 -BRDA:88,4,1,0 -BRDA:111,5,0,0 -BRDA:111,5,1,0 -BRDA:113,6,0,0 -BRDA:113,6,1,0 -BRDA:118,7,0,0 -BRDA:118,7,1,0 -BRDA:118,8,0,0 -BRDA:118,8,1,0 -BRDA:129,9,0,0 -BRDA:129,9,1,0 -BRDA:129,10,0,0 -BRDA:129,10,1,0 -BRDA:129,11,0,0 -BRDA:129,11,1,0 -BRDA:129,12,0,0 -BRDA:129,12,1,0 -BRDA:130,13,0,0 -BRDA:130,13,1,0 -BRDA:130,14,0,0 -BRDA:130,14,1,0 -BRDA:135,15,0,0 -BRDA:135,15,1,0 -BRDA:151,16,0,0 -BRDA:152,17,0,0 -BRDA:152,17,1,0 -BRDA:162,18,0,0 -BRDA:162,18,1,0 -BRDA:166,19,0,0 -BRDA:166,19,1,0 -BRDA:166,20,0,0 -BRDA:166,20,1,0 -BRDA:166,21,0,0 -BRDA:166,21,1,0 -BRDA:166,22,0,0 -BRDA:166,22,1,0 -BRDA:166,23,0,0 -BRDA:166,23,1,0 -BRDA:170,24,0,0 -BRDA:170,24,1,0 -BRDA:173,25,0,0 -BRDA:173,25,1,0 -BRDA:173,26,0,0 -BRDA:173,26,1,0 -BRDA:175,27,0,0 -BRDA:175,27,1,0 -BRDA:175,28,0,0 -BRDA:175,28,1,0 -BRDA:175,29,0,0 -BRDA:175,29,1,0 -BRDA:175,30,0,0 -BRDA:175,30,1,0 -BRDA:175,31,0,0 -BRDA:175,31,1,0 -BRDA:181,32,0,0 -BRDA:181,32,1,0 -BRDA:185,33,0,0 -BRDA:185,33,1,0 -BRDA:185,34,0,0 -BRDA:185,34,1,0 -BRDA:185,35,0,0 -BRDA:185,35,1,0 -BRDA:185,36,0,0 -BRDA:185,36,1,0 -BRDA:185,37,0,0 -BRDA:185,37,1,0 -BRDA:188,38,0,0 -BRDA:188,38,1,0 -BRDA:194,39,0,0 -BRDA:194,39,1,0 -BRDA:194,40,0,0 -BRDA:194,40,1,0 -BRDA:196,41,0,0 -BRDA:196,41,1,0 -BRDA:203,42,0,0 -BRDA:203,42,1,0 -BRDA:208,43,0,0 -BRDA:208,43,1,0 -BRDA:215,44,0,0 -BRDA:215,44,1,0 -BRDA:215,45,0,0 -BRDA:215,45,1,0 -BRDA:218,46,0,0 -BRDA:218,46,1,0 -BRF:93 -BRH:0 -end_of_record -TN: -SF:lib/result/ArrowResultHandler.ts -FN:17,(anonymous_1) -FN:34,(anonymous_2) -FN:41,(anonymous_3) -FN:53,(anonymous_4) -FNF:4 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -DA:2,1 -DA:5,1 -DA:6,1 -DA:8,1 -DA:22,0 -DA:23,0 -DA:26,0 -DA:27,0 -DA:29,0 -DA:30,0 -DA:35,0 -DA:36,0 -DA:38,0 -DA:42,0 -DA:43,0 -DA:49,0 -DA:51,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:56,0 -DA:60,0 -DA:61,0 -DA:67,0 -LF:25 -LH:4 -BRDA:26,0,0,0 -BRDA:26,0,1,0 -BRDA:26,1,0,0 -BRDA:26,1,1,0 -BRDA:27,2,0,0 -BRDA:27,2,1,0 -BRDA:27,3,0,0 -BRDA:27,3,1,0 -BRDA:29,4,0,0 -BRDA:29,4,1,0 -BRDA:29,5,0,0 -BRDA:29,5,1,0 -BRDA:35,6,0,0 -BRDA:35,6,1,0 -BRDA:42,7,0,0 -BRDA:42,7,1,0 -BRDA:53,8,0,0 -BRDA:53,8,1,0 -BRDA:53,9,0,0 -BRDA:53,9,1,0 -BRDA:53,10,0,0 -BRDA:53,10,1,0 -BRDA:53,11,0,0 -BRDA:53,11,1,0 -BRDA:54,12,0,0 -BRDA:54,12,1,0 -BRDA:55,13,0,0 -BRDA:55,13,1,0 -BRDA:60,14,0,0 -BRDA:60,14,1,0 -BRF:30 -BRH:0 -end_of_record -TN: -SF:lib/result/CloudFetchResultHandler.ts -FN:21,(anonymous_7) -FN:35,(anonymous_8) -FN:42,(anonymous_9) -FN:45,(anonymous_10) -FN:54,(anonymous_11) -FN:67,(anonymous_12) -FN:72,(anonymous_13) -FN:93,(anonymous_14) -FN:115,(anonymous_15) -FN:121,(anonymous_16) -FN:123,(anonymous_17) -FNF:11 -FNH:0 -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:0,(anonymous_10) -FNDA:0,(anonymous_11) -FNDA:0,(anonymous_12) -FNDA:0,(anonymous_13) -FNDA:0,(anonymous_14) -FNDA:0,(anonymous_15) -FNDA:0,(anonymous_16) -FNDA:0,(anonymous_17) -DA:1,1 -DA:3,1 -DA:7,1 -DA:8,1 -DA:10,1 -DA:17,0 -DA:19,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:30,0 -DA:31,0 -DA:36,0 -DA:37,0 -DA:39,0 -DA:43,0 -DA:45,0 -DA:46,0 -DA:49,0 -DA:50,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:66,0 -DA:67,0 -DA:69,0 -DA:73,0 -DA:74,0 -DA:76,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:94,0 -DA:95,0 -DA:98,0 -DA:99,0 -DA:100,0 -DA:101,0 -DA:104,0 -DA:105,0 -DA:107,0 -DA:109,0 -DA:116,0 -DA:117,0 -DA:118,0 -DA:120,0 -DA:121,0 -DA:122,0 -DA:123,0 -DA:125,0 -LF:54 -LH:5 -BRDA:28,0,0,0 -BRDA:28,0,1,0 -BRDA:28,1,0,0 -BRDA:28,1,1,0 -BRDA:30,2,0,0 -BRDA:30,2,1,0 -BRDA:30,3,0,0 -BRDA:30,3,1,0 -BRDA:36,4,0,0 -BRDA:36,4,1,0 -BRDA:36,5,0,0 -BRDA:36,5,1,0 -BRDA:45,6,0,0 -BRDA:45,6,1,0 -BRDA:45,7,0,0 -BRDA:45,7,1,0 -BRDA:45,8,0,0 -BRDA:45,8,1,0 -BRDA:45,9,0,0 -BRDA:45,9,1,0 -BRDA:52,10,0,0 -BRDA:52,10,1,0 -BRDA:59,11,0,0 -BRDA:59,11,1,0 -BRDA:66,12,0,0 -BRDA:66,12,1,0 -BRDA:81,13,0,0 -BRDA:81,13,1,0 -BRDA:94,14,0,0 -BRDA:94,14,1,0 -BRDA:100,15,0,0 -BRDA:100,15,1,0 -BRF:32 -BRH:0 -end_of_record -TN: -SF:lib/result/JsonResultHandler.ts -FN:13,(anonymous_0) -FN:23,(anonymous_1) -FN:27,(anonymous_2) -FN:41,(anonymous_3) -FN:43,(anonymous_4) -FN:44,(anonymous_5) -FN:59,(anonymous_6) -FN:67,(anonymous_7) -FN:75,(anonymous_8) -FNF:9 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -DA:4,1 -DA:6,1 -DA:18,0 -DA:19,0 -DA:20,0 -DA:24,0 -DA:28,0 -DA:29,0 -DA:32,0 -DA:33,0 -DA:34,0 -DA:37,0 -DA:38,0 -DA:42,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:49,0 -DA:51,0 -DA:53,0 -DA:60,0 -DA:61,0 -DA:63,0 -DA:64,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:71,0 -DA:76,0 -DA:77,0 -DA:79,0 -LF:31 -LH:2 -BRDA:28,0,0,0 -BRDA:28,0,1,0 -BRDA:33,1,0,0 -BRDA:33,1,1,0 -BRDA:37,2,0,0 -BRDA:37,2,1,0 -BRDA:45,3,0,0 -BRDA:45,3,1,0 -BRDA:60,4,0,0 -BRDA:60,4,1,0 -BRDA:60,5,0,0 -BRDA:60,5,1,0 -BRDA:63,6,0,0 -BRDA:63,6,1,0 -BRDA:68,7,0,0 -BRDA:68,7,1,0 -BRDA:68,8,0,0 -BRDA:68,8,1,0 -BRF:18 -BRH:0 -end_of_record -TN: -SF:lib/result/ResultSlicer.ts -FN:17,(anonymous_0) -FN:22,(anonymous_1) -FN:29,(anonymous_2) -FNF:3 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -DA:10,1 -DA:15,0 -DA:18,0 -DA:19,0 -DA:23,0 -DA:24,0 -DA:26,0 -DA:32,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:39,0 -DA:42,0 -DA:43,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:49,0 -DA:53,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:70,0 -DA:71,0 -DA:74,0 -LF:32 -LH:1 -BRDA:23,0,0,0 -BRDA:23,0,1,0 -BRDA:32,1,0,0 -BRDA:32,1,1,0 -BRDA:33,2,0,0 -BRDA:33,2,1,0 -BRDA:46,3,0,0 -BRDA:46,3,1,0 -BRDA:56,4,0,0 -BRDA:56,4,1,0 -BRDA:67,5,0,0 -BRDA:67,5,1,0 -BRDA:68,6,0,0 -BRDA:68,6,1,0 -BRDA:68,7,0,0 -BRDA:68,7,1,0 -BRF:16 -BRH:0 -end_of_record -TN: -SF:lib/result/RowSetProvider.ts -FN:8,(anonymous_1) -FN:13,checkIfOperationHasMoreRows -FN:37,(anonymous_3) -FN:46,(anonymous_4) -FN:54,(anonymous_5) -FN:62,(anonymous_6) -FN:69,(anonymous_7) -FN:96,(anonymous_8) -FNF:8 -FNH:1 -FNDA:1,(anonymous_1) -FNDA:0,checkIfOperationHasMoreRows -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -DA:1,1 -DA:2,1 -DA:3,1 -DA:6,1 -DA:8,1 -DA:9,1 -DA:10,1 -DA:14,0 -DA:15,0 -DA:18,0 -DA:20,0 -DA:21,0 -DA:24,1 -DA:29,0 -DA:31,0 -DA:35,0 -DA:43,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:56,0 -DA:59,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:70,0 -DA:71,0 -DA:72,0 -DA:76,0 -DA:77,0 -DA:81,0 -DA:82,0 -DA:85,0 -DA:86,0 -DA:93,0 -DA:99,0 -DA:100,0 -DA:103,0 -DA:104,0 -DA:107,0 -LF:42 -LH:8 -BRDA:8,0,0,1 -BRDA:8,0,1,1 -BRDA:14,1,0,0 -BRDA:14,1,1,0 -BRDA:18,2,0,0 -BRDA:18,2,1,0 -BRDA:18,3,0,0 -BRDA:18,3,1,0 -BRDA:18,4,0,0 -BRDA:18,4,1,0 -BRDA:21,5,0,0 -BRDA:21,5,1,0 -BRDA:21,6,0,0 -BRDA:21,6,1,0 -BRDA:21,7,0,0 -BRDA:21,7,1,0 -BRDA:21,8,0,0 -BRDA:21,8,1,0 -BRDA:21,9,0,0 -BRDA:21,9,1,0 -BRDA:21,10,0,0 -BRDA:21,10,1,0 -BRDA:43,11,0,0 -BRDA:43,11,1,0 -BRDA:43,12,0,0 -BRDA:43,12,1,0 -BRDA:55,13,0,0 -BRDA:55,13,1,0 -BRDA:71,14,0,0 -BRDA:71,14,1,0 -BRDA:76,15,0,0 -BRDA:76,15,1,0 -BRDA:81,16,0,0 -BRDA:81,16,1,0 -BRDA:99,17,0,0 -BRDA:99,17,1,0 -BRDA:103,18,0,0 -BRDA:103,18,1,0 -BRF:38 -BRH:2 -end_of_record -TN: -SF:lib/result/utils.ts -FN:27,getSchemaColumns -FN:32,(anonymous_2) -FN:35,isString -FN:39,convertJSON -FN:51,convertBigInt -FN:61,convertThriftValue -FN:126,hiveSchemaToArrowSchema -FN:133,(anonymous_8) -FN:149,getColumnValue -FNF:9 -FNH:0 -FNDA:0,getSchemaColumns -FNDA:0,(anonymous_2) -FNDA:0,isString -FNDA:0,convertJSON -FNDA:0,convertBigInt -FNDA:0,convertThriftValue -FNDA:0,hiveSchemaToArrowSchema -FNDA:0,(anonymous_8) -FNDA:0,getColumnValue -DA:1,1 -DA:2,1 -DA:19,1 -DA:20,1 -DA:27,1 -DA:28,0 -DA:29,0 -DA:32,0 -DA:36,0 -DA:40,0 -DA:41,0 -DA:44,0 -DA:45,0 -DA:47,0 -DA:52,0 -DA:53,0 -DA:55,0 -DA:56,0 -DA:58,0 -DA:61,1 -DA:62,0 -DA:63,0 -DA:66,0 -DA:69,0 -DA:72,0 -DA:74,0 -DA:77,0 -DA:79,0 -DA:81,0 -DA:96,0 -DA:101,1 -DA:126,1 -DA:127,0 -DA:128,0 -DA:131,0 -DA:133,0 -DA:134,0 -DA:135,0 -DA:136,0 -DA:137,0 -DA:139,0 -DA:142,0 -DA:143,0 -DA:144,0 -DA:145,0 -DA:146,0 -DA:149,1 -DA:150,0 -DA:151,0 -DA:153,0 -LF:50 -LH:9 -BRDA:28,0,0,0 -BRDA:28,0,1,0 -BRDA:36,1,0,0 -BRDA:36,1,1,0 -BRDA:40,2,0,0 -BRDA:40,2,1,0 -BRDA:52,3,0,0 -BRDA:52,3,1,0 -BRDA:55,4,0,0 -BRDA:55,4,1,0 -BRDA:62,5,0,0 -BRDA:62,5,1,0 -BRDA:66,6,0,0 -BRDA:66,6,1,0 -BRDA:66,6,2,0 -BRDA:66,6,3,0 -BRDA:66,6,4,0 -BRDA:66,6,5,0 -BRDA:66,6,6,0 -BRDA:66,6,7,0 -BRDA:66,6,8,0 -BRDA:66,6,9,0 -BRDA:66,6,10,0 -BRDA:66,6,11,0 -BRDA:66,6,12,0 -BRDA:66,6,13,0 -BRDA:66,6,14,0 -BRDA:66,6,15,0 -BRDA:66,6,16,0 -BRDA:66,6,17,0 -BRDA:66,6,18,0 -BRDA:66,6,19,0 -BRDA:66,6,20,0 -BRDA:66,6,21,0 -BRDA:66,6,22,0 -BRDA:127,7,0,0 -BRDA:127,7,1,0 -BRDA:134,8,0,0 -BRDA:134,8,1,0 -BRDA:134,9,0,0 -BRDA:134,9,1,0 -BRDA:134,10,0,0 -BRDA:134,10,1,0 -BRDA:134,11,0,0 -BRDA:134,11,1,0 -BRDA:135,12,0,0 -BRDA:135,12,1,0 -BRDA:136,13,0,0 -BRDA:136,13,1,0 -BRDA:137,14,0,0 -BRDA:137,14,1,0 -BRDA:150,15,0,0 -BRDA:150,15,1,0 -BRDA:154,16,0,0 -BRDA:154,16,1,0 -BRDA:154,17,0,0 -BRDA:154,17,1,0 -BRDA:154,18,0,0 -BRDA:154,18,1,0 -BRDA:154,19,0,0 -BRDA:154,19,1,0 -BRDA:154,20,0,0 -BRDA:154,20,1,0 -BRDA:154,21,0,0 -BRDA:154,21,1,0 -BRDA:154,22,0,0 -BRDA:154,22,1,0 -BRDA:154,23,0,0 -BRDA:154,23,1,0 -BRDA:154,24,0,0 -BRDA:154,24,1,0 -BRDA:154,25,0,0 -BRDA:154,25,1,0 -BRDA:154,26,0,0 -BRDA:154,26,1,0 -BRDA:154,27,0,0 -BRDA:154,27,1,0 -BRDA:154,28,0,0 -BRDA:154,28,1,0 -BRDA:154,29,0,0 -BRDA:154,29,1,0 -BRF:81 -BRH:0 -end_of_record -TN: -SF:lib/telemetry/CircuitBreaker.ts -FN:23,(anonymous_0) -FN:73,(anonymous_1) -FN:87,(anonymous_2) -FN:114,(anonymous_3) -FN:121,(anonymous_4) -FN:128,(anonymous_5) -FN:135,(anonymous_6) -FN:161,(anonymous_7) -FN:187,(anonymous_8) -FN:198,(anonymous_9) -FN:213,(anonymous_10) -FN:223,(anonymous_11) -FN:233,(anonymous_12) -FNF:13 -FNH:13 -FNDA:1,(anonymous_0) -FNDA:50,(anonymous_1) -FNDA:110,(anonymous_2) -FNDA:27,(anonymous_3) -FNDA:9,(anonymous_4) -FNDA:5,(anonymous_5) -FNDA:20,(anonymous_6) -FNDA:85,(anonymous_7) -FNDA:27,(anonymous_8) -FNDA:34,(anonymous_9) -FNDA:6,(anonymous_10) -FNDA:3,(anonymous_11) -FNDA:1,(anonymous_12) -DA:18,1 -DA:23,1 -DA:25,1 -DA:27,1 -DA:29,1 -DA:47,1 -DA:62,1 -DA:63,50 -DA:65,50 -DA:67,50 -DA:73,50 -DA:74,50 -DA:88,110 -DA:91,110 -DA:92,13 -DA:93,5 -DA:96,8 -DA:97,8 -DA:98,8 -DA:101,105 -DA:102,105 -DA:103,20 -DA:104,20 -DA:106,85 -DA:107,85 -DA:115,27 -DA:122,9 -DA:129,5 -DA:136,20 -DA:139,20 -DA:141,20 -DA:142,11 -DA:143,11 -DA:148,11 -DA:150,3 -DA:151,3 -DA:152,3 -DA:153,3 -DA:162,85 -DA:164,85 -DA:165,85 -DA:167,85 -DA:171,85 -DA:172,18 -DA:173,18 -DA:174,18 -DA:184,1 -DA:187,27 -DA:188,27 -DA:199,34 -DA:200,34 -DA:201,31 -DA:202,31 -DA:203,31 -DA:204,31 -DA:206,34 -DA:214,6 -DA:224,3 -DA:225,3 -DA:226,3 -DA:234,1 -LF:61 -LH:61 -BRDA:23,0,0,1 -BRDA:23,0,1,1 -BRDA:91,1,0,13 -BRDA:91,1,1,97 -BRDA:92,2,0,5 -BRDA:92,2,1,8 -BRDA:92,3,0,13 -BRDA:92,3,1,13 -BRDA:141,4,0,11 -BRDA:141,4,1,9 -BRDA:148,5,0,3 -BRDA:148,5,1,8 -BRDA:171,6,0,18 -BRDA:171,6,1,67 -BRDA:171,7,0,85 -BRDA:171,7,1,83 -BRDA:200,8,0,31 -BRDA:200,8,1,3 -BRF:18 -BRH:18 -end_of_record -TN: -SF:lib/telemetry/DatabricksTelemetryExporter.ts -FN:105,(anonymous_1) -FN:123,(anonymous_2) -FN:131,(anonymous_3) -FN:148,(anonymous_4) -FN:205,(anonymous_5) -FN:216,(anonymous_6) -FN:217,(anonymous_7) -FN:263,(anonymous_8) -FN:324,(anonymous_9) -FN:334,(anonymous_10) -FN:335,(anonymous_11) -FN:345,(anonymous_12) -FN:357,(anonymous_13) -FN:358,(anonymous_14) -FNF:14 -FNH:14 -FNDA:14,(anonymous_1) -FNDA:16,(anonymous_2) -FNDA:13,(anonymous_3) -FNDA:13,(anonymous_4) -FNDA:17,(anonymous_5) -FNDA:18,(anonymous_6) -FNDA:18,(anonymous_7) -FNDA:18,(anonymous_8) -FNDA:17,(anonymous_9) -FNDA:18,(anonymous_10) -FNDA:558,(anonymous_11) -FNDA:14,(anonymous_12) -FNDA:4,(anonymous_13) -FNDA:4,(anonymous_14) -DA:17,1 -DA:19,1 -DA:20,1 -DA:22,1 -DA:98,1 -DA:106,14 -DA:107,14 -DA:108,14 -DA:111,14 -DA:112,14 -DA:115,14 -DA:124,16 -DA:125,1 -DA:128,15 -DA:130,15 -DA:131,15 -DA:132,13 -DA:136,9 -DA:137,2 -DA:139,7 -DA:149,13 -DA:150,13 -DA:151,13 -DA:153,13 -DA:156,13 -DA:157,17 -DA:158,17 -DA:159,6 -DA:161,11 -DA:164,11 -DA:165,2 -DA:166,2 -DA:170,9 -DA:171,2 -DA:172,2 -DA:176,7 -DA:177,3 -DA:178,3 -DA:182,4 -DA:183,4 -DA:184,4 -DA:186,4 -DA:191,4 -DA:197,0 -DA:198,0 -DA:206,17 -DA:207,17 -DA:210,17 -DA:211,17 -DA:216,18 -DA:217,18 -DA:219,17 -DA:225,17 -DA:233,17 -DA:236,17 -DA:237,17 -DA:240,17 -DA:251,15 -DA:252,9 -DA:253,9 -DA:254,9 -DA:257,6 -DA:264,18 -DA:281,18 -DA:283,0 -DA:296,18 -DA:297,0 -DA:299,0 -DA:300,0 -DA:304,0 -DA:305,0 -DA:311,18 -DA:312,0 -DA:318,18 -DA:325,17 -DA:326,1 -DA:328,16 -DA:335,18 -DA:336,558 -DA:337,558 -DA:338,558 -DA:346,14 -DA:348,14 -DA:350,0 -DA:358,4 -DA:359,4 -LF:86 -LH:76 -BRDA:112,0,0,14 -BRDA:112,0,1,0 -BRDA:124,1,0,1 -BRDA:124,1,1,15 -BRDA:124,2,0,16 -BRDA:124,2,1,16 -BRDA:136,3,0,2 -BRDA:136,3,1,7 -BRDA:151,4,0,6 -BRDA:151,4,1,7 -BRDA:151,5,0,13 -BRDA:151,5,1,13 -BRDA:164,6,0,2 -BRDA:164,6,1,9 -BRDA:170,7,0,2 -BRDA:170,7,1,7 -BRDA:176,8,0,3 -BRDA:176,8,1,4 -BRDA:197,9,0,0 -BRDA:197,9,1,0 -BRDA:210,10,0,3 -BRDA:210,10,1,14 -BRDA:210,11,0,17 -BRDA:210,11,1,17 -BRDA:211,12,0,16 -BRDA:211,12,1,1 -BRDA:228,13,0,16 -BRDA:228,13,1,1 -BRDA:233,14,0,16 -BRDA:233,14,1,1 -BRDA:251,15,0,9 -BRDA:251,15,1,6 -BRDA:281,16,0,0 -BRDA:281,16,1,18 -BRDA:281,17,0,18 -BRDA:281,17,1,18 -BRDA:296,18,0,0 -BRDA:296,18,1,18 -BRDA:299,19,0,0 -BRDA:299,19,1,0 -BRDA:299,20,0,0 -BRDA:299,20,1,0 -BRDA:304,21,0,0 -BRDA:304,21,1,0 -BRDA:304,22,0,0 -BRDA:304,22,1,0 -BRDA:311,23,0,0 -BRDA:311,23,1,18 -BRDA:313,24,0,0 -BRDA:313,24,1,0 -BRDA:314,25,0,0 -BRDA:314,25,1,0 -BRDA:325,26,0,1 -BRDA:325,26,1,16 -BRDA:325,27,0,17 -BRDA:325,27,1,17 -BRDA:337,28,0,540 -BRDA:337,28,1,18 -BRF:58 -BRH:40 -end_of_record -TN: -SF:lib/telemetry/ExceptionClassifier.ts -FN:41,(anonymous_1) -FN:73,(anonymous_2) -FNF:2 -FNH:2 -FNDA:11,(anonymous_1) -FNDA:9,(anonymous_2) -DA:17,1 -DA:18,1 -DA:30,1 -DA:43,11 -DA:44,0 -DA:49,11 -DA:51,11 -DA:57,9 -DA:61,2 -DA:75,9 -DA:76,0 -DA:80,9 -DA:81,0 -DA:86,9 -DA:88,9 -DA:95,7 -DA:99,2 -LF:17 -LH:14 -BRDA:43,0,0,0 -BRDA:43,0,1,11 -BRDA:49,1,0,9 -BRDA:49,1,1,2 -BRDA:49,2,0,11 -BRDA:49,2,1,11 -BRDA:51,3,0,9 -BRDA:51,3,1,2 -BRDA:57,4,0,9 -BRDA:57,4,1,8 -BRDA:57,4,2,7 -BRDA:57,4,3,7 -BRDA:75,5,0,0 -BRDA:75,5,1,9 -BRDA:80,6,0,0 -BRDA:80,6,1,9 -BRDA:80,7,0,9 -BRDA:80,7,1,9 -BRDA:86,8,0,7 -BRDA:86,8,1,2 -BRDA:86,9,0,9 -BRDA:86,9,1,9 -BRDA:88,10,0,7 -BRDA:88,10,1,2 -BRDA:95,11,0,7 -BRDA:95,11,1,7 -BRDA:95,11,2,7 -BRDA:95,11,3,7 -BRDA:95,11,4,0 -BRF:29 -BRH:25 -end_of_record -TN: -SF:lib/telemetry/FeatureFlagCache.ts -FN:44,(anonymous_1) -FN:52,(anonymous_2) -FN:69,(anonymous_3) -FN:83,(anonymous_4) -FN:114,(anonymous_5) -FN:162,(anonymous_6) -FN:186,(anonymous_7) -FN:197,(anonymous_8) -FNF:8 -FNH:0 -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -FNDA:0,(anonymous_7) -FNDA:0,(anonymous_8) -DA:17,0 -DA:19,0 -DA:20,0 -DA:37,0 -DA:40,0 -DA:42,0 -DA:44,0 -DA:45,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:59,0 -DA:61,0 -DA:62,0 -DA:70,0 -DA:71,0 -DA:72,0 -DA:73,0 -DA:74,0 -DA:84,0 -DA:85,0 -DA:87,0 -DA:88,0 -DA:91,0 -DA:93,0 -DA:94,0 -DA:96,0 -DA:97,0 -DA:100,0 -DA:104,0 -DA:115,0 -DA:117,0 -DA:119,0 -DA:122,0 -DA:125,0 -DA:127,0 -DA:130,0 -DA:131,0 -DA:134,0 -DA:144,0 -DA:145,0 -DA:146,0 -DA:150,0 -DA:153,0 -DA:155,0 -DA:156,0 -DA:157,0 -DA:158,0 -DA:162,0 -DA:164,0 -DA:166,0 -DA:167,0 -DA:168,0 -DA:169,0 -DA:174,0 -DA:175,0 -DA:178,0 -DA:179,0 -DA:187,0 -DA:188,0 -DA:190,0 -DA:199,0 -LF:62 -LH:0 -BRDA:54,0,0,0 -BRDA:54,0,1,0 -BRDA:71,1,0,0 -BRDA:71,1,1,0 -BRDA:73,2,0,0 -BRDA:73,2,1,0 -BRDA:87,3,0,0 -BRDA:87,3,1,0 -BRDA:91,4,0,0 -BRDA:91,4,1,0 -BRDA:93,5,0,0 -BRDA:93,5,1,0 -BRDA:104,6,0,0 -BRDA:104,6,1,0 -BRDA:104,7,0,0 -BRDA:104,7,1,0 -BRDA:144,8,0,0 -BRDA:144,8,1,0 -BRDA:153,9,0,0 -BRDA:153,9,1,0 -BRDA:153,10,0,0 -BRDA:153,10,1,0 -BRDA:153,10,2,0 -BRDA:156,11,0,0 -BRDA:156,11,1,0 -BRDA:156,12,0,0 -BRDA:156,12,1,0 -BRDA:164,13,0,0 -BRDA:164,13,1,0 -BRDA:187,14,0,0 -BRDA:187,14,1,0 -BRDA:187,15,0,0 -BRDA:187,15,1,0 -BRF:33 -BRH:0 -end_of_record -TN: -SF:lib/telemetry/MetricsAggregator.ts -FN:69,(anonymous_1) -FN:95,(anonymous_2) -FN:124,(anonymous_3) -FN:139,(anonymous_4) -FN:184,(anonymous_5) -FN:218,(anonymous_6) -FN:242,(anonymous_7) -FN:294,(anonymous_8) -FN:316,(anonymous_9) -FN:346,(anonymous_10) -FN:354,(anonymous_11) -FN:370,(anonymous_12) -FNF:12 -FNH:12 -FNDA:14,(anonymous_1) -FNDA:21,(anonymous_2) -FNDA:12,(anonymous_3) -FNDA:2,(anonymous_4) -FNDA:6,(anonymous_5) -FNDA:7,(anonymous_6) -FNDA:5,(anonymous_7) -FNDA:18,(anonymous_8) -FNDA:12,(anonymous_9) -FNDA:21,(anonymous_10) -FNDA:2,(anonymous_11) -FNDA:3,(anonymous_12) -DA:18,1 -DA:19,1 -DA:21,1 -DA:56,1 -DA:57,14 -DA:59,14 -DA:61,14 -DA:69,14 -DA:70,14 -DA:71,14 -DA:72,14 -DA:73,14 -DA:74,14 -DA:77,14 -DA:80,0 -DA:81,0 -DA:84,0 -DA:85,0 -DA:86,0 -DA:96,21 -DA:98,21 -DA:100,21 -DA:101,12 -DA:102,12 -DA:106,9 -DA:107,2 -DA:108,2 -DA:112,7 -DA:113,6 -DA:117,0 -DA:125,12 -DA:133,12 -DA:140,2 -DA:143,2 -DA:144,2 -DA:147,2 -DA:149,2 -DA:151,1 -DA:154,1 -DA:155,0 -DA:156,0 -DA:157,0 -DA:160,1 -DA:169,1 -DA:173,1 -DA:174,1 -DA:176,1 -DA:177,1 -DA:185,6 -DA:187,6 -DA:189,3 -DA:190,3 -DA:191,3 -DA:194,1 -DA:195,1 -DA:196,1 -DA:197,1 -DA:198,1 -DA:199,1 -DA:202,2 -DA:203,2 -DA:204,2 -DA:205,2 -DA:207,2 -DA:211,0 -DA:219,7 -DA:221,7 -DA:222,4 -DA:234,7 -DA:243,5 -DA:245,5 -DA:246,5 -DA:247,5 -DA:248,1 -DA:252,4 -DA:265,4 -DA:268,4 -DA:269,1 -DA:278,1 -DA:282,4 -DA:285,0 -DA:295,18 -DA:298,18 -DA:299,2 -DA:300,2 -DA:301,2 -DA:302,2 -DA:306,18 -DA:307,1 -DA:317,12 -DA:319,12 -DA:320,12 -DA:321,2 -DA:324,10 -DA:325,10 -DA:327,10 -DA:330,10 -DA:334,10 -DA:335,7 -DA:339,0 -DA:347,21 -DA:349,21 -DA:350,21 -DA:351,7 -DA:354,21 -DA:356,2 -DA:360,21 -DA:363,0 -DA:371,3 -DA:373,3 -DA:375,3 -DA:376,3 -DA:377,3 -DA:381,3 -DA:382,1 -DA:386,3 -DA:389,0 -LF:117 -LH:103 -BRDA:72,0,0,2 -BRDA:72,0,1,12 -BRDA:72,1,0,14 -BRDA:72,1,1,14 -BRDA:73,2,0,3 -BRDA:73,2,1,11 -BRDA:73,3,0,14 -BRDA:73,3,1,14 -BRDA:74,4,0,1 -BRDA:74,4,1,13 -BRDA:74,5,0,14 -BRDA:74,5,1,14 -BRDA:100,6,0,12 -BRDA:100,6,1,9 -BRDA:106,7,0,2 -BRDA:106,7,1,7 -BRDA:112,8,0,6 -BRDA:112,8,1,1 -BRDA:143,9,0,2 -BRDA:143,9,1,0 -BRDA:144,10,0,2 -BRDA:144,10,1,0 -BRDA:147,11,0,2 -BRDA:147,11,1,0 -BRDA:147,12,0,2 -BRDA:147,12,1,2 -BRDA:149,13,0,1 -BRDA:149,13,1,1 -BRDA:154,14,0,0 -BRDA:154,14,1,1 -BRDA:154,15,0,1 -BRDA:154,15,1,0 -BRDA:174,16,0,1 -BRDA:174,16,1,0 -BRDA:187,17,0,3 -BRDA:187,17,1,1 -BRDA:187,17,2,2 -BRDA:187,17,3,0 -BRDA:196,18,0,0 -BRDA:196,18,1,1 -BRDA:196,19,0,1 -BRDA:196,19,1,1 -BRDA:197,20,0,0 -BRDA:197,20,1,1 -BRDA:197,21,0,1 -BRDA:197,21,1,1 -BRDA:198,22,0,0 -BRDA:198,22,1,1 -BRDA:198,23,0,1 -BRDA:198,23,1,1 -BRDA:203,24,0,2 -BRDA:203,24,1,0 -BRDA:203,25,0,2 -BRDA:203,25,1,2 -BRDA:204,26,0,2 -BRDA:204,26,1,0 -BRDA:221,27,0,4 -BRDA:221,27,1,3 -BRDA:247,28,0,1 -BRDA:247,28,1,4 -BRDA:298,29,0,2 -BRDA:298,29,1,16 -BRDA:306,30,0,1 -BRDA:306,30,1,17 -BRDA:316,31,0,7 -BRDA:320,32,0,2 -BRDA:320,32,1,10 -BRDA:334,33,0,7 -BRDA:334,33,1,3 -BRDA:350,34,0,7 -BRDA:350,34,1,14 -BRDA:375,35,0,3 -BRDA:375,35,1,0 -BRF:73 -BRH:60 -end_of_record -TN: -SF:lib/telemetry/TelemetryEventEmitter.ts -FN:35,(anonymous_0) -FN:48,(anonymous_1) -FN:72,(anonymous_2) -FN:96,(anonymous_3) -FN:132,(anonymous_4) -FN:164,(anonymous_5) -FNF:6 -FNH:6 -FNDA:11,(anonymous_0) -FNDA:4,(anonymous_1) -FNDA:2,(anonymous_2) -FNDA:1,(anonymous_3) -FNDA:1,(anonymous_4) -FNDA:3,(anonymous_5) -DA:17,1 -DA:19,1 -DA:20,1 -DA:32,1 -DA:35,11 -DA:36,11 -DA:39,11 -DA:40,11 -DA:49,4 -DA:51,3 -DA:52,3 -DA:53,3 -DA:60,3 -DA:63,2 -DA:73,2 -DA:75,1 -DA:76,1 -DA:77,1 -DA:84,1 -DA:87,0 -DA:105,1 -DA:107,1 -DA:108,1 -DA:109,1 -DA:120,1 -DA:123,0 -DA:139,1 -DA:141,1 -DA:142,1 -DA:143,1 -DA:152,1 -DA:155,0 -DA:171,3 -DA:173,2 -DA:174,2 -DA:175,2 -DA:184,2 -DA:187,0 -LF:38 -LH:34 -BRDA:40,0,0,11 -BRDA:40,0,1,0 -BRDA:40,1,0,11 -BRDA:40,1,1,11 -BRDA:49,2,0,1 -BRDA:49,2,1,3 -BRDA:73,3,0,1 -BRDA:73,3,1,1 -BRDA:105,4,0,0 -BRDA:105,4,1,1 -BRDA:139,5,0,0 -BRDA:139,5,1,1 -BRDA:171,6,0,1 -BRDA:171,6,1,2 -BRF:14 -BRH:11 -end_of_record -TN: -SF:lib/telemetry/types.ts -FN:25,(anonymous_0) -FNF:1 -FNH:1 -FNDA:1,(anonymous_0) -DA:20,1 -DA:25,1 -DA:26,1 -DA:27,1 -DA:28,1 -DA:29,1 -DA:30,1 -DA:65,1 -LF:8 -LH:8 -BRDA:25,0,0,1 -BRDA:25,0,1,1 -BRF:2 -BRH:2 -end_of_record -TN: -SF:lib/utils/CloseableCollection.ts -FN:7,(anonymous_0) -FN:10,(anonymous_1) -FN:11,(anonymous_2) -FN:17,(anonymous_3) -FN:24,(anonymous_4) -FNF:5 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -DA:7,1 -DA:8,0 -DA:11,0 -DA:12,0 -DA:14,0 -DA:18,0 -DA:19,0 -DA:21,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -LF:13 -LH:1 -BRDA:18,0,0,0 -BRDA:18,0,1,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:lib/utils/OperationIterator.ts -FN:8,(anonymous_0) -FN:15,(anonymous_1) -FN:19,(anonymous_2) -FN:31,(anonymous_3) -FN:41,(anonymous_4) -FN:57,(anonymous_5) -FN:66,(anonymous_6) -FNF:7 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:0,(anonymous_6) -DA:9,0 -DA:10,0 -DA:16,0 -DA:20,0 -DA:22,0 -DA:23,0 -DA:26,0 -DA:32,0 -DA:33,0 -DA:36,0 -DA:40,1 -DA:42,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:48,0 -DA:52,1 -DA:53,0 -DA:55,0 -DA:58,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:70,0 -DA:73,0 -DA:74,0 -DA:75,0 -DA:76,0 -DA:80,0 -DA:83,0 -LF:30 -LH:2 -BRDA:22,0,0,0 -BRDA:22,0,1,0 -BRDA:22,1,0,0 -BRDA:22,1,1,0 -BRDA:22,2,0,0 -BRDA:22,2,1,0 -BRDA:22,3,0,0 -BRDA:22,3,1,0 -BRDA:32,4,0,0 -BRDA:32,4,1,0 -BRDA:32,5,0,0 -BRDA:32,5,1,0 -BRDA:32,6,0,0 -BRDA:32,6,1,0 -BRDA:43,7,0,0 -BRDA:43,7,1,0 -BRDA:67,8,0,0 -BRDA:67,8,1,0 -BRDA:74,9,0,0 -BRDA:74,9,1,0 -BRF:20 -BRH:0 -end_of_record -TN: -SF:lib/utils/buildUserAgentString.ts -FN:6,getNodeVersion -FN:10,getOperatingSystemVersion -FN:14,redactInternalToken -FN:24,buildUserAgentString -FNF:4 -FNH:0 -FNDA:0,getNodeVersion -FNDA:0,getOperatingSystemVersion -FNDA:0,redactInternalToken -FNDA:0,buildUserAgentString -DA:1,1 -DA:2,1 -DA:4,1 -DA:7,0 -DA:11,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:18,0 -DA:21,0 -DA:24,1 -DA:25,0 -DA:26,0 -DA:29,0 -DA:30,0 -LF:15 -LH:4 -BRDA:17,0,0,0 -BRDA:17,0,1,0 -BRDA:25,1,0,0 -BRDA:25,1,1,0 -BRF:4 -BRH:0 -end_of_record -TN: -SF:lib/utils/definedOrError.ts -FN:1,definedOrError -FNF:1 -FNH:0 -FNDA:0,definedOrError -DA:1,1 -DA:2,0 -DA:3,0 -DA:5,0 -LF:4 -LH:1 -BRDA:2,0,0,0 -BRDA:2,0,1,0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:lib/utils/formatProgress.ts -FN:8,(anonymous_0) -FN:12,(anonymous_1) -FN:13,(anonymous_2) -FN:16,(anonymous_3) -FN:19,(anonymous_4) -FN:25,formatProgress -FNF:6 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:0,formatProgress -DA:3,1 -DA:6,0 -DA:9,0 -DA:13,0 -DA:17,0 -DA:18,0 -DA:19,0 -DA:21,0 -DA:25,1 -DA:26,0 -LF:10 -LH:2 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/utils/index.ts -FN:7,(anonymous_7) -FNF:1 -FNH:0 -FNDA:0,(anonymous_7) -DA:1,1 -DA:2,1 -DA:3,1 -DA:4,1 -DA:5,1 -DA:7,1 -LF:6 -LH:6 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/utils/lz4.ts -FN:5,tryLoadLZ4Module -FN:35,getResolvedModule -FNF:2 -FNH:0 -FNDA:0,tryLoadLZ4Module -FNDA:0,getResolvedModule -DA:6,0 -DA:7,0 -DA:9,0 -DA:11,0 -DA:12,0 -DA:15,0 -DA:16,0 -DA:19,0 -DA:21,0 -DA:22,0 -DA:27,0 -DA:28,0 -DA:36,0 -DA:37,0 -DA:39,0 -DA:42,1 -LF:16 -LH:1 -BRDA:9,0,0,0 -BRDA:9,0,1,0 -BRDA:9,1,0,0 -BRDA:9,1,1,0 -BRDA:15,2,0,0 -BRDA:15,2,1,0 -BRDA:19,3,0,0 -BRDA:19,3,1,0 -BRDA:36,4,0,0 -BRDA:36,4,1,0 -BRDA:37,5,0,0 -BRDA:37,5,1,0 -BRDA:37,6,0,0 -BRDA:37,6,1,0 -BRDA:39,7,0,0 -BRDA:39,7,1,0 -BRF:16 -BRH:0 -end_of_record -TN: -SF:lib/utils/protocolVersion.ts -FN:16,isFeatureSupported -FN:33,supportsParameterizedQueries -FN:43,supportsAsyncMetadataOperations -FN:53,supportsResultPersistenceMode -FN:63,supportsArrowCompression -FN:73,supportsArrowMetadata -FN:83,supportsMultipleCatalogs -FN:93,supportsCloudFetch -FNF:8 -FNH:0 -FNDA:0,isFeatureSupported -FNDA:0,supportsParameterizedQueries -FNDA:0,supportsAsyncMetadataOperations -FNDA:0,supportsResultPersistenceMode -FNDA:0,supportsArrowCompression -FNDA:0,supportsArrowMetadata -FNDA:0,supportsMultipleCatalogs -FNDA:0,supportsCloudFetch -DA:1,1 -DA:16,1 -DA:20,0 -DA:21,0 -DA:24,0 -DA:33,1 -DA:34,0 -DA:43,1 -DA:44,0 -DA:53,1 -DA:54,0 -DA:63,1 -DA:64,0 -DA:73,1 -DA:74,0 -DA:83,1 -DA:84,0 -DA:93,1 -DA:94,0 -LF:19 -LH:9 -BRDA:20,0,0,0 -BRDA:20,0,1,0 -BRDA:20,1,0,0 -BRDA:20,1,1,0 -BRF:4 -BRH:0 -end_of_record