// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. licenses this file
// to you 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 {
fetchResults,
fetchResultSize,
ResultApiResponse,
ResultMeta,
ResultSizeApiResponse
} from '../../../apps/editor/execution/api';
import {
EXECUTABLE_RESULT_UPDATED_TOPIC,
ExecutableResultUpdatedEvent
} from '../../../apps/editor/execution/events';
import { Observable } from 'knockout';
import * as ko from 'knockout';
import huePubSub from '../../../utils/huePubSub';
import sleep from '../../../utils/timing/sleep';
import SqlExecutable, { ExecutionStatus } from './sqlExecutable';
export const RESULT_TYPE = {
TABLE: 'table'
};
const META_TYPE_TO_CSS: { [key: string]: string } = {
bigint: 'sort-numeric',
date: 'sort-date',
datetime: 'sort-date',
decimal: 'sort-numeric',
double: 'sort-numeric',
float: 'sort-numeric',
int: 'sort-numeric',
real: 'sort-numeric',
smallint: 'sort-numeric',
timestamp: 'sort-date',
tinyint: 'sort-numeric'
};
const NUMERIC_TYPES: { [key: string]: boolean } = {
bigint: true,
decimal: true,
double: true,
float: true,
int: true,
real: true,
smallint: true,
tinyint: true
};
const DATE_TIME_TYPES: { [key: string]: boolean } = {
date: true,
datetime: true,
timestamp: true
};
const COMPLEX_TYPES: { [key: string]: boolean } = {
array: true,
map: true,
struct: true
};
// huePubSub.subscribe('editor.ws.query.fetch_result', executionResult => {
// if (executionResult.status != 'finalMessage') {
// const result = new ExecutionResult(null);
// result.fetchedOnce = true;
// result.handleResultResponse(executionResult);
// // eslint-disable-next-line no-undef
// executionResult.data.forEach(element => $('#wsResult').append('
' + element + ''));
// }
// });
export interface KoEnrichedMeta extends ResultMeta {
cssClass: string;
checked: Observable;
originalIndex: number;
}
export type ResultRow = (string | number)[];
export enum ResultType {
Table = 'table'
}
export default class ExecutionResult {
executable: SqlExecutable;
streaming: boolean;
type?: ResultType;
rows: ResultRow[] = [];
meta: ResultMeta[] = [];
cleanedMeta: ResultMeta[] = [];
cleanedDateTimeMeta: ResultMeta[] = [];
cleanedStringMeta: ResultMeta[] = [];
cleanedNumericMeta: ResultMeta[] = [];
koEnrichedMeta: KoEnrichedMeta[] = [];
lastRows: ResultRow[] = [];
images = [];
hasMore = true;
isEscaped = false;
fetchedOnce = false;
constructor(executable: SqlExecutable, streaming?: boolean) {
this.executable = executable;
this.streaming = !!streaming;
}
async fetchResultSize(): Promise {
if (this.executable.status === ExecutionStatus.failed) {
return;
}
let attempts = 0;
const waitForRows = async (): Promise => {
attempts++;
if (attempts < 10) {
const resultSizeResponse = await fetchResultSize({
executable: this.executable,
silenceErrors: true
});
if (resultSizeResponse.rows) {
return resultSizeResponse;
} else {
await sleep(1000);
return await waitForRows();
}
} else {
return Promise.reject();
}
};
return await waitForRows();
}
async fetchRows(options?: { rows?: number; startOver?: boolean }): Promise {
const resultResponse = await fetchResults({
executable: this.executable,
rows: (options && options.rows) || 100,
startOver: !!(options && options.startOver)
});
if (resultResponse) {
this.handleResultResponse(resultResponse);
}
}
handleResultResponse(resultResponse: ResultApiResponse): void {
const initialIndex = this.rows.length;
resultResponse.data.forEach((row, index) => {
row.unshift(initialIndex + index + 1);
});
this.rows.push(...resultResponse.data);
this.lastRows = resultResponse.data;
if (!this.meta.length) {
this.meta = resultResponse.meta;
this.meta.unshift({ type: 'INT_TYPE', name: '', comment: null });
this.meta.forEach((item, index) => {
const cleanedType = item.type.replace(/_type/i, '').toLowerCase();
if (index) {
this.cleanedMeta.push(item);
if (NUMERIC_TYPES[cleanedType]) {
this.cleanedNumericMeta.push(item);
} else if (DATE_TIME_TYPES[cleanedType]) {
this.cleanedDateTimeMeta.push(item);
} else if (!COMPLEX_TYPES[cleanedType]) {
this.cleanedStringMeta.push(item);
}
}
this.koEnrichedMeta.push({
name: item.name,
type: cleanedType,
comment: item.comment,
cssClass: META_TYPE_TO_CSS[cleanedType] || 'sort-string',
checked: ko.observable(true),
originalIndex: index
});
});
}
this.hasMore = resultResponse.has_more;
this.isEscaped = resultResponse.isEscaped;
if (resultResponse.type) {
this.type = resultResponse.type;
}
this.fetchedOnce = true;
this.notify();
}
notify(): void {
huePubSub.publish(EXECUTABLE_RESULT_UPDATED_TOPIC, this);
}
}