/** * Perform operator-specific checks of input and return the shape of the output. Currently, * the output array must exist. * @param schemas the shapes of the input arrays * @param query the query context */ ArrayDesc inferSchema(std::vector< ArrayDesc> schemas, shared_ptr< Query> query) { assert(schemas.size() == 1); assert(_parameters.size() == 1); string arrayName = ((shared_ptr<OperatorParamReference>&)_parameters[0])->getObjectName(); ArrayDesc const& srcDesc = schemas[0]; //Ensure attributes names uniqueness. ArrayDesc dstDesc; if (!SystemCatalog::getInstance()->getArrayDesc(arrayName, dstDesc, false)) { throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_ARRAY_DOESNT_EXIST) << arrayName; } if(dstDesc.isImmutable()) { throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_ILLEGAL_OPERATION) << "Target of INSERT must be a mutable array"; } Dimensions const& srcDims = srcDesc.getDimensions(); Dimensions const& dstDims = dstDesc.getDimensions(); if (srcDims.size() != dstDims.size()) { //TODO: this will get lifted when we allow redimension+insert in the same op //and when we DO implement redimension+insert - we will need to match attributes/dimensions by name, not position. throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_ILLEGAL_OPERATION) << "Temporary restriction: target of INSERT must have same dimensions as the source"; } for (size_t i = 0, n = srcDims.size(); i < n; i++) { if( srcDims[i].getType() != TID_INT64 || dstDims[i].getType() != TID_INT64) { throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_ILLEGAL_OPERATION) << "Temporary restriction: INSERT only supports integer dimensions"; } //TODO: we can also allow arrays that are smaller whose length is not evenly divided by chunk interval //but then we have to detect "edge chunks" and rewrite them cleverly if( srcDims[i].getStartMin() != dstDims[i].getStartMin() || srcDims[i].getChunkInterval() != dstDims[i].getChunkInterval() || srcDims[i].getChunkOverlap() != dstDims[i].getChunkOverlap() || srcDims[i].getEndMax() > dstDims[i].getEndMax() || ( srcDims[i].getEndMax() < dstDims[i].getEndMax() && srcDims[i].getLength() % srcDims[i].getChunkInterval() != 0)) { throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_DIMENSIONS_DONT_MATCH) << srcDims[i].getBaseName() << dstDims[i].getBaseName(); } } Attributes const& srcAttrs = srcDesc.getAttributes(true); Attributes const& dstAttrs = dstDesc.getAttributes(true); if (srcAttrs.size() != dstAttrs.size()) { throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_ILLEGAL_OPERATION) << "Temporary restriction: target of INSERT must have same attributes as the source"; } for (size_t i = 0, n = srcAttrs.size(); i < n; i++) { if(srcAttrs[i].getType() != dstAttrs[i].getType()) { throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_WRONG_ATTRIBUTE_TYPE) << srcAttrs[i].getName() << srcAttrs[i].getType() << dstAttrs[i].getType(); } //can't store nulls into a non-nullable attribute if(!dstAttrs[i].isNullable() && srcAttrs[i].isNullable()) { throw USER_EXCEPTION(SCIDB_SE_INFER_SCHEMA, SCIDB_LE_WRONG_ATTRIBUTE_FLAGS) << srcAttrs[i].getName(); } } //Note: let us NOT add arrayID numbers to the schema - because we do not have our ArrayID yet. //We will get our ArrayID when we execute and create the array. Until then - don't bother. //Old store code adds the arrayID to the schema - but that's the arrayID of the previous version, //not the new version created by the op. A dangerous fallacy - stupid and unnecessary. return ArrayDesc(arrayName, dstDesc.getAttributes(), dstDesc.getDimensions(), dstDesc.getFlags()); }