7 min readRishi

FormDataSource.research: The Range You Added Never Ran

FormDataSource.research: The Range You Added Never Ran

The customer filter on the sales order form looked right. The clicked method added a range on CustAccount for US-001 and called research() because that is the method people reach for when the grid is stale. The grid still listed every order in the company. SQL never saw the range. research reruns the QueryRun that is already live. It does not rebuild that QueryRun from query().

That is the whole property. Four form data source methods sound like "refresh the form." They are not interchangeable. One redisplays a buffer. One reloads a RecId. One reruns the cursor you already have. One starts a new cursor from the query object you just edited.

Four methods, four jobs

FormDataSource gives you four ways to make the screen catch up. Mixing them is how you get a filter that never applies, a line that never appears, or a display method that still shows the old name.

MethodHits SQLWhat it loadsCursor
refresh()NoRedisplays the current buffer; display methods run againUnchanged
reread()Current RecId onlyReplaces that one form cache row from the databaseUnchanged
research() / research(true)The live querySame filters as the last executeQuery; new and deleted rows in that setFirst row, or the same RecId when you pass true
executeQuery()query() as it is nowRebuilds QueryRun from query()First row of the new result

refresh() is what the field modified handler should call after it writes Name on the buffer it already holds. There is nothing in SQL to go get. If another session changed CreditMax, refresh() will not see it. That is reread().

reread() on the data source is the form version of buffer.reread(). The table-buffer call reloads this variable by RecId. It does not walk the grid, and it does not pick up a row you inserted on a different buffer. The record cache still applies: a unique-key lookup can be answered from the AOS. reread() is a database read of that RecId, which is what you want after someone else committed.

query() is the next cursor; queryRun() is this one

FormDataSource.query() is the query object you edit. executeQuery() constructs a QueryRun from it and runs that. After that, queryRun() is the cursor the grid is bound to. research() executes that QueryRun again. It does not clone query() first.

So this is a no-op against SQL's WHERE:

QueryBuildDataSource qbds;

qbds = salesTable_ds.query().dataSourceTable(tableNum(SalesTable));
SysQuery::findOrCreateRange(qbds, fieldNum(SalesTable, CustAccount))
    .value(queryValue(_custAccount));

salesTable_ds.research();

The range sits on query(). The live QueryRun still has whatever ranges were there the last time executeQuery() ran — form init, a user filter, an auto-complete. research() re-reads those orders. US-001 never becomes a predicate.

SysQuery::findOrCreateRange is the right helper so a second click does not stack a second range on the same field. It does not change which method you must call after it. Apply the object you edited:

QueryBuildDataSource qbds;
SalesTable            current;

current = salesTable_ds.cursor();
qbds = salesTable_ds.query().dataSourceTable(tableNum(SalesTable));
SysQuery::findOrCreateRange(qbds, fieldNum(SalesTable, CustAccount))
    .value(queryValue(_custAccount));

salesTable_ds.executeQuery();
salesTable_ds.findRecord(current);

executeQuery() positions on the first row of the new result. That is the contract. If the current order still matches the new range, findRecord puts the cursor back on that RecId. If it does not match, the grid is honest: that order is not in the set.

Clearing a filter is the same shape. clearRanges() or an empty value on the range still lives on query() until executeQuery().

reread will not invent a line

The other mix-up is an insert. A custom button writes a SalesLine on its own buffer and then "refreshes" the lines grid with reread():

SalesLine salesLine;

salesLine.clear();
salesLine.SalesId = salesTable.SalesId;
salesLine.ItemId  = _itemId;
salesLine.initFromSalesTable(salesTable);
salesLine.initValue();
salesLine.insert();

salesLine_ds.reread();

insert() committed. The grid still shows the line that was current when the user clicked. reread() reloaded that line. The new RecId is not the current record, so it is not the one SQL was asked for.

research(true) is the method that re-runs the live lines query and tries to keep the header-side cursor where it was:

salesLine.insert();
salesLine_ds.research(true);

true is retain position. Pass it when the user should stay on the order they were editing. Calling research() with no argument re-runs the same query and lands on the first row of the result, which on a header-lines form feels like the form jumped.

If the new line should become current, research and then findRecord on the buffer you inserted. reread() still does not belong here: there is no "current row" that is the new row until the query has seen it.

A delete is the mirror. reread() of a RecId that is gone is a missing row, not a rebuilt set. research(true) drops the deleted line from the grid and keeps position if another row can hold it.

What the child data source actually listens to

Header-lines forms join a child data source to the parent. Active and Delayed re-query the child when the parent cursor moves. Passive does not. A Passive lines data source stays empty until something calls executeQuery() or research() on that data source. Do not assume a header research() filled a child you marked Passive so it would stay quiet.

Inner and outer joins are part of the parent's query. Changing a range on the child QueryBuildDataSource of a joined source is still a query() edit. It waits for executeQuery() on the parent, the same as a range on SalesTable.

Active / Activated on the data source fire when the cursor moves. executeQuery() and research() both move it. A handler that is safe on user click can run again after your research, including work you did not want to do twice. That is the same placement rule as in the form data source events post: keep those handlers cheap, and do not hide a second executeQuery() inside Activated.

Which call to write

  • You changed a range, a filter, or clearRanges() on query(). executeQuery(). Then findRecord if the old RecId should still be current.
  • The query is right; the database moved. Another session inserted, updated, or deleted rows that this query already describes. research(true) on that data source.
  • This row changed; the set did not. You updated the current buffer on another connection, or you need to drop a cached display value. reread() on the data source. Table-buffer reread() if you are not on a form.
  • You only changed a field in memory. refresh(). No SQL.
  • You inserted on a different buffer. research(true), not reread().

Do not wrap executeQuery() in a loop. Do not call research() "to be safe" after every reread(). Each research is a query. The indexes and joins on that data source are what the user waits on.

What this is not for

A way to bypass query() and poke queryRun().query() instead. You can. You then have two query objects to keep in sync, and the next executeQuery() throws away the run you patched. Edit query(), then executeQuery().

A replacement for select in a batch. Data source methods exist because a form has a cursor, a cache, and linked sources. A SysOperation job should select, update_recordset, or a QueryRun you own. There is no grid to research.

A fix for a range you put on the wrong data source. A filter on SalesLine.ItemId does not hide headers unless the line source is joined in a way that makes that true. executeQuery() will faithfully run the wrong query.

An implicit ttscommit. These methods read. They do not save the dirty buffer. write() on the data source is still the save.

The filter in the first paragraph never became SQL because research() is loyal to the cursor it has, not the query object you edited after the fact. Put the range on query(), call executeQuery(), and put the cursor back with findRecord if you still need that order. Use reread() when the current RecId is the thing that changed, and research(true) when the set changed. Then the grid shows US-001 because the query you ran is the query you wrote.

Keep reading

Newsletter

New posts, straight to your inbox

One email when a new post goes up. Double opt-in, unsubscribe anytime. Not a daily digest.

Comments

  • No comments yet. Be the first.