"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TeradataCursor = exports.STRING = exports.NUMBER = exports.DATE = exports.BINARY = void 0;
const teradata_logging_1 = require("./teradata-logging");
const teradata_exceptions_1 = require("./teradata-exceptions");
exports.BINARY = "Uint8Array";
exports.DATE = "Date";
exports.NUMBER = "number";
exports.STRING = "string";
class TeradataCursor {
    constructor(connection, ffiLib, ffiRef, ffiByteArray) {
        this.desc = null;
        this.rowCount = 0n;
        this.rowNumber = null;
        this.uRowsHand = null;
        this.arraySize = 1;
        this.bClosed = false;
        this.conn = connection;
        this.lib = ffiLib;
        this.ref = ffiRef;
        this.byteArray = ffiByteArray;
        this.logger = new teradata_logging_1.TeradataLogging(this.conn.uLog);
    }
    get description() {
        return this.desc;
    }
    get rowcount() {
        return this.rowCount;
    }
    get rownumber() {
        return this.rowNumber;
    }
    get connection() {
        return this.conn;
    }
    get uRowsHandle() {
        return this.uRowsHand;
    }
    get arraysize() {
        return this.arraySize;
    }
    set arraysize(value) {
        if (Number.isInteger(value) && value > 0) {
            this.arraySize = value;
        }
        else {
            throw new TypeError(`unexpected value ${value}`);
        }
    }
    callproc(procname, parameters) {
        this.logger.traceLog("> enter callproc TeradataCursor");
        try {
            let sCall = "{CALL " + procname;
            if (parameters) {
                sCall += " (";
                for (let i = 0; i < parameters.length; i++) {
                    if (i > 0) {
                        sCall += ",";
                    }
                    sCall += "?";
                }
                sCall += ")";
            }
            sCall += "}";
            this.logger.traceLog("sCall=" + sCall);
            this.execute(sCall, parameters);
        }
        finally {
            this.logger.traceLog("< leave callproc TeradataCursor");
        }
    }
    close() {
        this.logger.traceLog("> enter close TeradataCursor");
        try {
            if (!this.bClosed) {
                this.bClosed = true;
                this._closeRows();
            }
        }
        finally {
            this.logger.traceLog("< leave close TeradataCursor");
        }
    }
    _stopIfClosed() {
        this.logger.traceLog("> enter _stopIfClosed TeradataCursor");
        try {
            if (this.bClosed) {
                throw new teradata_exceptions_1.ProgrammingError("Cursor is closed");
            }
        }
        finally {
            this.logger.traceLog("< leave _stopIfClosed TeradataCursor");
        }
    }
    _closeRows() {
        this.logger.traceLog("> enter _closeRows TeradataCursor");
        try {
            if (this.uRowsHand) {
                const dStartTime = Date.now();
                const jsgoCloseRows = this.lib.jsgoCloseRows;
                const jsgoFreePointer = this.lib.jsgoFreePointer;
                const cStringPtrType = this.ref.refType(this.ref.types.char);
                const outputPtrPtr = this.ref.alloc(cStringPtrType);
                jsgoCloseRows(this.conn.uLog, this.uRowsHand, outputPtrPtr);
                this.uRowsHand = null;
                let outputString = this.ref.allocCString("");
                outputString = this.ref.deref(outputPtrPtr);
                if (outputString.length > 0) {
                    const msg = this.ref.readCString(outputString);
                    jsgoFreePointer(this.conn.uLog, outputString);
                    throw new teradata_exceptions_1.OperationalError(msg);
                }
                this.logger.timingLog("_closeRows took " + (Date.now() - dStartTime) + " ms");
            }
        }
        finally {
            this.logger.traceLog("< leave _closeRows TeradataCursor");
        }
    }
    execute(operation, parameters, ignoreErrors = null) {
        this.logger.traceLog("> enter execute TeradataCursor");
        try {
            if (parameters === undefined || parameters === null) {
                this.executemany(operation, null, ignoreErrors);
            }
            else if (Array.isArray(parameters[0])) {
                this.executemany(operation, parameters, ignoreErrors);
            }
            else {
                this.executemany(operation, [parameters], ignoreErrors);
            }
        }
        finally {
            this.logger.traceLog("< leave execute TeradataCursor");
        }
    }
    executemany(procname, seqOfParameters, ignoreErrors = null) {
        this.logger.traceLog("> enter executemany TeradataCursor");
        this.logger.debugLog(seqOfParameters, true);
        try {
            const dStartTimeBegin = Date.now();
            this._stopIfClosed();
            this._closeRows();
            if (ignoreErrors !== null) {
                if (typeof ignoreErrors === "number") {
                    ignoreErrors = [ignoreErrors];
                }
                for (let i = 0; i < ignoreErrors.length; i++) {
                    if (!Number.isInteger(ignoreErrors[i])) {
                        throw new TypeError(`ignoreErrors[${i}] ${ignoreErrors[i]} is not an integer`);
                    }
                }
            }
            else {
                ignoreErrors = [];
            }
            const setIgnoreErrorCodes = [...ignoreErrors];
            let dStartTime = Date.now();
            const jsgoCreateRows = this.lib.jsgoCreateRows;
            const jsgoFreePointer = this.lib.jsgoFreePointer;
            const procnamePtr = this.ref.allocCString(procname);
            const aao = [];
            if (seqOfParameters !== null) {
                for (const row of seqOfParameters) {
                    for (const field of row) {
                        let ao = Buffer.allocUnsafe(0);
                        if (typeof field === "string") {
                            const aby = Buffer.from(field, "utf8");
                            ao = Buffer.allocUnsafe(9);
                            ao.writeUInt8("S".charCodeAt(0), 0);
                            ao.writeBigUInt64BE(BigInt(aby.byteLength), 1);
                            ao = Buffer.concat([ao, aby]);
                        }
                        else if (typeof field === "number") {
                            ao = Buffer.allocUnsafe(9);
                            ao.writeUInt8("D".charCodeAt(0), 0);
                            ao.writeDoubleBE(field, 1);
                        }
                        else if (field === null || field === undefined) {
                            ao = Buffer.allocUnsafe(1);
                            ao.writeUInt8("N".charCodeAt(0), 0);
                        }
                        else if (field instanceof Date) {
                            const sDate = field.toISOString().slice(0, 10);
                            const sTime = field.toISOString().slice(11, 23);
                            const s = `${sDate} ${sTime}`;
                            const aby = Buffer.from(s, "utf8");
                            ao = Buffer.allocUnsafe(9);
                            ao.writeUInt8("X".charCodeAt(0), 0);
                            ao.writeBigUInt64BE(BigInt(aby.byteLength), 1);
                            ao = Buffer.concat([ao, aby]);
                        }
                        else if (typeof field === "bigint") {
                            ao = Buffer.allocUnsafe(9);
                            ao.writeUInt8("L".charCodeAt(0), 0);
                            ao.writeBigInt64BE(field, 1);
                        }
                        else if (field instanceof Uint8Array) {
                            ao = Buffer.allocUnsafe(9);
                            ao.writeUInt8("B".charCodeAt(0), 0);
                            ao.writeBigUInt64BE(BigInt(field.length), 1);
                            const buf = Buffer.from(field);
                            ao = Buffer.concat([ao, buf]);
                        }
                        else {
                            throw new TypeError(`unexpected type=${typeof field} value=${field}`);
                        }
                        aao.push(ao);
                    }
                    aao.push(Buffer.from("Z"));
                }
            }
            aao.push(Buffer.from("Z"));
            const abyBindValuesBuffer = Buffer.concat(aao);
            this.logger.debugLog(abyBindValuesBuffer, true);
            const abyBindValues = Uint8Array.from(abyBindValuesBuffer);
            this.logger.timingLog("executemany serialize bind value took " + (Date.now() - dStartTime) + " ms and produced " + abyBindValues.length + " bytes");
            dStartTime = Date.now();
            const cStringPtrType = this.ref.refType(this.ref.types.char);
            const outputPtrPtr = this.ref.alloc(cStringPtrType);
            const rowsHandlePtr = this.ref.alloc(this.ref.types.ulong);
            jsgoCreateRows(this.conn.uLog, this.conn.uPoolHandle, procnamePtr, abyBindValues.length, abyBindValues, outputPtrPtr, rowsHandlePtr);
            let outputString = this.ref.allocCString("");
            outputString = this.ref.deref(outputPtrPtr);
            if (outputString.length > 0) {
                const sErr = this.ref.readCString(outputString);
                jsgoFreePointer(this.conn.uLog, outputString);
                const regex = /\[Error (\d+)\]/g;
                const found = sErr.matchAll(regex);
                const aErrorCodes = [];
                if (found) {
                    for (const codes of found) {
                        aErrorCodes.push(parseInt(codes[1], 10));
                    }
                }
                let bIgnore = false;
                for (const errorCode of aErrorCodes) {
                    if (setIgnoreErrorCodes.includes(errorCode)) {
                        bIgnore = true;
                        break;
                    }
                }
                this.logger.debugLog(`executemany bIgnore=${bIgnore} aErrorCodes=[${aErrorCodes}] setIgnoreErrorCodes=[${setIgnoreErrorCodes}]`);
                if (bIgnore) {
                    return;
                }
                throw new teradata_exceptions_1.OperationalError(sErr);
            }
            this.logger.timingLog("executemany createRows took " + (Date.now() - dStartTime) + " ms");
            if (rowsHandlePtr) {
                this.uRowsHand = this.ref.readUInt64LE(rowsHandlePtr);
            }
            else {
                throw new teradata_exceptions_1.OperationalError("rowsHandlePtr is null.");
            }
            this._obtainResultMetaData();
            this.logger.timingLog("executemany took " + (Date.now() - dStartTimeBegin) + " ms");
        }
        finally {
            this.logger.traceLog("< leave executemany TeradataCursor");
        }
    }
    fetchone() {
        this.logger.traceLog("> enter fetchone TeradataCursor");
        try {
            const row = this.next();
            return row;
        }
        catch (error) {
            if (error instanceof teradata_exceptions_1.OperationalError && error.message === "StopIteration") {
                return null;
            }
            else {
                throw error;
            }
        }
        finally {
            this.logger.traceLog("< leave fetchone TeradataCursor");
        }
    }
    fetchall() {
        this.logger.traceLog("> enter fetchall TeradataCursor");
        this._stopIfClosed();
        try {
            if (this.uRowsHandle) {
                const dStartTime = Date.now();
                let aRow = [];
                let rows = this.fetchmany_();
                while (rows) {
                    aRow = aRow.concat(rows);
                    rows = this.fetchmany_();
                }
                this.logger.timingLog("fetchall fetched " + aRow.length + " rows and took " + (Date.now() - dStartTime) + " ms");
                return aRow;
            }
        }
        finally {
            this.logger.traceLog("< leave fetchall TeradataCursor");
        }
        return [];
    }
    fetchmany(nDesiredRowCount) {
        this.logger.traceLog("> enter fetchmany TeradataCursor");
        this._stopIfClosed();
        try {
            if (this.uRowsHandle) {
                const result = this.fetchmany_(nDesiredRowCount);
                if (result) {
                    return result;
                }
            }
        }
        finally {
            this.logger.traceLog("< leave fetchmany TeradataCursor");
        }
        return [];
    }
    nextset() {
        this.logger.traceLog("> enter nextset TeradataCursor");
        try {
            this._stopIfClosed();
            if (this.uRowsHandle) {
                const dStartTime = Date.now();
                const jsgoNextResult = this.lib.jsgoNextResult;
                const jsgoFreePointer = this.lib.jsgoFreePointer;
                const cStringPtrType = this.ref.refType(this.ref.types.char);
                const outputPtrPtr = this.ref.alloc(cStringPtrType);
                const availPtr = this.ref.allocCString("C");
                jsgoNextResult(this.conn.uLog, this.uRowsHand, outputPtrPtr, availPtr);
                let outputString = this.ref.allocCString("");
                outputString = this.ref.deref(outputPtrPtr);
                if (outputString.length > 0) {
                    const msg = this.ref.readCString(outputString);
                    jsgoFreePointer(this.conn.uLog, outputString);
                    throw new teradata_exceptions_1.OperationalError(msg);
                }
                if (this.ref.readCString(availPtr) === "Y") {
                    this._obtainResultMetaData();
                    this.logger.timingLog("nextset() took " + (Date.now() - dStartTime) + " ms");
                    return true;
                }
                else {
                    this.desc = null;
                    this.rowCount = 0n;
                    this.logger.timingLog("nextset() took " + (Date.now() - dStartTime) + " ms");
                    return false;
                }
            }
            else {
                return false;
            }
        }
        finally {
            this.logger.traceLog("< leave nextset TeradataCursor");
        }
    }
    setinputsizes(sizes) {
        this._stopIfClosed();
    }
    setoutputsize(size, column) {
        this._stopIfClosed();
    }
    next() {
        this.logger.traceLog("> enter next TeradataCursor");
        try {
            this._stopIfClosed();
            if (this.uRowsHandle) {
                const row = this.fetchmany_(1);
                if (row && row.length > 0) {
                    return row[0];
                }
            }
            this.logger.debugLog("End of rows");
            throw new teradata_exceptions_1.OperationalError("StopIteration");
        }
        finally {
            this.logger.traceLog("< leave next TeradataCursor");
        }
    }
    fetchmany_(nDesiredRowCount) {
        this.logger.traceLog("> enter fetchmany_ TeradataCursor");
        try {
            const dStartTimeBegin = Date.now();
            this.logger.debugLog(`fetchmany_ nDesiredRowCount=${nDesiredRowCount}`);
            this.logger.debugLog(`fetchmany_ arraySize=${this.arraySize}`);
            if (!nDesiredRowCount) {
                nDesiredRowCount = this.arraySize;
            }
            const jsgoFetchRow = this.lib.jsgoFetchRow;
            const jsgoFreePointer = this.lib.jsgoFreePointer;
            const cStringPtrType = this.ref.refType(this.ref.types.char);
            const outputPtrPtr = this.ref.alloc(cStringPtrType);
            const columnValuesByteCountPtr = this.ref.alloc(this.ref.types.int64);
            const outByteArray = this.ref.refType(this.byteArray);
            const byteArrayPtrPtr = this.ref.alloc(outByteArray);
            let dStartTime = Date.now();
            jsgoFetchRow(this.conn.uLog, this.uRowsHand, outputPtrPtr, columnValuesByteCountPtr, byteArrayPtrPtr, nDesiredRowCount);
            this.logger.timingLog("fetchmany_ call to jsgoFetchRow took " + (Date.now() - dStartTime) + " ms");
            let outputString = this.ref.allocCString("");
            outputString = this.ref.deref(outputPtrPtr);
            if (outputString.length > 0) {
                const msg = this.ref.readCString(outputString);
                jsgoFreePointer(this.conn.uLog, outputString);
                throw new teradata_exceptions_1.OperationalError(msg);
            }
            dStartTime = Date.now();
            const rowsLength = this.ref.deref(columnValuesByteCountPtr);
            const byteArrayPtr = this.ref.deref(byteArrayPtrPtr);
            this.logger.timingLog("fetchmany_ call to ref.deref took " + (Date.now() - dStartTime) + " ms");
            let nObservedRowCount = 0;
            dStartTime = Date.now();
            if (byteArrayPtr.length !== 0) {
                this.logger.debugLog(`jsgoFetchRow rowsLength=${rowsLength}`);
                if (this.logger.bDebugLog) {
                    console.log(byteArrayPtr.buffer.slice(byteArrayPtr.byteOffset, byteArrayPtr.byteOffset + rowsLength));
                }
                const byteBuffer = Buffer.from(this.ref.reinterpret(byteArrayPtr, rowsLength, 0));
                const rows = [];
                let row;
                let i = 0;
                while (i < rowsLength - 1) {
                    row = [];
                    while (byteBuffer[i] !== "Z".charCodeAt(0)) {
                        let iNew = 0;
                        if (byteBuffer[i] === "S".charCodeAt(0)) {
                            iNew = this._deserializeString(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "I".charCodeAt(0)) {
                            iNew = this._deserializeInt(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "N".charCodeAt(0)) {
                            iNew = this._deserializeNull(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "D".charCodeAt(0)) {
                            iNew = this._deserializeDouble(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "L".charCodeAt(0)) {
                            iNew = this._deserializeLong(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "M".charCodeAt(0)) {
                            iNew = this._deserializeNumber(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "U".charCodeAt(0)) {
                            iNew = this._deserializeDate(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "V".charCodeAt(0)) {
                            iNew = this._deserializeTime(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "W".charCodeAt(0)) {
                            iNew = this._deserializeTimeWithTimeZone(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "X".charCodeAt(0)) {
                            iNew = this._deserializeTimestamp(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "Y".charCodeAt(0)) {
                            iNew = this._deserializeTimestampWithTimeZone(byteBuffer, i, row);
                        }
                        else if (byteBuffer[i] === "B".charCodeAt(0)) {
                            iNew = this._deserializeBytes(byteBuffer, i, row);
                        }
                        else {
                            throw new teradata_exceptions_1.OperationalError("Unknown Data Type:" + String.fromCharCode(byteBuffer[i]) + ".");
                        }
                        this.logger.debugLog(`fetchmany_() row[${row.length - 1}], typeCode=${byteBuffer[i].toString()} type=${typeof row[row.length - 1]} value=${row[row.length - 1]}`);
                        if (this.logger.bDebugLog) {
                            console.log("NODEJS-DEBUG deserialized row:");
                            console.log(row);
                        }
                        i = iNew;
                    }
                    rows.push(row);
                    nObservedRowCount += 1;
                    i += 1;
                }
                this.logger.timingLog("fetchmany_ deserialized " + rowsLength + " bytes and took " + (Date.now() - dStartTime) + " ms");
                this.logger.debugLog(`fetchmany_ nObservedRowCount=${nObservedRowCount}`);
                dStartTime = Date.now();
                jsgoFreePointer(this.conn.uLog, byteArrayPtr);
                this.logger.timingLog("fetchmany_ call to jsgoFreePointer took " + (Date.now() - dStartTime) + " ms");
                if (rows.length === 0) {
                    throw new teradata_exceptions_1.OperationalError(`Found no row with byteArrayPtr.length = ${byteArrayPtr.length} `);
                }
                this.logger.timingLog("fetchmany_(" + nDesiredRowCount + ") fetched " + nObservedRowCount + " rows and took " + (Date.now() - dStartTimeBegin) + " ms");
                return rows;
            }
            else {
                return null;
            }
        }
        finally {
            this.logger.traceLog("< leave fetchmany_ TeradataCursor");
        }
    }
    _obtainResultMetaData() {
        this.logger.traceLog("> enter _obtainResultMetaData TeradataCursor");
        try {
            const dStartTimeBegin = Date.now();
            const jsgoResultMetaData = this.lib.jsgoResultMetaData;
            const jsgoFreePointer = this.lib.jsgoFreePointer;
            const cStringPtrType = this.ref.refType(this.ref.types.char);
            const outputPtrPtr = this.ref.alloc(cStringPtrType);
            const metaDataByteCountPtr = this.ref.alloc(this.ref.types.int);
            const outByteArray = this.ref.refType(this.byteArray);
            const byteArrayPtrPtr = this.ref.alloc(outByteArray);
            const uActivityCount = this.ref.alloc(this.ref.types.ulong);
            let dStartTime = Date.now();
            jsgoResultMetaData(this.conn.uLog, this.uRowsHand, outputPtrPtr, uActivityCount, metaDataByteCountPtr, byteArrayPtrPtr);
            this.logger.timingLog("_obtainResultMetaData call to jsgoResultMetaData took " + (Date.now() - dStartTime) + " ms");
            let outputString = this.ref.allocCString("");
            outputString = this.ref.deref(outputPtrPtr);
            if (outputString.length > 0) {
                const msg = this.ref.readCString(outputString);
                jsgoFreePointer(this.conn.uLog, outputString);
                throw new teradata_exceptions_1.OperationalError(msg);
            }
            dStartTime = Date.now();
            this.rowCount = BigInt(this.ref.readUInt64LE(uActivityCount));
            const byteArrayPtr = this.ref.deref(byteArrayPtrPtr);
            const metadataLength = this.ref.deref(metaDataByteCountPtr);
            if (metadataLength > 0) {
                this.desc = [];
                let i = 0;
                const pcColumnMetaData = Buffer.from(this.ref.reinterpret(byteArrayPtr, metadataLength, 0));
                while (pcColumnMetaData[i] !== "Z".charCodeAt(0)) {
                    const columnDesc = [];
                    i = this._deserializeString(pcColumnMetaData, i, columnDesc);
                    i = this._deserializeString(pcColumnMetaData, i, null);
                    i = this._deserializeString(pcColumnMetaData, i, columnDesc);
                    if (columnDesc[columnDesc.length - 1] === "b") {
                        columnDesc[columnDesc.length - 1] = exports.BINARY;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "d") {
                        columnDesc[columnDesc.length - 1] = exports.NUMBER;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "i" || columnDesc[columnDesc.length - 1] === "l") {
                        columnDesc[columnDesc.length - 1] = exports.NUMBER;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "m") {
                        columnDesc[columnDesc.length - 1] = exports.NUMBER;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "s") {
                        columnDesc[columnDesc.length - 1] = exports.STRING;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "u") {
                        columnDesc[columnDesc.length - 1] = exports.STRING;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "v" || columnDesc[columnDesc.length - 1] === "w") {
                        columnDesc[columnDesc.length - 1] = exports.STRING;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "x") {
                        columnDesc[columnDesc.length - 1] = exports.DATE;
                    }
                    else if (columnDesc[columnDesc.length - 1] === "y") {
                        columnDesc[columnDesc.length - 1] = exports.STRING;
                    }
                    columnDesc.push(null);
                    i = this._deserializeLong(pcColumnMetaData, i, columnDesc);
                    i = this._deserializeLong(pcColumnMetaData, i, columnDesc);
                    i = this._deserializeLong(pcColumnMetaData, i, columnDesc);
                    i = this._deserializeBool(pcColumnMetaData, i, columnDesc);
                    this.logger.debugLog(columnDesc, true);
                    this.desc.push(columnDesc);
                }
                this.logger.timingLog("_obtainResultMetaData deserialized " + metadataLength + " bytes and took " + (Date.now() - dStartTime) + " ms");
                jsgoFreePointer(this.conn.uLog, byteArrayPtr);
                this.logger.timingLog("_obtainResultMetaData() took " + (Date.now() - dStartTimeBegin) + " ms");
            }
        }
        finally {
            this.logger.traceLog("< leave _obtainResultMetaData TeradataCursor");
        }
    }
    _deserializeBool(pc, i, row) {
        if (pc[i] === "T".charCodeAt(0) || pc[i] === "F".charCodeAt(0)) {
            if (row) {
                row.push(pc[i] === "T".charCodeAt(0));
            }
            return i + 1;
        }
        else if (pc[i] === "N".charCodeAt(0)) {
            return this._deserializeNull(pc, i, row);
        }
        else {
            throw new teradata_exceptions_1.OperationalError("Expected column type T/F/N.");
        }
    }
    _deserializeBytes(pc, i, row) {
        if (pc[i] === "B".charCodeAt(0)) {
            i += 1;
            const unByteCount = pc.readBigUInt64BE(i);
            let uByteCount = 0;
            if (unByteCount <= Number.MAX_SAFE_INTEGER) {
                uByteCount = Number(unByteCount);
            }
            else {
                throw new teradata_exceptions_1.OperationalError(`Data length > ${unByteCount} is not supported.`);
            }
            i += 8;
            const abyBuffer = pc.slice(i, i + uByteCount);
            const abyValue = new Uint8Array(abyBuffer);
            i += uByteCount;
            if (row) {
                row.push(abyValue);
            }
            return i;
        }
        else if (pc[i] === "N".charCodeAt(0)) {
            return this._deserializeNull(pc, i, row);
        }
        else {
            throw new teradata_exceptions_1.OperationalError("Expected column type B/N.");
        }
    }
    _deserializeCharacterValue(abyTypeCode, pc, i, row) {
        if (pc[i] === abyTypeCode) {
            i += 1;
            let uByteCount = 0;
            const unByteCount = pc.readBigUInt64BE(i);
            if (unByteCount <= Number.MAX_SAFE_INTEGER) {
                uByteCount = Number(unByteCount);
            }
            else {
                throw new teradata_exceptions_1.OperationalError(`Data length > ${unByteCount} is not supported.`);
            }
            i += 8;
            const sValue = pc.toString("utf8", i, i + uByteCount);
            i += uByteCount;
            if (row) {
                if (abyTypeCode === "L".charCodeAt(0)) {
                    throw new teradata_exceptions_1.OperationalError("BigInt should not be a char value.");
                }
                else if (abyTypeCode === "M".charCodeAt(0)) {
                    row.push(Number(sValue));
                }
                else if (abyTypeCode === "X".charCodeAt(0)) {
                    const timePieces = /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]*)?/;
                    const u = timePieces.exec(sValue);
                    let date;
                    if (u) {
                        const nMillisecondsUTC = Date.UTC(parseInt(u[1], 10), parseInt(u[2], 10) - 1, parseInt(u[3], 10), parseInt(u[4], 10), parseInt(u[5], 10), parseInt(u[6], 10), (u[7] && parseFloat(u[7]) * 1000) || 0);
                        date = new Date(nMillisecondsUTC);
                        row.push(date);
                    }
                    else {
                        throw new teradata_exceptions_1.OperationalError("Unexpected timestamp value");
                    }
                }
                else {
                    row.push(sValue);
                }
            }
            return i;
        }
        else if (pc[i] === "N".charCodeAt(0)) {
            return this._deserializeNull(pc, i, row);
        }
        else {
            throw new teradata_exceptions_1.OperationalError("Expected column type " + String.fromCharCode(abyTypeCode) + "/N.");
        }
    }
    _deserializeDate(pc, i, row) {
        return this._deserializeCharacterValue("U".charCodeAt(0), pc, i, row);
    }
    _deserializeDouble(pc, i, row) {
        if (pc[i] === "D".charCodeAt(0)) {
            i += 1;
            const dValue = pc.readDoubleBE(i);
            i += 8;
            if (row) {
                row.push(dValue);
            }
            return i;
        }
        else if (pc[i] === "N".charCodeAt(0)) {
            return this._deserializeNull(pc, i, row);
        }
        else {
            throw new teradata_exceptions_1.OperationalError("Expected column type D/N.");
        }
    }
    _deserializeInt(pc, i, row) {
        if (pc[i] === "I".charCodeAt(0)) {
            i += 1;
            const nValue = pc.readInt32BE(i);
            i += 4;
            if (row) {
                row.push(nValue);
            }
            return i;
        }
        else if (pc[i] === "N".charCodeAt(0)) {
            return this._deserializeNull(pc, i, row);
        }
        else {
            throw new teradata_exceptions_1.OperationalError("Expected column type I/N.");
        }
    }
    _deserializeLong(pc, i, row) {
        if (pc[i] === "L".charCodeAt(0)) {
            i += 1;
            const nValue = pc.readBigInt64BE(i);
            i += 8;
            if (row) {
                row.push(nValue);
            }
            return i;
        }
        else if (pc[i] === "N".charCodeAt(0)) {
            return this._deserializeNull(pc, i, row);
        }
        else {
            throw new teradata_exceptions_1.OperationalError("Expected column type L/N.");
        }
    }
    _deserializeNull(pc, i, row) {
        if (pc[i] === "N".charCodeAt(0)) {
            if (row) {
                row.push(null);
            }
            return i + 1;
        }
        else {
            throw new teradata_exceptions_1.OperationalError("Expected column type N.");
        }
    }
    _deserializeNumber(pc, i, row) {
        return this._deserializeCharacterValue("M".charCodeAt(0), pc, i, row);
    }
    _deserializeString(pc, i, row) {
        return this._deserializeCharacterValue("S".charCodeAt(0), pc, i, row);
    }
    _deserializeTime(pc, i, row) {
        return this._deserializeCharacterValue("V".charCodeAt(0), pc, i, row);
    }
    _deserializeTimeWithTimeZone(pc, i, row) {
        return this._deserializeCharacterValue("W".charCodeAt(0), pc, i, row);
    }
    _deserializeTimestamp(pc, i, row) {
        return this._deserializeCharacterValue("X".charCodeAt(0), pc, i, row);
    }
    _deserializeTimestampWithTimeZone(pc, i, row) {
        return this._deserializeCharacterValue("Y".charCodeAt(0), pc, i, row);
    }
}
exports.TeradataCursor = TeradataCursor;
//# sourceMappingURL=teradata-cursor.js.map