Собственно, вот пара вспомогательных методов для класса SysQuery. С помощью первого можно безопасно копировать запросы, в т.ч. при передаче с клиента на сервер и обратно.
X++:
/// <summary>
/// копируем запрос, по ходу перебивая dynalink'и в locked ranges
/// </summary>
/// <param name="_q">
/// исходный запрос
/// </param>
/// <param name="_copyHints">
/// надо ли копировать хинты forceSelectOrder и forceNestedLoops
/// </param>
/// <returns>
/// копия исходного запроса
/// </returns>
/// <remarks>
/// может быть полезно, если запрос затем будет сохраняться в SysLastValue, либо передавать между клиентом и сервером, либо рихтовать под ные нужды (подсчет записей)
/// </remarks>
/// <exception cref="Exception::Error">
/// выбрасывается, если исходный запрос - "вырожденный" либо вовсе null
/// </exception>
public client server static Query copyQuery(
Query _q,
boolean _copyHints = true
)
{
QueryBuildDataSource qbdsOld;
QueryBuildDataSource qbdsNew;
QueryBuildDynalink qbdl;
Query ret;
str sq;
Counter n;
;
if (!(_q && _q.dataSourceCount() > 0))
{
throw error(Error::wrongUseOfFunction(funcname()));
}
ret = new Query(_q.pack(false)); // создать копию запроса
if (_copyHints)
{
// на эти извращения пришлось пойти потому, что у методов Query forceSelectOrder() и forceNestedLoop() параметры обязательны,
// поэтому их нельзя использовать как нормальные свойства для определения наличия или отсутствия соотв. хинтов в запросе
sq = _q.dataSourceNo(1).toString();
if (match(@"^SELECT WITH.* SELECT_ORDER[ ,]", sq))
{
ret.forceSelectOrder(true);
}
if (match(@"^SELECT WITH.* NESTED_LOOP[ ,]", sq))
{
ret.forceNestedLoop(true);
}
}
qbdsNew = ret.dataSourceNo(1); // из нового запроса
qbdsOld = _q.dataSourceNo(1); // из исходного запроса
for (n = 1; n <= qbdsOld.dynalinkCount(); n++)
{
qbdl = qbdsOld.dynalink(n);
// очищаем *все* range'и по данному полю и ставим свой range с нужным статусом
SysQuery::setFieldRange(qbdsNew, qbdl.field(), queryValue(qbdl.cursor().(qbdl.dynamicField())), RangeStatus::Locked);
}
return ret;
}
/// <summary>
/// устанавливает фильтр по полю, удаляя все прочие и опционально управляя его статусом
/// </summary>
public static QueryBuildRange setFieldRange(
QueryBuildDataSource _qbds,
fieldId _fieldId,
Range _rangeValue,
RangeStatus _rangeStatus = RangeStatus::Open
)
{
QueryBuildRange ret;
;
if (_qbds)
{
while (_qbds.findRange(_fieldId))
{
_qbds.clearRange(_fieldId);
}
ret = _qbds.addRange(_fieldId);
ret.value(_rangeValue);
ret.status(_rangeStatus);
}
return ret;
}