/*##############################################################################
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 "jlib.hpp"
#include "jexcept.hpp"
#include "jmisc.hpp"
#include "workunit.hpp"
#include "hql.hpp"
#include "hqlexpr.hpp"
#include "hqlfold.hpp"
#include "hqlstmt.hpp"
#include "hqltrans.ipp"
#include "hqlutil.hpp"
#include "hqlattr.hpp"
#include "hqlcatom.hpp"
#include "hqlfunc.hpp"
#include "hqlcpp.ipp"
#include "hqlcpputil.hpp"
//===========================================================================
static ITypeInfo * cachedVoidType;
static IHqlExpression * cachedBoolValue[2];
static IHqlExpression * cachedZero;
static IHqlExpression * cachedNullChar;
static IHqlExpression * defaultAttrExpr;
static IHqlExpression * selfAttrExpr;
ITypeInfo * boolType;
ITypeInfo * sizetType;
ITypeInfo * signedType;
ITypeInfo * unsignedType;
ITypeInfo * defaultIntegralType;
ITypeInfo * counterType;
ITypeInfo * unknownDataType;
ITypeInfo * unknownStringType;
ITypeInfo * unknownVarStringType;
ITypeInfo * unknownUtf8Type;
ITypeInfo * constUnknownVarStringType;
ITypeInfo * unknownUnicodeType;
ITypeInfo * fposType;
ITypeInfo * doubleType;
IHqlExpression * skipActionMarker;
IHqlExpression * skipReturnMarker;
IHqlExpression * subGraphMarker;
IHqlExpression * removedAssignTag;
IHqlExpression * internalAttrExpr;
IHqlExpression * activityIdMarkerExpr;
IHqlExpression * conditionalRowMarkerExpr;
//===========================================================================
MODULE_INIT(INIT_PRIORITY_STANDARD)
{
boolType = makeBoolType();
signedType = makeIntType(sizeof(signed), true);
unsignedType = makeIntType(sizeof(unsigned), false);
sizetType = makeIntType(sizeof(size32_t), false);
defaultIntegralType = makeIntType(8, true);
counterType = makeIntType(8, false);
unknownDataType = makeDataType(UNKNOWN_LENGTH);
unknownStringType = makeStringType(UNKNOWN_LENGTH, NULL, NULL);
unknownVarStringType = makeVarStringType(UNKNOWN_LENGTH);
unknownUtf8Type = makeUtf8Type(UNKNOWN_LENGTH, 0);
constUnknownVarStringType = makeConstantModifier(LINK(unknownVarStringType));
unknownUnicodeType = makeUnicodeType(UNKNOWN_LENGTH, 0);
fposType = makeIntType(8, false);
doubleType = makeRealType(8);
cachedVoidType = makeVoidType();
cachedBoolValue[false] = createConstant(false);
cachedBoolValue[true] = createConstant(true);
cachedZero = createIntConstant(0);
cachedNullChar = createConstant(createCharValue(0, makeCharType()));
defaultAttrExpr = createAttribute(defaultAtom);
selfAttrExpr = createAttribute(selfAtom);
skipActionMarker = createAttribute(skipActionMarkerAtom);
skipReturnMarker = createAttribute(skipReturnMarkerAtom);
subGraphMarker = createAttribute(subgraphAtom);
removedAssignTag = createAttribute(_internal_Atom);
internalAttrExpr = createAttribute(internalAtom);
activityIdMarkerExpr = createAttribute(activityIdMarkerAtom);
conditionalRowMarkerExpr = createAttribute(_conditionalRowMarker_Atom);
return true;
}
MODULE_EXIT()
{
conditionalRowMarkerExpr->Release();
activityIdMarkerExpr->Release();
internalAttrExpr->Release();
removedAssignTag->Release();
subGraphMarker->Release();
skipReturnMarker->Release();
skipActionMarker->Release();
selfAttrExpr->Release();
defaultAttrExpr->Release();
boolType->Release();
cachedVoidType->Release();
cachedBoolValue[false]->Release();
cachedBoolValue[true]->Release();
cachedZero->Release();
cachedNullChar->Release();
unsignedType->Release();
signedType->Release();
defaultIntegralType->Release();
sizetType->Release();
counterType->Release();
unknownDataType->Release();
unknownStringType->Release();
unknownVarStringType->Release();
unknownUtf8Type->Release();
constUnknownVarStringType->Release();
unknownUnicodeType->Release();
fposType->Release();
doubleType->Release();
}
//===========================================================================
IHqlExpression * getZero() { return LINK(cachedZero); }
ITypeInfo * queryBoolType() { return boolType; }
ITypeInfo * queryVoidType() { return cachedVoidType; }
IHqlExpression * queryBoolExpr(bool value){ return cachedBoolValue[value]; }
IHqlExpression * queryNullChar() { return cachedNullChar; }
IHqlExpression * queryZero() { return cachedZero; }
IHqlExpression * getDefaultAttr() { return LINK(defaultAttrExpr); }
IHqlExpression * getSelfAttr() { return LINK(selfAttrExpr); }
IHqlExpression * queryActivityIdMarker() { return activityIdMarkerExpr; }
IHqlExpression * queryConditionalRowMarker() { return conditionalRowMarkerExpr; }
//===========================================================================
ITypeInfo * getArrayElementType(ITypeInfo * itemType)
{
// use a var string type to get better C++ generated...
if (storePointerInArray(itemType))
return makeVarStringType(UNKNOWN_LENGTH);
return LINK(itemType);
}
ITypeInfo * getConcatResultType(IHqlExpression * expr)
{
assertex(!"not sure if this is unicode safe, but appears not to be used");
//first work out the maximum size of the target
unsigned max = expr->numChildren();
unsigned idx;
unsigned totalSize = 0;
bool unknown = false;
type_t resultType = type_string;
for (idx = 0; idx < max; idx++)
{
ITypeInfo * type = expr->queryChild(idx)->queryType();
unsigned size = type->getStringLen();
if (size == UNKNOWN_LENGTH)
unknown = true;
else
totalSize += size;
if (type->getTypeCode() == type_varstring)
resultType = type_varstring;
}
if (unknown)
totalSize = 1023;
if (resultType == type_string)
return makeStringType(totalSize, NULL, NULL);
return makeVarStringType(totalSize);
}
bool isCompare3Valued(ITypeInfo * type)
{
type = type->queryPromotedType();
switch (type->getTypeCode())
{
case type_string: case type_data:
if (type->getSize() != 1)
return true;
break;
case type_qstring:
case type_varstring:
case type_unicode:
case type_varunicode:
case type_decimal:
case type_utf8:
return true;
}
return false;
}
bool storePointerInArray(ITypeInfo * type)
{
return type->isReference() && isTypePassedByAddress(type);
}
//Convert no_dataset_alias(expr, uid) to expr'
IHqlExpression * normalizeDatasetAlias(IHqlExpression * expr)
{
IHqlExpression * uid = expr->queryProperty(_uid_Atom);
assertex(uid);
return appendOwnedOperand(expr->queryChild(0), LINK(uid));
}
//---------------------------------------------------------------------------
bool isSelectSortedTop(IHqlExpression * selectExpr)
{
IHqlExpression * index = selectExpr->queryChild(1);
if (matchesConstantValue(index, 1))
{
IHqlExpression * ds = selectExpr->queryChild(0);
return ((ds->getOperator() == no_sort) || (ds->getOperator() == no_topn));
}
return false;
}
ITypeInfo * makeRowReferenceType(IHqlExpression * ds)
{
ITypeInfo * recordType = ds ? LINK(ds->queryRecordType()) : NULL;
ITypeInfo * rowType = makeReferenceModifier(makeRowType(recordType));
if (ds)
{
ITypeInfo * dsType = ds->queryType();
if (hasLinkedRow(dsType))
rowType = makeAttributeModifier(rowType, getLinkCountedAttr());
if (hasOutOfLineModifier(dsType))
rowType = makeOutOfLineModifier(rowType);
}
return rowType;
}
ITypeInfo * makeRowReferenceType(const CHqlBoundExpr & bound)
{
return makeRowReferenceType(bound.expr);
}
IHqlExpression * addMemberSelector(IHqlExpression * expr, IHqlExpression * selector)
{
if (!expr)
return NULL;
if (expr->getOperator() == no_variable)
return createValue(no_pselect, expr->getType(), LINK(selector), LINK(expr));
if (expr->numChildren() == 0)
return LINK(expr);
HqlExprArray args;
ForEachChild(i, expr)
args.append(*addMemberSelector(expr->queryChild(i), selector));
return expr->clone(args);
}
//Only called on translated expressions
IHqlExpression * addExpressionModifier(IHqlExpression * expr, typemod_t modifier, IInterface * extra)
{
//Not sure which is best implementation...
#if 1
return createValue(no_typetransfer, makeModifier(expr->getType(), modifier, LINK(extra)), LINK(expr));
#else
HqlExprArray args;
unwindChildren(args, expr);
return createValue(expr->getOperator(), makeModifier(expr->getType(), modifier, LINK(extra)), args);
#endif
}
static void expandFieldNames(StringBuffer & out, IHqlExpression * record, StringBuffer & prefix, const char * sep, IHqlExpression * formatFunc)
{
ForEachChild(i, record)
{
IHqlExpression * cur = record->queryChild(i);
switch (cur->getOperator())
{
case no_record:
expandFieldNames(out, cur, prefix, sep, formatFunc);
break;
case no_ifblock:
expandFieldNames(out, cur->queryChild(1), prefix, sep, formatFunc);
break;
case no_field:
{
StringBuffer lowerName;
lowerName.append(cur->queryName()).toLowerCase();
if (formatFunc)
{
HqlExprArray args;
args.append(*createConstant(lowerName.str()));
OwnedHqlExpr bound = createBoundFunction(NULL, formatFunc, args, NULL, true);
OwnedHqlExpr folded = foldHqlExpression(bound, NULL, HFOthrowerror|HFOfoldimpure|HFOforcefold);
assertex(folded->queryValue());
lowerName.clear();
getStringValue(lowerName, folded);
}
switch (cur->queryType()->getTypeCode())
{
case type_record:
case type_row:
{
unsigned len = prefix.length();
prefix.append(lowerName).append(".");
expandFieldNames(out, cur->queryRecord(), prefix, sep, formatFunc);
prefix.setLength(len);
break;
}
default:
{
if (out.length())
out.append(sep);
out.append(prefix).append(lowerName);
break;
}
}
break;
}
}
}
}
void expandFieldNames(StringBuffer & out, IHqlExpression * record, const char * sep, IHqlExpression * formatFunc)
{
StringBuffer prefix;
expandFieldNames(out, record, prefix, sep, formatFunc);
}
IHqlExpression * ensurePositiveOrZeroInt64(IHqlExpression * expr)
{
if (!expr->queryType()->isSigned())
return LINK(expr);
Owned type = makeIntType(8, true);
if (isCast(expr) && expr->queryType() == type)
{
ITypeInfo * uncastType = expr->queryChild(0)->queryType();
if (!uncastType->isSigned() && uncastType->isInteger() && uncastType->getSize() < 8)
return LINK(expr);
}
OwnedHqlExpr cast = ensureExprType(expr, type);
IValue * value = cast->queryValue();
Owned zeroValue = type->castFrom(true, I64C(0));
OwnedHqlExpr zero = createConstant(LINK(zeroValue));
if (value)
{
if (value->compare(zeroValue) < 0)
return LINK(zero);
return LINK(cast);
}
//A bit convoluted, but we only want to evaluate impure expressions (e.g., random()!) once.
//So force them to appear pure (so get commoned up), wrap in an alias, and then create the conditional assignment
if (!cast->isPure())
{
OwnedHqlExpr localAttr = createLocalAttribute();
OwnedHqlExpr pure = createValue(no_pure, cast->getType(), LINK(cast));
cast.setown(createAlias(pure, localAttr));
}
return createValue(no_if, LINK(type), createBoolExpr(no_lt, LINK(cast), LINK(zero)), LINK(zero), LINK(cast));
}
void getOutputLibraryName(SCMStringBuffer & libraryName, IConstWorkUnit * wu)
{
wu->getApplicationValue("LibraryModule", "name", libraryName);
}
IHqlExpression * projectCreateSetDataset(IHqlExpression * expr)
{
IHqlExpression * ds = expr->queryChild(0);
IHqlExpression * select = expr->queryChild(1);
IHqlExpression * record = ds->queryRecord();
//Project down to a single field if necessary. Not needed if selecting the only field in the dataset.
if (queryRealChild(record, 1) || (select->getOperator() != no_select) || (record->queryChild(0) != select->queryChild(1)) || ds->queryNormalizedSelector() != select->queryChild(0))
{
HqlExprArray assigns;
OwnedHqlExpr targetField;
if (select->getOperator() == no_select)
targetField.set(select->queryChild(1));
else
targetField.setown(createField(valueAtom, select->getType(), NULL));
IHqlExpression * newRecord = createRecord(targetField);
assigns.append(*createAssign(createSelectExpr(getSelf(newRecord), LINK(targetField)), LINK(select)));
IHqlExpression * newTransform = createValue(no_newtransform, makeTransformType(LINK(newRecord->queryRecordType())), assigns);
HqlExprArray args;
args.append(*LINK(ds));
args.append(*newRecord);
args.append(*newTransform);
OwnedHqlExpr projectedDs = createDataset(no_newusertable, args);
return createValue(no_createset, expr->getType(), LINK(projectedDs), createSelectExpr(LINK(projectedDs), LINK(targetField)));
}
return LINK(expr);
}
IHqlExpression * mapInternalFunctionParameters(IHqlExpression * expr)
{
switch (expr->getOperator())
{
case no_sortlist:
{
HqlExprArray args;
ForEachChild(i, expr)
args.append(*mapInternalFunctionParameters(expr->queryChild(i)));
return cloneOrLink(expr, args);
}
case no_param:
{
ITypeInfo * type = expr->queryType();
//String parameters need to be passed as c++ const string parameters
switch (type->getTypeCode())
{
case type_string:
case type_varstring:
case type_data:
case type_qstring:
case type_unicode:
case type_utf8:
case type_varunicode:
if (!expr->hasProperty(constAtom))
return appendOwnedOperand(expr, createAttribute(constAtom));
break;
}
break;
}
}
return LINK(expr);
}