/*############################################################################## Copyright (C) 2011 HPCC Systems. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ############################################################################## */ #include "platform.h" #include "jliball.hpp" #include "bcd.hpp" #include "workunit.hpp" #include "seclib.hpp" #include "eclrtl.hpp" #include "fvresultset.ipp" #include "fileview.hpp" #include "fverror.hpp" #include "fvdatasource.hpp" #include "fvwusource.ipp" #include "fvresultset.ipp" #include "fvdisksource.ipp" #include "fvidxsource.ipp" #include "fvrelate.ipp" #include "dasess.hpp" #define DEFAULT_FETCH_SIZE 100 #define FILEVIEW_VERSION 1 #define MAX_SORT_ELEMENTS 1000 //#define PAGELOADED_WORKUNITS #define MAX_FILTER_ELEMENTS 20000 #define MAX_SKIP_ELEMENTS 1000 #define MAX_SKIP_TIME 10000 // 10 seconds, no match then give up CResultSetMetaData * nullMeta; ITypeInfo * filePositionType; MODULE_INIT(INIT_PRIORITY_STANDARD) { nullMeta = new CResultSetMetaData(NULL, false); filePositionType = makeIntType(8, false); return true; } MODULE_EXIT() { filePositionType->Release(); nullMeta->Release(); } //--------------------------------------------------------------------------- IFvDataSource * createDataSource(IConstWUResult * wuResult, const char * wuid, const char * username, const char * password) { Owned ds; SCMStringBuffer tempFilename; wuResult->getResultFilename(tempFilename); __int64 rowLimit = wuResult->getResultRowLimit(); if (tempFilename.length()) ds.setown(new WorkunitDiskDataSource(tempFilename.str(), wuResult, wuid, username, password)); else if (rowLimit == -2) assertex(!"Delayed queries not yet supported"); else if ((rowLimit == 0) && (wuResult->getResultTotalRowCount() == 0)) ds.setown(new NullDataSource); #ifdef PAGELOADED_WORKUNITS else if (wuResult->getResultDataSize() < PAGED_WU_LIMIT) ds.setown(new FullWorkUnitDataSource(wuResult, wuid)); else ds.setown(new PagedWorkUnitDataSource(wuResult, wuid)); #else else ds.setown(new FullWorkUnitDataSource(wuResult, wuid)); #endif if (ds && ds->init()) return ds.getClear(); return NULL; } IFvDataSource * createFileDataSource(const char * logicalName, const char * cluster, const char * username, const char * password) { Owned udesc; if(username != NULL && *username != '\0') { udesc.setown(createUserDescriptor()); udesc->set(username, password); } Owned df = queryDistributedFileDirectory().lookup(logicalName, udesc.get()); if (!df) throwError1(FVERR_CouldNotResolveX, logicalName); return createFileDataSource(df, logicalName, cluster, username, password); } IFvDataSource * createFileDataSource(IDistributedFile * df, const char * logicalName, const char * cluster, const char * username, const char * password) { bool blocked; if (df->isCompressed(&blocked) && !blocked) throwError1(FVERR_CompressedFile, logicalName); IPropertyTree & properties = df->queryAttributes(); const char * format = properties.queryProp("@format"); if (format && (stricmp(format,"csv")==0 || memicmp(format, "utf", 3) == 0)) { Owned ds = new DirectCsvDiskDataSource(df, format); if (ds && ds->init()) return ds.getClear(); return NULL; } const char * recordEcl = properties.queryProp("ECL"); if (!recordEcl) throwError1(FVERR_NoRecordDescription, logicalName); OwnedHqlExpr diskRecord = parseQuery(recordEcl); if (!diskRecord) throwError1(FVERR_BadRecordDesc, logicalName); Owned ds; try { const char * kind = properties.queryProp("@kind"); if (kind && (stricmp(kind, "key") == 0)) { if (isSimplifiedRecord(diskRecord, true)) ds.setown(new IndexDataSource(logicalName, diskRecord, username, password)); else throwError1(FVERR_ViewComplexKey, logicalName); } else if (isSimplifiedRecord(diskRecord, false)) ds.setown(new DirectDiskDataSource(logicalName, diskRecord, username, password)); else if (cluster) ds.setown(new TranslatedDiskDataSource(logicalName, diskRecord, cluster, username, password)); else throwError1(FVERR_NeedClusterToBrowseX, logicalName); } catch (IException * e) { ds.setown(new FailureDataSource(diskRecord, e, false, 0)); e->Release(); } if (ds && ds->init()) return ds.getClear(); return NULL; } //--------------------------------------------------------------------------- static __int64 getIntBias(unsigned size) { return I64C(1) << (size * 8 - 1); } static __int64 getIntFromSwapInt(ITypeInfo & type, const void * cur, bool isMappedIndexField); static __int64 getIntFromInt(ITypeInfo & type, const void * cur, bool isMappedIndexField) { #if __BYTE_ORDER == __LITTLE_ENDIAN if (isMappedIndexField) return getIntFromSwapInt(type, cur, isMappedIndexField); #endif unsigned size=type.getSize(); bool isSigned = type.isSigned(); if (isSigned && !isMappedIndexField) { switch (size) { case 1: return *((signed char *)cur); case 2: return *((short *)cur); case 3: return rtlReadInt3(cur); case 4: return *((int *)cur); case 5: return rtlReadInt5(cur); case 6: return rtlReadInt6(cur); case 7: return rtlReadInt7(cur); case 8: return *((__int64 *)cur); } } else { unsigned __int64 result; switch (size) { case 1: result = *((unsigned char *)cur); break; case 2: result = *((unsigned short *)cur); break; case 3: result = rtlReadUInt3(cur); break; case 4: result = *((unsigned int *)cur); break; case 5: result = rtlReadUInt5(cur); break; case 6: result = rtlReadUInt6(cur); break; case 7: result = rtlReadUInt7(cur); break; case 8: result = *((unsigned __int64 *)cur); break; default: UNIMPLEMENTED; } if (isSigned && isMappedIndexField) result -= getIntBias(size); return result; } UNIMPLEMENTED; } static __int64 getIntFromSwapInt(ITypeInfo & type, const void * cur, bool isMappedIndexField) { #if __BYTE_ORDER != __LITTLE_ENDIAN if (insideIndex) return getIntFromInt(type, cur, isMappedIndexField); #endif unsigned size = type.getSize(); bool isSigned = type.isSigned(); if (isSigned && !isMappedIndexField) { switch (size) { case 1: return *((signed char *)cur); case 2: return rtlRevInt2(cur); case 3: return rtlRevInt3(cur); case 4: return rtlRevInt4(cur); case 5: return rtlRevInt5(cur); case 6: return rtlRevInt6(cur); case 7: return rtlRevInt7(cur); case 8: return rtlRevInt8(cur); } } else { unsigned __int64 result; switch (size) { case 1: result = *((unsigned char *)cur); break; case 2: result = rtlRevUInt2(cur); break; case 3: result = rtlRevUInt3(cur); break; case 4: result = rtlRevUInt4(cur); break; case 5: result = rtlRevUInt5(cur); break; case 6: result = rtlRevUInt6(cur); break; case 7: result = rtlRevUInt7(cur); break; case 8: result = rtlRevUInt8(cur); break; } if (isSigned && isMappedIndexField) result -= getIntBias(size); return result; } UNIMPLEMENTED; } //--------------------------------------------------------------------------- CResultSetMetaData::CResultSetMetaData(IFvDataSourceMetaData * _meta, bool _useXPath) { meta = _meta; fixedSize = true; alwaysUseXPath = _useXPath; unsigned max = meta ? meta->numColumns() : 0; for (unsigned idx = 0; idx < max; idx++) { ITypeInfo * type = meta->queryType(idx); CResultSetColumnInfo * column = new CResultSetColumnInfo; column->type = type; column->flag = meta->queryFieldFlags(idx); if (type->getSize() == UNKNOWN_LENGTH) fixedSize = false; switch (type->getTypeCode()) { case type_void: case type_boolean: case type_int: case type_swapint: case type_decimal: case type_real: case type_data: case type_string: case type_varstring: case type_qstring: case type_unicode: case type_varunicode: case type_utf8: case type_packedint: column->childMeta.set(nullMeta); break; case type_set: case type_table: case type_groupedtable: column->childMeta.setown(new CResultSetMetaData(meta->queryChildMeta(idx), _useXPath)); break; default: UNIMPLEMENTED; } columns.append(*column); } } CResultSetMetaData::CResultSetMetaData(const CResultSetMetaData & _other) { meta = _other.meta; alwaysUseXPath = _other.alwaysUseXPath; ForEachItemIn(i, _other.columns) columns.append(OLINK(_other.columns.item(i))); fixedSize = _other.fixedSize; } void CResultSetMetaData::calcFieldOffsets(const byte * data, unsigned * offsets) const { unsigned curOffset = 0; ForEachItemIn(idx, columns) { ITypeInfo & type = *columns.item(idx).type; unsigned size = type.getSize(); if (size == UNKNOWN_LENGTH) { const byte * cur = data + curOffset; switch (type.getTypeCode()) { case type_data: case type_string: case type_table: case type_groupedtable: size = *((unsigned *)cur) + sizeof(unsigned); break; case type_set: size = *((unsigned *)(cur + sizeof(bool))) + sizeof(unsigned) + sizeof(bool); break; case type_qstring: size = rtlQStrSize(*((unsigned *)cur)) + sizeof(unsigned); break; case type_unicode: size = *((unsigned *)cur)*sizeof(UChar) + sizeof(unsigned); break; case type_utf8: size = sizeof(unsigned) + rtlUtf8Size(*(unsigned *)cur, cur+sizeof(unsigned)); break; case type_varstring: size = strlen((char *)cur)+1; break; case type_varunicode: size = (rtlUnicodeStrlen((UChar *)cur)+1)*sizeof(UChar); break; case type_packedint: size = rtlGetPackedSize(cur); break; default: UNIMPLEMENTED; } } offsets[idx] = curOffset; curOffset += size; } offsets[columns.ordinality()] = curOffset; } IResultSetMetaData * CResultSetMetaData::getChildMeta(int column) const { if (columns.isItem(column)) return LINK(columns.item(column).childMeta); return NULL; } int CResultSetMetaData::getColumnCount() const { return columns.ordinality(); } DisplayType CResultSetMetaData::getColumnDisplayType(int columnIndex) const { CResultSetColumnInfo & curColumn = columns.item(columnIndex); unsigned flag = curColumn.flag; switch (flag) { case FVFFbeginif: return TypeBeginIfBlock; case FVFFendif: return TypeEndIfBlock; case FVFFbeginrecord: return TypeBeginRecord; case FVFFendrecord: return TypeEndRecord; } ITypeInfo & type = *curColumn.type; switch (type.getTypeCode()) { case type_boolean: return TypeBoolean; case type_int: case type_swapint: case type_packedint: if (type.isSigned()) return TypeInteger; return TypeUnsignedInteger; case type_decimal: case type_real: return TypeReal; case type_qstring: case type_string: case type_varstring: return TypeString; case type_unicode: case type_varunicode: case type_utf8: return TypeUnicode; case type_data: return TypeData; case type_set: return TypeSet; case type_table: case type_groupedtable: return TypeDataset; } UNIMPLEMENTED; // Should have been translated to one of the above by this point... return TypeUnknown; } IStringVal & CResultSetMetaData::getColumnLabel(IStringVal & s, int column) const { assertex(columns.isItem(column)); s.set(meta->queryName(column)); return s; } IStringVal & CResultSetMetaData::getColumnEclType(IStringVal & s, int column) const { assertex(columns.isItem(column)); StringBuffer str; s.set(columns.item(column).type->getECLType(str).str()); return s; } IStringVal & CResultSetMetaData::getColumnXmlType(IStringVal & s, int column) const { //This really doesn't make any sense - only makes sense to get the entire schema because of the user-defined types UNIMPLEMENTED; } bool CResultSetMetaData::isSigned(int column) const { assertex(columns.isItem(column)); return columns.item(column).type->isSigned(); } bool CResultSetMetaData::isEBCDIC(int column) const { assertex(columns.isItem(column)); ICharsetInfo * charset = columns.item(column).type->queryCharset(); return (charset && charset->queryName() == ebcdicAtom); } bool CResultSetMetaData::isBigEndian(int column) const { assertex(columns.isItem(column)); ITypeInfo * type = columns.item(column).type; #if __BYTE_ORDER == __LITTLE_ENDIAN return (type->getTypeCode() == type_swapint); #else return (type->getTypeCode() != type_swapint); #endif } unsigned CResultSetMetaData::getColumnRawType(int column) const { assertex(columns.isItem(column)); return getClarionResultType(columns.item(column).type); } unsigned CResultSetMetaData::getColumnRawSize(int column) const { assertex(columns.isItem(column)); unsigned size = columns.item(column).type->getSize(); return (size == UNKNOWN_LENGTH) ? 0 : size; } unsigned CResultSetMetaData::getNumKeyedColumns() const { return meta->numKeyedColumns(); } IStringVal & CResultSetMetaData::getNaturalColumnLabel(IStringVal & s, int columnIndex) const { assertex(columns.isItem(columnIndex)); CResultSetColumnInfo & column = columns.item(columnIndex); s.set(column.naturalName); return s; } bool CResultSetMetaData::isVirtual(int columnIndex) const { assertex(columns.isItem(columnIndex)); CResultSetColumnInfo & column = columns.item(columnIndex); return (column.flag == FVFFvirtual); } bool CResultSetMetaData::hasGetTranslation(int columnIndex) const { assertex(columns.isItem(columnIndex)); CResultSetColumnInfo & column = columns.item(columnIndex); return (column.getTransforms.ordinality() != 0); } bool CResultSetMetaData::hasSetTranslation(int columnIndex) const { assertex(columns.isItem(columnIndex)); CResultSetColumnInfo & column = columns.item(columnIndex); return (column.setTransforms.ordinality() != 0); } static bool findSize(int size, IntArray &sizes) { ForEachItemIn(idx, sizes) { if (sizes.item(idx)==size) return true; } return false; } unsigned CResultSetMetaData::queryColumnIndex(unsigned firstField, const char * fieldName) const { // DonKeep track of record depth, so we don't select fields from nested records.. unsigned recordDepth = 0; unsigned max = columns.ordinality(); for (unsigned idx =firstField; idx < max; idx++) { CResultSetColumnInfo & column = columns.item(idx); unsigned flag = column.flag; const char * name = meta->queryName(idx); if ((recordDepth == 0) && (name && stricmp(name, fieldName) == 0)) return idx; switch (flag) { case FVFFbeginrecord: recordDepth++; break; case FVFFendrecord: if (recordDepth == 0) return NotFound; recordDepth--; break; } } return NotFound; } ITypeInfo * containsSingleSimpleFieldBlankXPath(IResultSetMetaData * meta) { if (meta->getColumnCount() != 1) return NULL; CResultSetMetaData * castMeta = static_cast(meta); const char * xpath = castMeta->queryXPath(0); if (xpath && (*xpath == 0)) { return castMeta->queryType(0); } return NULL; } void CResultSetMetaData::getXmlSchema(ISchemaBuilder & builder, bool useXPath) const { StringBuffer prefix, suffix; ForEachItemIn(idx, columns) { CResultSetColumnInfo & column = columns.item(idx); unsigned flag = column.flag; const char * name = meta->queryName(idx); const char * childname = NULL; if (useXPath) { const char * xname = meta->queryXPath(idx); if (xname) { const char * slash = strchr(xname, '/'); if (slash) { const char * slash2 = strchr(slash+1, '/'); prefix.clear().append(slash-xname, xname); name = prefix.str(); if (slash2) { suffix.clear().append(slash2-(slash+1), slash+1); childname = suffix.str(); } else childname = slash+1; } else name = xname; } } switch (flag) { case FVFFbeginif: builder.beginIfBlock(); break; case FVFFendif: builder.endIfBlock(); break; case FVFFbeginrecord: builder.beginRecord(name); break; case FVFFendrecord: builder.endRecord(name); break; case FVFFdataset: { if (!childname) childname = "Row"; ITypeInfo * singleFieldType = (useXPath && name && *name && childname && *childname) ? containsSingleSimpleFieldBlankXPath(column.childMeta.get()) : NULL; if (!singleFieldType || !builder.addSingleFieldDataset(name, childname, *singleFieldType)) { if (builder.beginDataset(name, childname)) { static_cast(column.childMeta.get())->getXmlSchema(builder, useXPath); } builder.endDataset(name, childname); } break; } default: { ITypeInfo & type = *column.type; if (type.getTypeCode() == type_set) { if (!childname) childname = "Item"; builder.addSetField(name, childname, type); } else builder.addField(name, type); break; } } } } IStringVal & CResultSetMetaData::getXmlSchema(IStringVal & str, bool addHeader) const { XmlSchemaBuilder builder(addHeader); getXmlSchema(builder, alwaysUseXPath); builder.getXml(str); return str; } IStringVal & CResultSetMetaData::getXmlXPathSchema(IStringVal & str, bool addHeader) const { XmlSchemaBuilder builder(addHeader); getXmlSchema(builder, true); builder.getXml(str); return str; } //--------------------------------------------------------------------------- IResultSetCursor * CResultSetBase::createCursor() { return doCreateCursor(false); } IResultSetCursor * CResultSetBase::createCursor(IDataVal & savedCursor) { MemoryBuffer buffer; buffer.append(savedCursor.length(), savedCursor.data()); byte version; unsigned column; bool desc; buffer.read(version); buffer.read(column); if (column == (unsigned)-1) return new CResultSetCursor(getMeta(), this, buffer); buffer.read(desc); return new CResultSetSortedCursor(getMeta(), this, column, desc, buffer); } IFilteredResultSet * CResultSetBase::createFiltered() { return new CFilteredResultSetBuilder(this); } IResultSetCursor * CResultSetBase::createSortedCursor(unsigned column, bool descend) { if (getNumRows() > MAX_SORT_ELEMENTS) return NULL; return new CResultSetSortedCursor(getMeta(), this, column, descend); } CResultSetCursor * CResultSetBase::doCreateCursor(bool oldInterface) { return new CResultSetCursor(getMeta(), this, oldInterface); } //--------------------------------------------------------------------------- CResultSet::CResultSet(IFvDataSource * _dataSource, bool _useXPath) : meta(_dataSource->queryMetaData(), _useXPath) { dataSource.set(_dataSource); if (dataSource->isIndex()) calcMappedFields(); } IExtendedNewResultSet * CResultSet::cloneForFilter() { Owned clonedDataSource = dataSource->cloneForFilter(); if (clonedDataSource) return new CResultSet(clonedDataSource, meta.alwaysUseXPath); return NULL; } void CResultSet::calcMappedFields() { //Work out which fields within an index record are mapped. It should be any numeric fields, //but not those within ifblocks or nested child records.... and not the fileposition unsigned max = getMetaData().getColumnCount(); unsigned nesting = 0; for(unsigned i = 0; i < max; i++) { unsigned flag = meta.queryFlags(i); bool mapped = false; switch (flag) { case FVFFbeginif: case FVFFbeginrecord: nesting++; break; case FVFFendif: case FVFFendrecord: nesting--; break; default: if ((nesting == 0) && (i != max -1)) { ITypeInfo * type = meta.queryType(i); switch (type->getTypeCode()) { case type_int: case type_swapint: mapped = true; break; } } break; } mappedFields.append(mapped); } } int CResultSet::findColumn(const char * columnName) const { SCMStringBuffer s; for(int i = 0; i < getMetaData().getColumnCount(); i++) { s.clear(); if(!stricmp(columnName, getMetaData().getColumnLabel(s, i).str())) return i; } return -1; } const IResultSetMetaData & CResultSet::getMetaData() const { return meta; } __int64 CResultSet::getNumRows() const { return dataSource->numRows(); } void CResultSet::setColumnMapping(IDistributedFile * df) { StringBuffer mappingText; df->getColumnMapping(mappingText); if (mappingText.length()) { FieldTransformInfoArray mappings; parseFileColumnMapping(mappings, mappingText.str(), meta); ForEachItemIn(i, mappings) { FieldTransformInfo & cur = mappings.item(i); CResultSetColumnInfo & column = meta.columns.item(cur.column); appendArray(column.getTransforms, cur.getTransforms); appendArray(column.setTransforms, cur.setTransforms); column.naturalName.setown(cur.naturalName.detach()); } } } bool CResultSet::supportsRandomSeek() const { return meta.meta->supportsRandomSeek(); } bool CResultSet::fetch(MemoryBuffer & out, __int64 offset) { CriticalBlock procedure(cs); return dataSource->fetchRow(out, offset); } bool CResultSet::fetchRaw(MemoryBuffer & out, __int64 offset) { CriticalBlock procedure(cs); return dataSource->fetchRawRow(out, offset); } bool CResultSet::getRow(MemoryBuffer & out, __int64 row) { CriticalBlock procedure(cs); return dataSource->getRow(out, row); } bool CResultSet::getRawRow(MemoryBuffer & out, __int64 row) { CriticalBlock procedure(cs); return dataSource->getRawRow(out, row); } bool CResultSet::isMappedIndexField(unsigned columnIndex) { return mappedFields.isItem(columnIndex) && mappedFields.item(columnIndex); } void CResultSet::serialize(MemoryBuffer & out) { out.append((byte)FILEVIEW_VERSION); // current version } //--------------------------------------------------------------------------- CResultSetCursor::CResultSetCursor(const CResultSetMetaData & _meta, IExtendedNewResultSet * _resultSet, bool _oldInterface) : meta(_meta) { oldInterface = _oldInterface; init(_resultSet); absolute(BEFORE_FIRST_ROW); } CResultSetCursor::CResultSetCursor(const CResultSetMetaData & _meta, IExtendedNewResultSet * _resultSet, MemoryBuffer & buffer) : meta(_meta) { oldInterface = false; init(_resultSet); buffer.read(curRow); absolute(curRow); } CResultSetCursor::~CResultSetCursor() { if (!oldInterface) resultSet->onClose(); delete [] offsets; } void CResultSetCursor::init(IExtendedNewResultSet * _resultSet) { resultSet.set(_resultSet); offsets = new unsigned[meta.getColumnCount()+1]; if (meta.isFixedSize()) meta.calcFieldOffsets(NULL, offsets); if (!oldInterface) resultSet->onOpen(); } bool CResultSetCursor::absolute(__int64 row) { curRow = row; curRowData.clear(); if ((row >= 0) && resultSet->getRow(curRowData, curRow)) { if (!meta.isFixedSize()) meta.calcFieldOffsets((const byte *)curRowData.toByteArray(), offsets); return true; } return false; } void CResultSetCursor::afterLast() { absolute(getNumRows()+1); curRowData.clear(); } void CResultSetCursor::beforeFirst() { absolute(BEFORE_FIRST_ROW); curRowData.clear(); } int CResultSetCursor::findColumn(const char * columnName) const { SCMStringBuffer s; for(int i = 0; i < getMetaData().getColumnCount(); i++) { s.clear(); if(!stricmp(columnName, getMetaData().getColumnLabel(s, i).str())) return i; } return -1; } bool CResultSetCursor::fetch(__int64 offset) { curRow = AFTER_LAST_ROW; curRowData.clear(); if (resultSet->fetch(curRowData, offset)) { if (!meta.isFixedSize()) meta.calcFieldOffsets((const byte *)curRowData.toByteArray(), offsets); return true; } return false; } bool CResultSetCursor::first() { return absolute(0); } static unsigned getLength(ITypeInfo & type, const byte * & cursor) { unsigned len = type.getStringLen(); if (len != UNKNOWN_LENGTH) return len; len = *(unsigned *)cursor; cursor += sizeof(unsigned); return len; } bool CResultSetCursor::getBoolean(int columnIndex) { if (!isValid()) return false; const byte * cur = getColumn(columnIndex); ITypeInfo & type = *meta.columns.item(columnIndex).type; unsigned size = type.getSize(); unsigned len = UNKNOWN_LENGTH; // error value switch (type.getTypeCode()) { case type_void: case type_set: case type_table: case type_groupedtable: return false; case type_boolean: return *((byte *)cur) != 0; case type_int: case type_swapint: switch (size) { case 1: return *((byte *)cur) != 0; case 2: return *((short *)cur) != 0; case 4: return *((int *)cur) != 0; case 8: return *((__int64 *)cur) != 0; } break; case type_packedint: if (type.isSigned()) return rtlGetPackedSigned(cur) != 0; else return rtlGetPackedUnsigned(cur) != 0; case type_decimal: if (type.isSigned()) return Dec2Bool(size, cur); return UDec2Bool(size, cur); case type_real: if (size == 4) return *((float *)cur) != 0; return *((double *)cur) != 0; case type_string: len = getLength(type, cur); return rtlStrToBool(len, (const char *)cur); case type_unicode: len = getLength(type, cur); return rtlUnicodeToBool(len, (UChar const *)cur); case type_varstring: return rtlVStrToBool((const char *)cur); case type_varunicode: return rtlUnicodeToBool(rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur); case type_utf8: len = getLength(type, cur); return rtlUtf8ToBool(len, (const char *)cur); case type_qstring: len = getLength(type, cur); return rtlQStrToBool(len, (const char *)cur); case type_data: len = getLength(type, cur); return rtlDataToBool(len, cur); } UNIMPLEMENTED; return true; } IDataVal & CResultSetCursor::getBytes(IDataVal &d, int columnIndex) { if (isValid()) d.setLen(getColumn(columnIndex), offsets[columnIndex+1]-offsets[columnIndex]); else d.setLen(NULL, 0); return d; } IResultSetCursor * CResultSetCursor::getChildren(int columnIndex) const { if (!isValid()) return NULL; ITypeInfo & type = *meta.columns.item(columnIndex).type; const byte * cur = getColumn(columnIndex); switch (type.getTypeCode()) { case type_set: cur += sizeof(bool); break; case type_table: case type_groupedtable: break; default: return NULL; } unsigned len = *(unsigned *)cur; const byte * data = cur + sizeof(unsigned); Owned childData = meta.meta->createChildDataSource(columnIndex, len, data); Owned nestedResult = new CResultSet(childData, meta.alwaysUseXPath); return nestedResult->createCursor(); } xdouble CResultSetCursor::getDouble(int columnIndex) { if (!isValid()) return 0.0; const byte * cur = getColumn(columnIndex); ITypeInfo & type = *meta.columns.item(columnIndex).type; unsigned size = type.getSize(); unsigned len = UNKNOWN_LENGTH; // error value switch (type.getTypeCode()) { case type_void: case type_set: case type_table: case type_groupedtable: return 0; case type_boolean: return *((byte *)cur) != 0; case type_int: if (type.isSigned()) return (xdouble)getIntFromInt(type, cur, isMappedIndexField(columnIndex)); return (xdouble)(__int64)getIntFromInt(type, cur, isMappedIndexField(columnIndex)); case type_swapint: if (type.isSigned()) return (xdouble)getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex)); return (xdouble)(__int64)getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex)); case type_packedint: if (type.isSigned()) return (xdouble)rtlGetPackedSigned(cur); else return (xdouble)rtlGetPackedUnsigned(cur); case type_decimal: { DecLock(); if (type.isSigned()) DecPushDecimal(cur, type.getSize(), type.getPrecision()); else DecPushUDecimal(cur, type.getSize(), type.getPrecision()); xdouble ret = DecPopReal(); DecUnlock(); return ret; } case type_real: if (size == 4) return *((float *)cur); return *((double *)cur); case type_data: case type_string: len = getLength(type, cur); return rtlStrToReal(len, (const char *)cur); case type_qstring: { len = getLength(type, cur); unsigned newSize; char * newStr; rtlQStrToStrX(newSize, newStr, len, (const char *)cur); double ret = rtlStrToReal(newSize, newStr); rtlFree(newStr); return ret; } case type_unicode: len = getLength(type, cur); return rtlUnicodeToReal(len, (UChar const *)cur); case type_utf8: len = getLength(type, cur); return rtlUtf8ToReal(len, (const char *)cur); case type_varstring: return rtlVStrToReal((const char *)cur); case type_varunicode: return rtlUnicodeToReal(rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur); } UNIMPLEMENTED; return 0.0; } int CResultSetCursor::getFetchSize() const { return DEFAULT_FETCH_SIZE; } __int64 CResultSetCursor::getInt(int columnIndex) { if (!isValid()) return 0; const byte * cur = getColumn(columnIndex); ITypeInfo & type = *meta.columns.item(columnIndex).type; unsigned size = type.getSize(); unsigned len = UNKNOWN_LENGTH; switch (type.getTypeCode()) { case type_void: case type_set: case type_table: case type_groupedtable: return 0; case type_boolean: return *((byte *)cur) != 0; case type_int: return getIntFromInt(type, cur, isMappedIndexField(columnIndex)); case type_swapint: return getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex)); case type_packedint: if (type.isSigned()) return rtlGetPackedSigned(cur); else return rtlGetPackedUnsigned(cur); case type_decimal: { DecLock(); if (type.isSigned()) DecPushDecimal(cur, type.getSize(), type.getPrecision()); else DecPushUDecimal(cur, type.getSize(), type.getPrecision()); __int64 ret = DecPopInt64(); DecUnlock(); return ret; } case type_real: if (size == 4) return (__int64) *((float *)cur); return (__int64) *((double *)cur); case type_data: case type_string: len = getLength(type, cur); return rtlStrToInt8(len, (const char *)cur); case type_qstring: { unsigned newSize; char * newStr; len = getLength(type, cur); rtlQStrToStrX(newSize, newStr, len, (const char *)cur); __int64 ret = rtlStrToInt8(newSize, newStr); rtlFree(newStr); return ret; } case type_unicode: len = getLength(type, cur); return rtlUnicodeToInt8(len, (UChar const *)cur); case type_utf8: len = getLength(type, cur); return rtlUtf8ToInt(len, (const char *)cur); case type_varstring: return rtlVStrToInt8((const char *)cur); case type_varunicode: return rtlUnicodeToInt8(rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur); } UNIMPLEMENTED; return 0; } bool CResultSetCursor::getIsAll(int columnIndex) const { if (!isValid()) return false; ITypeInfo & type = *meta.columns.item(columnIndex).type; if (type.getTypeCode() != type_set) return false; const byte * cur = getColumn(columnIndex); return *(bool *)cur; } int CResultSetCursor::getType() { if (getNumRows() != UNKNOWN_NUM_ROWS) return TYPE_SCROLL_INSENSITIVE; return TYPE_FORWARD_ONLY; } const IResultSetMetaData & CResultSetCursor::getMetaData() const { return meta; } __int64 CResultSetCursor::getCurRow() const { return curRow; } __int64 CResultSetCursor::getNumRows() const { return resultSet->getNumRows(); } IDataVal & CResultSetCursor::getRaw(IDataVal &d, int columnIndex) { //MORE: This should work on the raw data! return getBytes(d, columnIndex); } IDataVal & CResultSetCursor::getRawRow(IDataVal &d) { MemoryBuffer temp; if (resultSet->getRawRow(temp, curRow)) d.setLen(temp.toByteArray(), temp.length()); return d; } __int64 CResultSetCursor::translateRow(__int64 row) const { return row; } IStringVal & CResultSetCursor::getString(IStringVal & ret, int columnIndex) { if (!isValid()) { ret.set(""); return ret; } const byte * cur = getColumn(columnIndex); unsigned resultLen; char * resultStr = NULL; ITypeInfo & type = *meta.columns.item(columnIndex).type; unsigned size = type.getSize(); unsigned len; switch (type.getTypeCode()) { case type_void: case type_set: case type_table: case type_groupedtable: ret.set(""); break; case type_boolean: if (*((byte *)cur) != 0) ret.set("1"); else ret.set(""); break; case type_int: { __int64 value = getIntFromInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) rtlInt8ToStrX(resultLen, resultStr, value); else rtlUInt8ToStrX(resultLen, resultStr, (unsigned __int64) value); ret.setLen(resultStr, resultLen); break; } case type_swapint: { __int64 value = getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) rtlInt8ToStrX(resultLen, resultStr, value); else rtlUInt8ToStrX(resultLen, resultStr, (unsigned __int64) value); ret.setLen(resultStr, resultLen); break; } case type_packedint: { if (type.isSigned()) rtlInt8ToStrX(resultLen, resultStr, rtlGetPackedSigned(cur)); else rtlUInt8ToStrX(resultLen, resultStr, rtlGetPackedUnsigned(cur)); ret.setLen(resultStr, resultLen); break; } case type_decimal: { DecLock(); if (type.isSigned()) DecPushDecimal(cur, type.getSize(), type.getPrecision()); else DecPushUDecimal(cur, type.getSize(), type.getPrecision()); DecPopStringX(resultLen, resultStr); DecUnlock(); ret.setLen(resultStr, resultLen); return ret; } case type_real: if (size == 4) rtlRealToStrX(resultLen, resultStr, *(float *)cur); else rtlRealToStrX(resultLen, resultStr, *(double *)cur); ret.setLen(resultStr, resultLen); break; case type_qstring: len = getLength(type, cur); rtlQStrToStrX(resultLen, resultStr, len, (const char *)cur); ret.setLen(resultStr, resultLen); break; case type_data: case type_string: len = getLength(type, cur); ret.setLen((const char *)cur, len); break; case type_unicode: len = getLength(type, cur); rtlUnicodeToStrX(resultLen, resultStr, len, (UChar const *)cur); ret.setLen(resultStr, resultLen); break; case type_utf8: len = getLength(type, cur); rtlUtf8ToStrX(resultLen, resultStr, len, (const char *)cur); ret.setLen(resultStr, resultLen); break; case type_varstring: ret.set((const char *)cur); break; case type_varunicode: rtlUnicodeToStrX(resultLen, resultStr, rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur); ret.setLen(resultStr, resultLen); break; default: UNIMPLEMENTED; } free(resultStr); return ret; } IStringVal & CResultSetCursor::getDisplayText(IStringVal &ret, int columnIndex) { if (!isValid()) { ret.set(""); return ret; } CResultSetColumnInfo & column = meta.columns.item(columnIndex); unsigned flags = column.flag; switch (flags) { case FVFFbeginif: case FVFFendif: case FVFFbeginrecord: case FVFFendrecord: case FVFFdataset: case FVFFset: ret.set(""); return ret; } const byte * cur = getColumn(columnIndex); unsigned resultLen; char * resultStr = NULL; ITypeInfo & type = *column.type; unsigned size = type.getSize(); unsigned len = UNKNOWN_LENGTH; switch (type.getTypeCode()) { case type_boolean: if (*((byte *)cur) != 0) ret.set("true"); else ret.set("false"); break; case type_int: { __int64 value = getIntFromInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) rtlInt8ToStrX(resultLen, resultStr, value); else rtlUInt8ToStrX(resultLen, resultStr, (unsigned __int64) value); ret.setLen(resultStr, resultLen); break; } case type_swapint: { __int64 value = getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) rtlInt8ToStrX(resultLen, resultStr, value); else rtlUInt8ToStrX(resultLen, resultStr, (unsigned __int64) value); ret.setLen(resultStr, resultLen); break; } case type_packedint: { if (type.isSigned()) rtlInt8ToStrX(resultLen, resultStr, rtlGetPackedSigned(cur)); else rtlUInt8ToStrX(resultLen, resultStr, rtlGetPackedUnsigned(cur)); ret.setLen(resultStr, resultLen); break; } case type_decimal: { DecLock(); if (type.isSigned()) DecPushDecimal(cur, type.getSize(), type.getPrecision()); else DecPushUDecimal(cur, type.getSize(), type.getPrecision()); DecPopStringX(resultLen, resultStr); DecUnlock(); ret.setLen(resultStr, resultLen); return ret; } case type_real: if (size == 4) rtlRealToStrX(resultLen, resultStr, *(float *)cur); else rtlRealToStrX(resultLen, resultStr, *(double *)cur); ret.setLen(resultStr, resultLen); break; case type_qstring: len = getLength(type, cur); rtlQStrToStrX(resultLen, resultStr, len, (const char *)cur); ret.setLen(resultStr, resultLen); break; case type_data: { len = getLength(type, cur); StringBuffer temp; while (len--) temp.appendhex(*cur++, true); ret.setLen(temp.str(), temp.length()); break; } case type_string: { len = getLength(type, cur); rtlStrToUtf8X(resultLen, resultStr, len , (const char *)cur); ret.setLen(resultStr, rtlUtf8Size(resultLen, resultStr)); break; } case type_unicode: len = getLength(type, cur); rtlUnicodeToUtf8X(resultLen, resultStr, len, (UChar const *)cur); ret.setLen(resultStr, rtlUtf8Size(resultLen, resultStr)); break; case type_utf8: len = getLength(type, cur); ret.setLen((const char *)cur, rtlUtf8Size(len, cur)); break; case type_varstring: ret.set((const char *)cur); break; case type_varunicode: rtlUnicodeToCodepageX(resultLen, resultStr, rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur, "UTF-8"); ret.setLen(resultStr, resultLen); break; default: UNIMPLEMENTED; } rtlFree(resultStr); return ret; } void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex, const char *tag) { if (!isValid()) return; const char * name = (tag) ? tag : meta.meta->queryXmlTag(columnIndex); CResultSetColumnInfo & column = meta.columns.item(columnIndex); unsigned flags = column.flag; switch (flags) { case FVFFbeginif: case FVFFendif: return; case FVFFbeginrecord: appendXMLOpenTag(out, name); return; case FVFFendrecord: appendXMLCloseTag(out, name); return; } const byte * cur = getColumn(columnIndex); unsigned resultLen; char * resultStr = NULL; ITypeInfo & type = *column.type; unsigned size = type.getSize(); unsigned len = UNKNOWN_LENGTH; switch (type.getTypeCode()) { case type_boolean: outputXmlBool(*((byte *)cur) != 0, name, out); break; case type_int: { __int64 value = getIntFromInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) outputXmlInt((__int64) value, name, out); else outputXmlUInt((unsigned __int64) value, name, out); break; } case type_swapint: { __int64 value = getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) outputXmlInt((__int64) value, name, out); else outputXmlUInt((unsigned __int64) value, name, out); break; } case type_packedint: { if (type.isSigned()) outputXmlInt(rtlGetPackedSigned(cur), name, out); else outputXmlUInt(rtlGetPackedUnsigned(cur), name, out); break; } case type_decimal: if (type.isSigned()) outputXmlDecimal(cur, size, type.getPrecision(), name, out); else outputXmlUDecimal(cur, size, type.getPrecision(), name, out); break; case type_real: if (size == 4) outputXmlReal(*(float *)cur, name, out); else outputXmlReal(*(double *)cur, name, out); break; case type_qstring: len = getLength(type, cur); rtlQStrToStrX(resultLen, resultStr, len, (const char *)cur); outputXmlString(resultLen, resultStr, name, out); break; case type_data: len = getLength(type, cur); outputXmlData(len, cur, name, out); break; case type_string: len = getLength(type, cur); if (meta.isEBCDIC(columnIndex)) { rtlStrToEStrX(resultLen, resultStr, len, (const char *)cur); outputXmlString(resultLen, resultStr, name, out); } else outputXmlString(len, (const char *)cur, name, out); break; case type_unicode: len = getLength(type, cur); outputXmlUnicode(len, (UChar const *)cur, name, out); break; case type_varstring: if (meta.isEBCDIC(columnIndex)) { rtlStrToEStrX(resultLen, resultStr, strlen((const char *)cur), (const char *)cur); outputXmlString(resultLen, resultStr, name, out); } else outputXmlString(strlen((const char *)cur), (const char *)cur, name, out); break; case type_varunicode: outputXmlUnicode(rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur, name, out); break; case type_utf8: len = getLength(type, cur); outputXmlUtf8(len, (const char *)cur, name, out); break; case type_table: case type_groupedtable: { appendXMLOpenTag(out, name); Owned childCursor = getChildren(columnIndex); ForEach(*childCursor) { StringBufferAdaptor adaptor(out); childCursor->getXmlRow(adaptor); } appendXMLCloseTag(out, name); break; } case type_set: { appendXMLOpenTag(out, name); if (getIsAll(columnIndex)) outputXmlSetAll(out); else { Owned childCursor = getChildren(columnIndex); ForEach(*childCursor) { StringBufferAdaptor adaptor(out); childCursor->getXmlItem(adaptor); } } appendXMLCloseTag(out, name); break; } default: UNIMPLEMENTED; } rtlFree(resultStr); } void CResultSetCursor::getXmlAttrText(StringBuffer & out, int columnIndex, const char *tag) { if (!isValid()) return; const char * name = (tag) ? tag : meta.meta->queryXmlTag(columnIndex); if (name && *name=='@') name++; CResultSetColumnInfo & column = meta.columns.item(columnIndex); unsigned flags = column.flag; switch (flags) { case FVFFbeginif: case FVFFendif: case FVFFbeginrecord: case FVFFendrecord: return; } const byte * cur = getColumn(columnIndex); unsigned resultLen; char * resultStr = NULL; ITypeInfo & type = *column.type; unsigned size = type.getSize(); unsigned len = UNKNOWN_LENGTH; switch (type.getTypeCode()) { case type_boolean: outputXmlAttrBool((*((byte *)cur)) != 0, name, out); break; case type_int: { __int64 value = getIntFromInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) outputXmlAttrInt((__int64) value, name, out); else outputXmlAttrUInt((unsigned __int64) value, name, out); break; } case type_swapint: { __int64 value = getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex)); if (type.isSigned()) outputXmlAttrInt((__int64) value, name, out); else outputXmlAttrUInt((unsigned __int64) value, name, out); break; } case type_packedint: { if (type.isSigned()) outputXmlAttrInt(rtlGetPackedSigned(cur), name, out); else outputXmlAttrUInt(rtlGetPackedUnsigned(cur), name, out); break; } case type_decimal: if (type.isSigned()) outputXmlAttrDecimal(cur, size, type.getPrecision(), name, out); else outputXmlAttrUDecimal(cur, size, type.getPrecision(), name, out); break; case type_real: if (size == 4) outputXmlAttrReal(*(float *)cur, name, out); else outputXmlAttrReal(*(double *)cur, name, out); break; case type_qstring: len = getLength(type, cur); rtlQStrToStrX(resultLen, resultStr, len, (const char *)cur); outputXmlAttrString(resultLen, resultStr, name, out); break; case type_data: len = getLength(type, cur); outputXmlAttrData(len, cur, name, out); break; case type_string: len = getLength(type, cur); if (meta.isEBCDIC(columnIndex)) { rtlStrToEStrX(resultLen, resultStr, len, (const char *)cur); outputXmlAttrString(resultLen, resultStr, name, out); } else outputXmlAttrString(len, (const char *)cur, name, out); break; case type_unicode: len = getLength(type, cur); outputXmlAttrUnicode(len, (UChar const *)cur, name, out); break; case type_varstring: if (meta.isEBCDIC(columnIndex)) { rtlStrToEStrX(resultLen, resultStr, strlen((const char *)cur), (const char *)cur); outputXmlAttrString(resultLen, resultStr, name, out); } else outputXmlAttrString(strlen((const char *)cur), (const char *)cur, name, out); break; case type_varunicode: outputXmlAttrUnicode(rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur, name, out); break; case type_utf8: len = getLength(type, cur); outputXmlAttrUtf8(len, (const char *)cur, name, out); break; case type_table: case type_groupedtable: case type_set: break; default: UNIMPLEMENTED; } rtlFree(resultStr); } IStringVal & CResultSetCursor::getXml(IStringVal &ret, int columnIndex) { StringBuffer temp; getXmlText(temp, columnIndex); ret.set(temp.str()); return ret; } IStringVal & CResultSetCursor::getXmlItem(IStringVal & ret) { StringBuffer temp; getXmlText(temp, 0, meta.meta->queryXmlTag()); ret.set(temp.str()); return ret; } //More efficient than above... IStringVal & CResultSetCursor::getXmlRow(IStringVal &ret) { StringBuffer temp; const char *rowtag = meta.meta->queryXmlTag(); if (rowtag && *rowtag) { temp.append('<').append(rowtag); const IntArray &attributes = meta.meta->queryAttrList(); ForEachItemIn(ac, attributes) getXmlAttrText(temp, attributes.item(ac)); temp.append('>'); } unsigned numColumns = meta.getColumnCount(); unsigned ignoreNesting = 0; for (unsigned col = 0; col < numColumns; col++) { unsigned flags = meta.columns.item(col).flag; const char *tag = meta.meta->queryXmlTag(col); if (tag && *tag=='@') continue; switch (flags) { case FVFFbeginif: if (ignoreNesting || !getBoolean(col)) ignoreNesting++; break; case FVFFendif: if (ignoreNesting) ignoreNesting--; break; case FVFFbeginrecord: if (ignoreNesting) ignoreNesting++; else getXmlText(temp, col); break; case FVFFendrecord: if (ignoreNesting) ignoreNesting--; else getXmlText(temp, col); break; case FVFFnone: case FVFFvirtual: case FVFFdataset: case FVFFset: if (ignoreNesting == 0) getXmlText(temp, col); break; } } assertex(ignoreNesting == 0); appendXMLCloseTag(temp, rowtag); ret.set(temp.str()); return ret; } bool CResultSetCursor::isAfterLast() const { return (curRowData.length() == 0) && (getCurRow() != BEFORE_FIRST_ROW); } bool CResultSetCursor::isBeforeFirst() const { return getCurRow() == BEFORE_FIRST_ROW; } bool CResultSetCursor::isFirst() const { return (getCurRow() == 0); } bool CResultSetCursor::isLast() const { if (curRowData.length() == 0) return false; __int64 numRows = getNumRows(); if (numRows != UNKNOWN_NUM_ROWS) return getCurRow() == numRows+1; MemoryBuffer temp; return !resultSet->getRow(temp, translateRow(getCurRow()+1)); } bool CResultSetCursor::isValid() const { return (curRowData.length() != 0); } bool CResultSetCursor::isNull(int columnIndex) const { //MORE: There needs to be some projected extra field to return false; } bool CResultSetCursor::last() { return absolute(getNumRows()-1); } bool CResultSetCursor::next() { return absolute(getCurRow()+1); } bool CResultSetCursor::previous() { return absolute(getCurRow()-1); } bool CResultSetCursor::relative(__int64 rows) { if (getCurRow() + rows < 0) { beforeFirst(); return false; } else return absolute (getCurRow() + rows); } void CResultSetCursor::serialize(IDataVal & d) { MemoryBuffer buffer; resultSet->serialize(buffer); serializeType(buffer); serialize(buffer); d.setLen(buffer.toByteArray(), buffer.length()); } void CResultSetCursor::serializeType(MemoryBuffer & buffer) { buffer.append((unsigned)-1); } void CResultSetCursor::serialize(MemoryBuffer & buffer) { buffer.append(getCurRow()); } void CResultSetCursor::setFetchSize(int rows) { } bool CResultSetCursor::supportsRandomSeek() const { return meta.meta->supportsRandomSeek(); } void CResultSetCursor::beginAccess() { resultSet->onOpen(); } void CResultSetCursor::endAccess() { resultSet->onClose(); } //--------------------------------------------------------------------------- bool IndirectResultSetCursor::absolute(__int64 row) { return queryBase()->absolute(row); } void IndirectResultSetCursor::afterLast() { queryBase()->afterLast(); } void IndirectResultSetCursor::beforeFirst() { queryBase()->beforeFirst(); } bool IndirectResultSetCursor::fetch(__int64 fileoffset) { return queryBase()->fetch(fileoffset); } bool IndirectResultSetCursor::first() { return queryBase()->first(); } bool IndirectResultSetCursor::getBoolean(int columnIndex) { return queryBase()->getBoolean(columnIndex); } IDataVal & IndirectResultSetCursor::getBytes(IDataVal &d, int columnIndex) { return queryBase()->getBytes(d, columnIndex); } IResultSetCursor * IndirectResultSetCursor::getChildren(int columnIndex) const { return queryBase()->getChildren(columnIndex); } xdouble IndirectResultSetCursor::getDouble(int columnIndex) { return queryBase()->getDouble(columnIndex); } int IndirectResultSetCursor::getFetchSize() const { return queryBase()->getFetchSize(); } bool IndirectResultSetCursor::getIsAll(int columnIndex) const { return queryBase()->getIsAll(columnIndex); } __int64 IndirectResultSetCursor::getInt(int columnIndex) { return queryBase()->getInt(columnIndex); } IDataVal & IndirectResultSetCursor::getRaw(IDataVal &d, int columnIndex) { return queryBase()->getRaw(d, columnIndex); } IDataVal & IndirectResultSetCursor::getRawRow(IDataVal &d) { return queryBase()->getRawRow(d); } __int64 IndirectResultSetCursor::getNumRows() const { return queryBase()->getNumRows(); } IStringVal & IndirectResultSetCursor::getString(IStringVal & ret, int columnIndex) { return queryBase()->getString(ret, columnIndex); } bool IndirectResultSetCursor::isAfterLast() const { return queryBase()->isAfterLast(); } bool IndirectResultSetCursor::isBeforeFirst() const { return queryBase()->isBeforeFirst(); } bool IndirectResultSetCursor::isFirst() const { return queryBase()->isFirst(); } bool IndirectResultSetCursor::isLast() const { return queryBase()->isLast(); } bool IndirectResultSetCursor::isNull(int columnIndex) const { return queryBase()->isNull(columnIndex); } bool IndirectResultSetCursor::isValid() const { return queryBase()->isValid(); } bool IndirectResultSetCursor::last() { return queryBase()->last(); } bool IndirectResultSetCursor::next() { return queryBase()->next(); } bool IndirectResultSetCursor::previous() { return queryBase()->previous(); } INewResultSet * IndirectResultSetCursor::queryResultSet() { return queryBase()->queryResultSet(); } bool IndirectResultSetCursor::relative(__int64 rows) { return queryBase()->relative(rows); } void IndirectResultSetCursor::serialize(IDataVal & d) { queryBase()->serialize(d); } IStringVal & IndirectResultSetCursor::getDisplayText(IStringVal &ret, int columnIndex) { return queryBase()->getDisplayText(ret, columnIndex); } IStringVal & IndirectResultSetCursor::getXml(IStringVal & ret, int columnIndex) { return queryBase()->getXml(ret, columnIndex); } IStringVal & IndirectResultSetCursor::getXmlRow(IStringVal &ret) { return queryBase()->getXmlRow(ret); } IStringVal & IndirectResultSetCursor::getXmlItem(IStringVal &ret) { return queryBase()->getXmlItem(ret); } void IndirectResultSetCursor::noteRelatedFileChanged() { queryBase()->noteRelatedFileChanged(); } //--------------------------------------------------------------------------- bool NotifyingResultSetCursor::absolute(__int64 row) { bool ret = IndirectResultSetCursor::absolute(row); notifyChanged(); return ret; } void NotifyingResultSetCursor::afterLast() { IndirectResultSetCursor::afterLast(); notifyChanged(); } void NotifyingResultSetCursor::beforeFirst() { IndirectResultSetCursor::beforeFirst(); notifyChanged(); } bool NotifyingResultSetCursor::fetch(__int64 fileoffset) { bool ret = IndirectResultSetCursor::fetch(fileoffset); notifyChanged(); return ret; } bool NotifyingResultSetCursor::first() { bool ret = IndirectResultSetCursor::first(); notifyChanged(); return ret; } bool NotifyingResultSetCursor::last() { bool ret = IndirectResultSetCursor::last(); notifyChanged(); return ret; } bool NotifyingResultSetCursor::next() { bool ret = IndirectResultSetCursor::next(); notifyChanged(); return ret; } bool NotifyingResultSetCursor::previous() { bool ret = IndirectResultSetCursor::previous(); notifyChanged(); return ret; } bool NotifyingResultSetCursor::relative(__int64 rows) { bool ret = IndirectResultSetCursor::relative(rows); notifyChanged(); return ret; } void NotifyingResultSetCursor::noteRelatedFileChanged() { IndirectResultSetCursor::noteRelatedFileChanged(); notifyChanged(); } void NotifyingResultSetCursor::notifyChanged() { ForEachItemIn(i, dependents) dependents.item(i).noteRelatedFileChanged(); } //--------------------------------------------------------------------------- DelayedFilteredResultSetCursor::DelayedFilteredResultSetCursor(INewResultSet * _resultSet) { filtered.setown(_resultSet->createFiltered()); } void DelayedFilteredResultSetCursor::clearCursor() { cursor.clear(); resultSet.clear(); } void DelayedFilteredResultSetCursor::clearFilters() { clearCursor(); filtered->clearFilters(); } void DelayedFilteredResultSetCursor::ensureFiltered() { } void DelayedFilteredResultSetCursor::noteRelatedFileChanged() { //Don't create a cursor, just to tell the class that the cursor is no longer valid! if (cursor) IndirectResultSetCursor::noteRelatedFileChanged(); } IExtendedResultSetCursor * DelayedFilteredResultSetCursor::queryBase() { //NB: Not thread safe - but none of the interface is if (!cursor) { ensureFiltered(); //MORE: should possibly have the ability to create a null dataset at this point. resultSet.setown(filtered->create()); cursor.setown(static_cast(resultSet->createCursor())); } return cursor; } //--------------------------------------------------------------------------- //was using function templates that didn't use their types in their arguments, but //VC++ fails to process them correctly. template int qsortCompare(const void * _left, const void * _right) { return 0; } typedef int (*qsortFunc)(const void *, const void *); struct StringKeyElement { unsigned index; StringAttr value; }; int qsortCompare_StringKeyElement(const void * _left, const void * _right) { const StringKeyElement * left = (const StringKeyElement *)_left; const StringKeyElement * right = (const StringKeyElement *)_right; int i = strcmp(left->value, right->value); if (i != 0) return i; if (left->index < right->index) return -1; assertex(left->index > right->index); return +1; } qsortFunc qsortCompare(StringKeyElement *) { return qsortCompare_StringKeyElement; } void extractKey(StringKeyElement & element, CResultSetCursor * cursor, unsigned column) { StringAttrAdaptor adaptor(element.value); cursor->getString(adaptor, column); } struct IntegerKeyElement { unsigned index; __int64 value; }; int qsortCompare_IntegerKeyElement(const void * _left, const void * _right) { const IntegerKeyElement * left = (const IntegerKeyElement *)_left; const IntegerKeyElement * right = (const IntegerKeyElement *)_right; if (left->value < right->value) return -1; if (left->value > right->value) return +1; if (left->index < right->index) return -1; assertex(left->index > right->index); return +1; } qsortFunc qsortCompare(IntegerKeyElement *) { return qsortCompare_IntegerKeyElement; } void extractKey(IntegerKeyElement & element, CResultSetCursor * cursor, unsigned column) { element.value = cursor->getInt(column); } struct UnsignedKeyElement { unsigned index; unsigned __int64 value; }; int qsortCompare_UnsignedKeyElement(const void * _left, const void * _right) { const UnsignedKeyElement * left = (const UnsignedKeyElement *)_left; const UnsignedKeyElement * right = (const UnsignedKeyElement *)_right; if (left->value < right->value) return -1; if (left->value > right->value) return +1; if (left->index < right->index) return -1; assertex(left->index > right->index); return +1; } qsortFunc qsortCompare(UnsignedKeyElement *) { return qsortCompare_UnsignedKeyElement; } void extractKey(UnsignedKeyElement & element, CResultSetCursor * cursor, unsigned column) { element.value = (unsigned __int64)cursor->getInt(column); } struct RealKeyElement { unsigned index; double value; }; int qsortCompare_RealKeyElement(const void * _left, const void * _right) { const RealKeyElement * left = (const RealKeyElement *)_left; const RealKeyElement * right = (const RealKeyElement *)_right; if (left->value < right->value) return -1; if (left->value > right->value) return +1; if (left->index < right->index) return -1; assertex(left->index > right->index); return +1; } qsortFunc qsortCompare(RealKeyElement *) { return qsortCompare_RealKeyElement; } void extractKey(RealKeyElement & element, CResultSetCursor * cursor, unsigned column) { element.value = cursor->getDouble(column); } template void buildCursorIndex(unsigned & numElements, unsigned * & elements, CResultSetCursor * cursor, unsigned column, bool descend, KEYELEMENT *) { __int64 max64 = cursor->getNumRows(); assertex(max64 <= MAX_SORT_ELEMENTS); unsigned max = (unsigned)max64; KEYELEMENT * keys = new KEYELEMENT[max]; for (unsigned idx=0; idx < max; idx++) { cursor->absolute(idx); keys[idx].index = idx; extractKey(keys[idx], cursor, column); } qsort(keys, max, sizeof(KEYELEMENT), qsortCompare((KEYELEMENT *)0)); numElements = max; elements = (unsigned *)malloc(max * sizeof(unsigned)); if (descend) { for (unsigned idx=0; idx < max; idx++) elements[max-idx-1] = keys[idx].index; } else { for (unsigned idx=0; idx < max; idx++) elements[idx] = keys[idx].index; } delete [] keys; } CResultSetSortedCursor::CResultSetSortedCursor(const CResultSetMetaData & _meta, IExtendedNewResultSet * _resultSet, unsigned _column, bool _desc) : CResultSetCursor(_meta, _resultSet, false) { numEntries = 0; elements = NULL; lastRow = 0; column = _column; desc = _desc; buildIndex(); } CResultSetSortedCursor::CResultSetSortedCursor(const CResultSetMetaData & _meta, IExtendedNewResultSet * _resultSet, unsigned _column, bool _desc, MemoryBuffer & buffer) : CResultSetCursor(_meta, _resultSet, false) { numEntries = 0; elements = NULL; lastRow = 0; column = _column; desc = _desc; buildIndex(); buffer.read(curRow); buffer.read(lastRow); absolute(lastRow); } CResultSetSortedCursor::~CResultSetSortedCursor() { free(elements); } void CResultSetSortedCursor::buildIndex() { Owned cursor = new CResultSetCursor(meta, resultSet, false); switch (meta.getColumnDisplayType(column)) { case TypeBoolean: case TypeInteger: buildCursorIndex(numEntries, elements, cursor, column, desc, (IntegerKeyElement*)0); break; case TypeUnsignedInteger: buildCursorIndex(numEntries, elements, cursor, column, desc, (UnsignedKeyElement*)0); break; case TypeReal: buildCursorIndex(numEntries, elements, cursor, column, desc, (RealKeyElement*)0); break; case TypeString: case TypeData: case TypeUnicode: //MORE! buildCursorIndex(numEntries, elements, cursor, column, desc, (StringKeyElement*)0); break; default: UNIMPLEMENTED; // Should have been translated to one of the above by this point... } } bool CResultSetSortedCursor::absolute(__int64 row) { lastRow = row; return CResultSetCursor::absolute(translateRow(row)); } __int64 CResultSetSortedCursor::getCurRow() const { return lastRow; } __int64 CResultSetSortedCursor::translateRow(__int64 row) const { if ((row >= 0) && (row < numEntries)) return elements[row]; return row; } void CResultSetSortedCursor::serializeType(MemoryBuffer & buffer) { buffer.append((unsigned)column); buffer.append(desc); } void CResultSetSortedCursor::serialize(MemoryBuffer & buffer) { CResultSetCursor::serialize(buffer); buffer.append(lastRow); } //--------------------------------------------------------------------------- inline byte hex2digit(char c) { if (c >= 'a') return (c - 'a' + 10); else if (c >= 'A') return (c - 'A' + 10); return (c - '0'); } inline byte getHexPair(const char * s) { return hex2digit(s[0]) << 4 | hex2digit(s[1]); } static unsigned getSubstringMatchLength(size32_t len, const void * data) { const char * inbuff = (const char *)data; unsigned trimLen = rtlTrimStrLen(len, inbuff); if (trimLen && (inbuff[trimLen-1] == '*')) return rtlUtf8Length(trimLen-1, inbuff); return FullStringMatch; } void CColumnFilter::addValue(unsigned sizeText, const char * text) { unsigned lenText = rtlUtf8Length(sizeText, text); unsigned size = type->getSize(); MemoryAttrItem * next = new MemoryAttrItem; type_t tc = type->getTypeCode(); if (isMappedIndexField) { if (__BYTE_ORDER == __LITTLE_ENDIAN) { if (tc == type_int) tc = type_swapint; } else { if (tc == type_swapint) tc = type_int; } } //sublen is the number of characters (not the size) subLen = getSubstringMatchLength(sizeText, text); switch (tc) { case type_void: case type_set: case type_table: case type_groupedtable: break; case type_boolean: { byte value = rtlCsvStrToBool(lenText, text); next->set(sizeof(value), &value); break; } break; case type_int: { __int64 value = type->isSigned() ? rtlStrToInt8(lenText, text) : (__int64)rtlStrToUInt8(lenText, text); if (isMappedIndexField && type->isSigned()) value += getIntBias(size); if (__BYTE_ORDER == __LITTLE_ENDIAN) next->set(size, &value); else next->set(size, ((const byte *)&value)+(sizeof(value)-size)); break; } case type_swapint: { __int64 value = type->isSigned() ? rtlStrToInt8(lenText, text) : (__int64)rtlStrToUInt8(lenText, text); if (isMappedIndexField && type->isSigned()) value += getIntBias(size); _rev8((char *)&value); if (__BYTE_ORDER == __LITTLE_ENDIAN) next->set(size, ((const byte *)&value)+(sizeof(value)-size)); else next->set(size, &value); break; } case type_packedint: { void * target = next->allocate(size); if (type->isSigned()) rtlSetPackedSigned(target, rtlStrToInt8(lenText, text)); else rtlSetPackedUnsigned(target, rtlStrToUInt8(lenText, text)); break; } case type_decimal: { void * target = next->allocate(size); DecLock(); rtlDecPushUtf8(lenText, text); if (type->isSigned()) DecPopDecimal(target, size, type->getPrecision()); else DecPopUDecimal(target, size, type->getPrecision()); DecUnlock(); break; } case type_real: { if (size == 4) { float value = (float)rtlStrToReal(lenText, text); next->set(sizeof(value), &value); } else { double value = rtlStrToReal(lenText, text); next->set(sizeof(value), &value); } break; } case type_qstring: { if (lenText > subLen) lenText = subLen; char * target = (char *)next->allocate(rtlQStrSize(lenText)); rtlStrToQStr(lenText, target, lenText, text); break; } break; case type_data: { if (lenText > subLen) lenText = subLen; if (subLen != FullStringMatch) subLen /= 2; unsigned max = lenText/2; char * target = (char *)next->allocate(max); for (unsigned i=0; i subLen) lenText = subLen; char * target = (char *)next->allocate(lenText); rtlUtf8ToStr(lenText, target, lenText, text); break; } case type_unicode: { if (lenText > subLen) lenText = subLen; UChar * target = (UChar *)next->allocate(lenText*2); rtlUtf8ToUnicode(lenText, target, lenText, text); break; } break; case type_varstring: { if (lenText > subLen) lenText = subLen; next->set(lenText+1, text); break; } case type_varunicode: { UChar * target = (UChar *)next->allocate(lenText*2+2); rtlUtf8ToUnicode(lenText, target, lenText, text); target[lenText] = 0; break; } break; case type_utf8: { if (lenText > subLen) sizeText = rtlUtf8Size(subLen, text); next->set(sizeText, text); //Should it be utf8 or ascii coming in? //char * target = (char *)next->allocate(lenText*4); //rtlStrToUtf8(lenText, target, lenText, text); break; } break; default: UNIMPLEMENTED; } if (next->length()) values.append(*next); else next->Release(); } void CColumnFilter::deserialize(MemoryBuffer & buffer) { unsigned num; buffer.read(num); for (unsigned i= 0; i < num; i++) { MemoryAttrItem * next = new MemoryAttrItem; ::deserialize(buffer, *next); values.append(*next); } } bool CColumnFilter::optimizeFilter(IFvDataSource * dataSource) { if (values.ordinality() == 0) return true; optimized = true; ForEachItemIn(i, values) { MemoryAttr & cur = values.item(i); if (!dataSource->addFilter(whichColumn, subLen, cur.length(), cur.get())) optimized = false; } return optimized; } bool CColumnFilter::matches(const byte * rowValue, unsigned valueSize, const byte * value) { unsigned size = type->getSize(); unsigned len; switch (type->getTypeCode()) { case type_void: case type_set: case type_table: case type_groupedtable: return true; case type_boolean: case type_int: case type_swapint: case type_packedint: return memcmp(rowValue, value, size) == 0; case type_decimal: if (type->isSigned()) return DecCompareDecimal(size, rowValue, value) == 0; return DecCompareUDecimal(size, rowValue, value) == 0; case type_real: if (size == 4) return *(float *)rowValue == *(float *)value; return *(double *)rowValue == *(double *)value; case type_qstring: len = getLength(*type, rowValue); if ((subLen != FullStringMatch) && (len > subLen)) len = subLen; return rtlCompareQStrQStr(len, rowValue, rtlQStrLength(valueSize), value) == 0; case type_data: len = getLength(*type, rowValue); if ((subLen != FullStringMatch) && (len > subLen)) len = subLen; return rtlCompareDataData(len, (const char *)rowValue, valueSize, (const char *)value) == 0; case type_string: len = getLength(*type, rowValue); if ((subLen != FullStringMatch) && (len > subLen)) len = subLen; return rtlCompareStrStr(len, (const char *)rowValue, valueSize, (const char *)value) == 0; case type_unicode: len = getLength(*type, rowValue); return rtlCompareUnicodeUnicode(len, (const UChar *)rowValue, valueSize/2, (const UChar *)value, "") == 0; case type_utf8: len = getLength(*type, rowValue); if ((subLen != FullStringMatch) && (len > subLen)) len = subLen; return rtlCompareUtf8Utf8(len, (const char *)rowValue, rtlUtf8Length(valueSize, value), (const char *)value, "") == 0; case type_varstring: return strcmp((const char *)rowValue, (const char *)value) != 0; case type_varunicode: return rtlCompareVUnicodeVUnicode((const UChar *)rowValue, (const UChar *)value, "") == 0; default: UNIMPLEMENTED; } } bool CColumnFilter::isValid(const byte * rowValue, const unsigned * offsets) { if (optimized || values.ordinality() == 0) return true; const byte * columnValue = rowValue + offsets[whichColumn]; ForEachItemIn(i, values) { MemoryAttr & cur = values.item(i); if (matches(columnValue, cur.length(), (const byte *)cur.get())) return true; } return false; } void CColumnFilter::serialize(MemoryBuffer & buffer) { buffer.append((unsigned)values.ordinality()); ForEachItemIn(i, values) ::serialize(buffer, values.item(i)); } //--------------------------------------------------------------------------- CFilteredResultSet::CFilteredResultSet(IExtendedNewResultSet * _other, ColumnFilterArray & _filters) : CIndirectResultSet(_other) { appendArray(filters, _filters); initExtra(); } #if 0 CFilteredResultSet::CFilteredResultSet(const CResultSetMetaData & _meta, CResultSet * _resultSet, MemoryBuffer & buffer) : CResultSetCursor(_meta, _resultSet, false) { unsigned numFilters; buffer.read(numFilters); for (unsigned i=0; i< numFilters; i++) { CColumnFilter & next = * new CColumnFilter(meta.queryType(i)); next.deserialize(buffer); filters.append(next); } initExtra(); buffer.read(curRow); buffer.read(lastRow); absolute(lastRow); } #endif CFilteredResultSet::~CFilteredResultSet() { delete [] offsets; } bool CFilteredResultSet::getRow(MemoryBuffer & out, __int64 row) { __int64 newRow = translateRow(row); if (newRow < 0) return false; return CIndirectResultSet::getRow(out, newRow); } bool CFilteredResultSet::getRawRow(MemoryBuffer & out, __int64 row) { __int64 newRow = translateRow(row); if (newRow < 0) return false; return CIndirectResultSet::getRawRow(out, newRow); } __int64 CFilteredResultSet::getNumRows() const { if (readAll) return validPositions.ordinality(); return UNKNOWN_NUM_ROWS; } bool CFilteredResultSet::rowMatchesFilter(const byte * row) { if (!meta.isFixedSize()) meta.calcFieldOffsets(row, offsets); ForEachItemIn(i, filters) { if (!filters.item(i).isValid(row, offsets)) return false; } return true; } __int64 CFilteredResultSet::translateRow(__int64 row) { if (row < 0) return row; if (row > MAX_FILTER_ELEMENTS) return AFTER_LAST_ROW; // would it be better to throw an error? if (!readAll && (row >= validPositions.ordinality())) { unsigned __int64 nextPos = 0; if (validPositions.ordinality()) nextPos = validPositions.tos()+1; MemoryBuffer tempBuffer; unsigned startTime = msTick(); while (row >= validPositions.ordinality()) { if (!CIndirectResultSet::getRow(tempBuffer.clear(), nextPos)) { readAll = true; break; } if (rowMatchesFilter((byte *)tempBuffer.toByteArray())) { validPositions.append(nextPos); } else { unsigned timeTaken = msTick() - startTime; if (timeTaken > MAX_SKIP_TIME) throwError1(FVERR_FilterTooRestrictive, timeTaken/1000); } nextPos++; } } if ((unsigned)row < validPositions.ordinality()) return validPositions.item((unsigned)row); return AFTER_LAST_ROW; } void CFilteredResultSet::serialize(MemoryBuffer & buffer) { CIndirectResultSet::serialize(buffer); buffer.append((unsigned)filters.ordinality()); ForEachItemIn(i, filters) filters.item(i).serialize(buffer); } void CFilteredResultSet::initExtra() { readAll = false; offsets = new unsigned[meta.getColumnCount()+1]; if (meta.isFixedSize()) meta.calcFieldOffsets(NULL, offsets); } //--------------------------------------------------------------------------- CFetchFilteredResultSet::CFetchFilteredResultSet(IExtendedNewResultSet * _parent, const CStrColumnFilter & _filter) : CIndirectResultSet(_parent) { ForEachItemIn(i, _filter.values) { const MemoryAttrItem & value = _filter.values.item(i); unsigned __int64 offset = rtlStrToUInt8(value.length(), static_cast(value.get())); validOffsets.append(offset); } } bool CFetchFilteredResultSet::getRow(MemoryBuffer & out, __int64 row) { if (!validOffsets.isItem((unsigned)row)) return false; return CIndirectResultSet::fetch(out, validOffsets.item((unsigned)row)); } bool CFetchFilteredResultSet::getRawRow(MemoryBuffer & out, __int64 row) { if (!validOffsets.isItem((unsigned)row)) return false; return CIndirectResultSet::fetchRaw(out, validOffsets.item((unsigned)row)); } __int64 CFetchFilteredResultSet::getNumRows() const { return validOffsets.ordinality(); } void CFetchFilteredResultSet::serialize(MemoryBuffer & buffer) { CIndirectResultSet::serialize(buffer); buffer.append((unsigned)validOffsets.ordinality()); ForEachItemIn(i, validOffsets) buffer.append(validOffsets.item(i)); } //--------------------------------------------------------------------------- CFilteredResultSetBuilder::CFilteredResultSetBuilder(IExtendedNewResultSet * _resultSet) { resultSet.set(_resultSet); } INewResultSet * CFilteredResultSetBuilder::create() { Linked baseResultSet = resultSet; const CResultSetMetaData & meta = static_cast(resultSet->getMetaData()); //Check for fetch filter, and if present apply that first unsigned numColumns = meta.getColumnCount(); if (filters.isItem(numColumns-1) && meta.isVirtual(numColumns-1)) { CStrColumnFilter & cur = filters.item(numColumns-1); if (cur.values.ordinality()) { baseResultSet.setown(new CFetchFilteredResultSet(resultSet, cur)); cur.values.kill(); } } Owned cloned = baseResultSet->cloneForFilter(); IFvDataSource * dataSource = cloned ? cloned->queryDataSource() : NULL; ColumnFilterArray rawFilters; ForEachItemIn(columnIndex, filters) { CStrColumnFilter & cur = filters.item(columnIndex); if (cur.values.ordinality()) { Owned next = new CColumnFilter(columnIndex, meta.queryType(columnIndex), resultSet->isMappedIndexField(columnIndex)); ForEachItemIn(j, cur.values) { MemoryAttrItem & curValue = cur.values.item(j); next->addValue(curValue.length(), reinterpret_cast(curValue.get())); } if (!cloned || !next->optimizeFilter(dataSource)) rawFilters.append(*next.getClear()); } } if (cloned) { dataSource->applyFilter(); if (rawFilters.ordinality() == 0) return cloned.getClear(); return new CFilteredResultSet(cloned, rawFilters); } if (rawFilters.ordinality() == 0) return baseResultSet.getClear(); return new CFilteredResultSet(baseResultSet, rawFilters); } void CFilteredResultSetBuilder::addFilter(unsigned columnIndex, const char * value) { addFilter(columnIndex, strlen(value), value); } void CFilteredResultSetBuilder::addFilter(unsigned columnIndex, unsigned len, const char * value) { assertex(columnIndex < (unsigned)resultSet->getMetaData().getColumnCount()); while (filters.ordinality() <= columnIndex) filters.append(*new CStrColumnFilter()); filters.item(columnIndex).addValue(len, value); } void CFilteredResultSetBuilder::addNaturalFilter(unsigned columnIndex, unsigned len, const char * value) { const CResultSetMetaData & meta = static_cast(resultSet->getMetaData()); assertex(columnIndex < (unsigned)meta.getColumnCount()); const CResultSetColumnInfo & column = meta.queryColumn(columnIndex); if (column.setTransforms.ordinality()) { MemoryAttr source(len, value); MemoryAttr translated; translateValue(translated, source, column.setTransforms); addFilter(columnIndex, translated.length(), static_cast(translated.get())); } else addFilter(columnIndex, len, value); } void CFilteredResultSetBuilder::clearFilter(unsigned columnIndex) { assertex(columnIndex < (unsigned)resultSet->getMetaData().getColumnCount()); if (filters.isItem(columnIndex)) filters.item(columnIndex).clear(); } void CFilteredResultSetBuilder::clearFilters() { filters.kill(); } //--------------------------------------------------------------------------- CResultSetFactoryBase::CResultSetFactoryBase(const char * _username, const char * _password) { username.set(_username); password.set(_password); } CResultSetFactoryBase::CResultSetFactoryBase(ISecManager &secmgr, ISecUser &secuser) { secMgr.set(&secmgr); secUser.set(&secuser); username.set(secuser.getName()); password.set(secuser.credentials().getPassword()); } //--------------------------------------------------------------------------- CResultSetFactory::CResultSetFactory(const char * _username, const char * _password) : CResultSetFactoryBase(_username, _password) { } CResultSetFactory::CResultSetFactory(ISecManager &secmgr, ISecUser &secuser) : CResultSetFactoryBase(secmgr, secuser) { } IResultSet * CResultSetFactory::createResultSet(IConstWUResult * wuResult, const char * wuid) { Owned ds = createDataSource(wuResult, wuid, username, password); if (ds) { Owned result = createResultSet(ds, (wuResult->getResultFormat() == ResultFormatXml)); return result->createOldStyleCursor(); } return NULL; } IDistributedFile * CResultSetFactory::lookupLogicalName(const char * logicalName) { Owned udesc; if(username != NULL && *username != '\0') { udesc.setown(createUserDescriptor()); udesc->set(username, password); } Owned df = queryDistributedFileDirectory().lookup(logicalName, udesc.get()); if (!df) throwError1(FVERR_CouldNotResolveX, logicalName); return df.getClear(); } IResultSet * CResultSetFactory::createFileResultSet(const char * logicalName, const char * cluster) { Owned df = lookupLogicalName(logicalName); Owned ds = createFileDataSource(df, logicalName, cluster, username, password); if (ds) { Owned result = createResultSet(ds, false); result->setColumnMapping(df); return result->createOldStyleCursor(); } return NULL; }; INewResultSet * CResultSetFactory::createNewResultSet(IConstWUResult * wuResult, const char * wuid) { Owned ds = createDataSource(wuResult, wuid, username, password); if (ds) return createResultSet(ds, (wuResult->getResultFormat() == ResultFormatXml)); return NULL; } INewResultSet * CResultSetFactory::createNewFileResultSet(const char * logicalName, const char * cluster) { Owned df = lookupLogicalName(logicalName); Owned ds = createFileDataSource(df, logicalName, cluster, username, password); if (ds) { Owned result = createResultSet(ds, false); result->setColumnMapping(df); return result.getClear(); } return NULL; } INewResultSet * CResultSetFactory::createNewResultSet(const char * wuid, unsigned sequence, const char * name) { Owned wuResult = (secMgr) ? secResolveResult(*secMgr, *secUser, wuid, sequence, name) : resolveResult(wuid, sequence, name); return (wuResult) ? createNewResultSet(wuResult, wuid) : NULL; } INewResultSet * CResultSetFactory::createNewFileResultSet(const char * logicalFile) { return createNewFileResultSet(logicalFile, NULL); } CResultSet * CResultSetFactory::createResultSet(IFvDataSource * ds, bool _useXPath) { //MORE: Save in a hash table, which times out after a certain period... return new CResultSet(ds, _useXPath); } IResultSetMetaData * CResultSetFactory::createResultSetMeta(IConstWUResult * wuResult) { return new CResultSetMetaData(createMetaData(wuResult), false); } IResultSetMetaData * CResultSetFactory::createResultSetMeta(const char * wuid, unsigned sequence, const char * name) { Owned wuResult = (secMgr) ? secResolveResult(*secMgr, *secUser, wuid, sequence, name) : resolveResult(wuid, sequence, name); return (wuResult) ? createResultSetMeta(wuResult) : NULL; } //--------------------------------------------------------------------------- CRemoteResultSetFactory::CRemoteResultSetFactory(const char * remoteServer, const char * _username, const char * _password) : CResultSetFactoryBase(_username, _password) { serverEP.set(remoteServer); } CRemoteResultSetFactory::CRemoteResultSetFactory(const char * remoteServer, ISecManager &secmgr, ISecUser &secuser) : CResultSetFactoryBase(secmgr, secuser) { serverEP.set(remoteServer); } IResultSet * CRemoteResultSetFactory::createResultSet(IConstWUResult * wuResult, const char * wuid) { SCMStringBuffer name; wuResult->getResultName(name); Owned ds = createRemoteDataSource(serverEP, username, password, wuid, wuResult->getResultSequence(), name.length() ? name.str() : NULL); if (ds) { Owned result = new CResultSet(ds, false); return result->createOldStyleCursor(); } return NULL; } IResultSet * CRemoteResultSetFactory::createFileResultSet(const char * logicalName, const char * cluster) { Owned ds = createRemoteFileDataSource(serverEP, username, password, logicalName); if (ds) { Owned result = new CResultSet(ds, false); return result->createOldStyleCursor(); } return NULL; }; INewResultSet * CRemoteResultSetFactory::createNewResultSet(IConstWUResult * wuResult, const char * wuid) { SCMStringBuffer name; wuResult->getResultName(name); Owned ds = createRemoteDataSource(serverEP, username, password, wuid, wuResult->getResultSequence(), name.length() ? name.str() : NULL); if (ds) return new CResultSet(ds, false); return NULL; } INewResultSet * CRemoteResultSetFactory::createNewFileResultSet(const char * logicalName, const char * cluster) { Owned ds = createRemoteFileDataSource(serverEP, username, password, logicalName); if (ds) return new CResultSet(ds, false); return NULL; } INewResultSet* CRemoteResultSetFactory::createNewResultSet(const char * wuid, unsigned sequence, const char * name) { Owned ds = createRemoteDataSource(serverEP, username, password, wuid, sequence, name); if (ds) return new CResultSet(ds, false); return NULL; } INewResultSet* CRemoteResultSetFactory::createNewFileResultSet(const char * logicalName) { Owned ds = createRemoteFileDataSource(serverEP, username, password, logicalName); if (ds) return new CResultSet(ds, false); return NULL; } IResultSetMetaData * CRemoteResultSetFactory::createResultSetMeta(IConstWUResult * wuResult) { UNIMPLEMENTED; } IResultSetMetaData * CRemoteResultSetFactory::createResultSetMeta(const char * wuid, unsigned sequence, const char * name) { UNIMPLEMENTED; } //--------------------------------------------------------------------------- extern "C" FILEVIEW_API IResultSet* createResultSet(IResultSetFactory & factory, IStringVal & error, IConstWUResult * wuResult, const char * wuid); extern "C" FILEVIEW_API IResultSet* createFileResultSet(IResultSetFactory & factory, IStringVal & error, const char * logicalFile, const char * queue, const char * cluster); IResultSet* createResultSet(IResultSetFactory & factory, IStringVal & error, IConstWUResult * wuResult, const char * wuid) { try { return factory.createResultSet(wuResult, wuid); } catch (IException * e) { StringBuffer s; error.set(e->errorMessage(s).str()); e->Release(); return NULL; } } IResultSet* createFileResultSet(IResultSetFactory & factory, IStringVal & error, const char * logicalFile, const char * cluster) { try { return factory.createFileResultSet(logicalFile, cluster); } catch (IException * e) { StringBuffer s; error.set(e->errorMessage(s).str()); e->Release(); return NULL; } } INewResultSet* createNewResultSet(IResultSetFactory & factory, IStringVal & error, IConstWUResult * wuResult, const char * wuid) { try { return factory.createNewResultSet(wuResult, wuid); } catch (IException * e) { StringBuffer s; error.set(e->errorMessage(s).str()); e->Release(); return NULL; } } INewResultSet* createNewFileResultSet(IResultSetFactory & factory, IStringVal & error, const char * logicalFile, const char * cluster) { try { return factory.createNewFileResultSet(logicalFile, cluster); } catch (IException * e) { StringBuffer s; error.set(e->errorMessage(s).str()); e->Release(); return NULL; } } INewResultSet* createNewResultSetSeqName(IResultSetFactory & factory, IStringVal & error, const char * wuid, unsigned sequence, const char * name) { try { return factory.createNewResultSet(wuid, sequence, name); } catch (IException * e) { StringBuffer s; error.set(e->errorMessage(s).str()); e->Release(); return NULL; } } //--------------------------------------------------------------------------- //MORE: There should be an option to create a proxy-based IFileView so that //i) formatting etc. can be done remotely //ii) cacching and sort order calculation can be done remotely. // Should still cache the last n rows locally (raw/non-raw). IResultSetFactory * getResultSetFactory(const char * username, const char * password) { return new CResultSetFactory(username, password); } IResultSetFactory * getSecResultSetFactory(ISecManager &secmgr, ISecUser &secuser) { return new CResultSetFactory(secmgr, secuser); } IResultSetFactory * getRemoteResultSetFactory(const char * remoteServer, const char * username, const char * password) { return new CRemoteResultSetFactory(remoteServer, username, password); } extern "C" FILEVIEW_API void getNumRows(IResultSet * rs, IStringVal &ret) { if (rs) { char buff[64]; #ifdef _WIN32 numtostr(buff, rs->getNumRows()); #else sprintf(buff, "%"I64F"d", rs->getNumRows()); #endif ret.set(buff); } } int findResultSetColumn(const INewResultSet * results, const char * columnName) { const IResultSetMetaData & meta = results->getMetaData(); SCMStringBuffer s; for(int i = 0; i < meta.getColumnCount(); i++) { s.clear(); if(!stricmp(columnName, meta.getColumnLabel(s, i).str())) return i; } return -1; } extern "C" FILEVIEW_API unsigned getResultCursorXml(IStringVal & ret, IResultSetCursor * cursor, const char * name, unsigned start, unsigned count, const char * schemaName) { StringBuffer text; if (schemaName) { text.append(""); const IResultSetMetaData & meta = cursor->queryResultSet()->getMetaData(); StringBufferAdaptor adaptor(text); meta.getXmlSchema(adaptor, false); text.append("").newline(); } text.append("").newline(); unsigned c=0; for(bool ok=cursor->absolute(start);ok;ok=cursor->next()) { text.append(" "); StringBufferAdaptor adaptor(text); cursor->getXmlRow(adaptor); text.newline(); c++; if(count && c>=count) break; } text.append("").newline(); ret.set(text.str()); return c; } extern "C" FILEVIEW_API unsigned getResultXml(IStringVal & ret, INewResultSet * result, const char* name,unsigned start, unsigned count, const char * schemaName) { Owned cursor = result->createCursor(); return getResultCursorXml(ret, cursor, name, start, count, schemaName); } extern "C" FILEVIEW_API unsigned getResultCursorBin(MemoryBuffer & ret, IResultSetCursor * cursor, unsigned start, unsigned count) { const IResultSetMetaData & meta = cursor->queryResultSet()->getMetaData(); unsigned numCols = meta.getColumnCount(); unsigned c=0; for(bool ok=cursor->absolute(start);ok;ok=cursor->next()) { for (unsigned col=0; col < numCols; col++) cursor->getRaw(MemoryBuffer2IDataVal(ret), col); c++; if(count && c>=count) break; } return c; } extern "C" FILEVIEW_API unsigned getResultBin(MemoryBuffer & ret, INewResultSet * result, unsigned start, unsigned count) { Owned cursor = result->createCursor(); return getResultCursorBin(ret, cursor, start, count); } extern "C" FILEVIEW_API IStringVal& getFullWorkUnitResultsXML(const char *username, const char *password, const IConstWorkUnit *cw, IStringVal &str, bool inclschema, WUExceptionSeverity minSeverity, bool noroot) { SCMStringBuffer wuid; cw->getWuid(wuid); StringBuffer result; if (!noroot) result.append(""); Owned exceptions = &cw->getExceptions(); ForEach(*exceptions) { if (exceptions->query().getSeverity()>=minSeverity) { SCMStringBuffer x, y; exceptions->query().getExceptionSource(x); exceptions->query().getExceptionMessage(y); result.append(""); encodeUtf8XML(x.str(), result); result.append(""); encodeUtf8XML(y.str(), result); result.append(""); } } Owned factory = getResultSetFactory(username, password); switch (cw->getState()) { case WUStateCompleted: case WUStateWait: { Owned results = &cw->getResults(); ForEach(*results) { IConstWUResult &ds = results->query(); if (ds.getResultSequence()>=0) { SCMStringBuffer resultXML, name; ds.getResultName(name); Owned nr = factory->createNewResultSet(&ds, wuid.str()); getResultXml(resultXML, nr.get(), name.str(), 0, 0, inclschema ? name.str() : NULL); result.append(resultXML); } } } break; case WUStateAborted: result.append("SystemQuery aborted by operator"); break; } if (!noroot) result.append(""); str.set(result.str()); return str; }