use of io.questdb.griffin.SqlExecutionInterruptor in project questdb by bluestreak01.
the class SampleByInterpolateRecordCursorFactory method getCursor.
@Override
public RecordCursor getCursor(SqlExecutionContext executionContext) throws SqlException {
recordKeyMap.clear();
dataMap.clear();
final RecordCursor baseCursor = base.getCursor(executionContext);
final Record baseRecord = baseCursor.getRecord();
final SqlExecutionInterruptor interruptor = executionContext.getSqlExecutionInterruptor();
try {
// init all record function for this cursor, in case functions require metadata and/or symbol tables
Function.init(recordFunctions, baseCursor, executionContext);
// At the same time check if cursor has data
while (baseCursor.hasNext()) {
interruptor.checkInterrupted();
final MapKey key = recordKeyMap.withKey();
mapSink.copy(baseRecord, key);
key.createValue();
}
// no data, nothing to do
if (recordKeyMap.size() == 0) {
baseCursor.close();
return EmptyTableRandomRecordCursor.INSTANCE;
}
// topTop() is guaranteeing that we get
// the same data as previous while() loop
// there is no data
baseCursor.toTop();
// Evaluate group-by functions.
// On every change of timestamp sample value we
// check group for gaps and fill them with placeholder
// entries. Values for these entries will be interpolated later
// we have data in cursor, so we can grab first value
final boolean good = baseCursor.hasNext();
assert good;
long timestamp = baseRecord.getTimestamp(timestampIndex);
sampler.setStart(timestamp);
long prevSample = sampler.round(timestamp);
// the lowest timestamp value
long loSample = prevSample;
long hiSample;
do {
// this seems inefficient, but we only double-sample
// very first record and nothing else
long sample = sampler.round(baseRecord.getTimestamp(timestampIndex));
if (sample != prevSample) {
// before we continue with next interval
// we need to fill gaps in current interval
// we will go over unique keys and attempt to
// find them in data map with current timestamp
fillGaps(prevSample, sample);
prevSample = sample;
GroupByUtils.toTop(groupByFunctions);
}
// same data group - evaluate group-by functions
MapKey key = dataMap.withKey();
mapSink.copy(baseRecord, key);
key.putLong(sample);
MapValue value = key.createValue();
if (value.isNew()) {
// not a gap
value.putByte(0, (byte) 0);
for (int i = 0; i < groupByFunctionCount; i++) {
groupByFunctions.getQuick(i).computeFirst(value, baseRecord);
}
} else {
for (int i = 0; i < groupByFunctionCount; i++) {
groupByFunctions.getQuick(i).computeNext(value, baseRecord);
}
}
if (!baseCursor.hasNext()) {
hiSample = sampler.nextTimestamp(prevSample);
break;
}
interruptor.checkInterrupted();
} while (true);
// fill gaps if any at end of base cursor
fillGaps(prevSample, hiSample);
if (groupByTwoPointFunctionCount > 0) {
final RecordCursor mapCursor = recordKeyMap.getCursor();
final Record mapRecord = mapCursor.getRecord();
while (mapCursor.hasNext()) {
MapValue value = findDataMapValue(mapRecord, loSample);
if (value.getByte(0) == 0) {
// we have at least 1 data point
long x1 = loSample;
long x2 = x1;
while (true) {
// to timestamp after 'sample' to begin with
x2 = sampler.nextTimestamp(x2);
if (x2 < hiSample) {
value = findDataMapValue(mapRecord, x2);
if (value.getByte(0) == 0) {
interpolateBoundaryRange(x1, x2, mapRecord);
x1 = x2;
}
} else {
break;
}
}
}
}
}
// find gaps by checking each of the unique keys against every sample
long sample;
for (sample = prevSample = loSample; sample < hiSample; prevSample = sample, sample = sampler.nextTimestamp(sample)) {
final RecordCursor mapCursor = recordKeyMap.getCursor();
final Record mapRecord = mapCursor.getRecord();
while (mapCursor.hasNext()) {
// locate first gap
MapValue value = findDataMapValue(mapRecord, sample);
if (value.getByte(0) == 1) {
// gap is at 'sample', so potential X-value is at 'prevSample'
// now we need to find Y-value
long current = sample;
while (true) {
// to timestamp after 'sample' to begin with
long x2 = sampler.nextTimestamp(current);
// is this timestamp within range?
if (x2 < hiSample) {
value = findDataMapValue(mapRecord, x2);
if (value.getByte(0) == 1) {
// gap
current = x2;
} else {
// do we really have X-value?
if (sample == loSample) {
// prevSample does not exist
// find first valid value from 'x2+1' onwards
long x1 = x2;
while (true) {
x2 = sampler.nextTimestamp(x2);
if (x2 < hiSample) {
final MapValue x2value = findDataMapValue(mapRecord, x2);
if (x2value.getByte(0) == 0) {
// non-gap
// found value at 'x2' - this is our Y-value
// the X-value it at 'x1'
// compute slope and go back down all the way to start
// computing values in records
// this has to be a loop that would store y1 and y2 values for each
// group-by function
// use current 'value' for record
MapValue x1Value = findDataMapValue2(mapRecord, x1);
interpolate(loSample, x1, mapRecord, x1, x2, x1Value, x2value);
break;
}
} else {
// we only have a single value at 'x1' - cannot interpolate
// make all values before and after 'x1' NULL
nullifyRange(loSample, x1, mapRecord);
nullifyRange(sampler.nextTimestamp(x1), hiSample, mapRecord);
break;
}
}
} else {
// calculate slope between 'preSample' and 'x2'
// yep, that's right, and go all the way back down
// to 'sample' calculating interpolated values
MapValue x1Value = findDataMapValue2(mapRecord, prevSample);
interpolate(sampler.nextTimestamp(prevSample), x2, mapRecord, prevSample, x2, x1Value, value);
}
break;
}
} else {
// try using first two values
// we had X-value at 'prevSample'
// it will become Y-value and X is at 'prevSample-1'
// and calculate interpolated value all the way to 'hiSample'
long x1 = sampler.previousTimestamp(prevSample);
if (x1 < loSample) {
// not enough data points
// fill all data points from 'sample' down with null
nullifyRange(sample, hiSample, mapRecord);
} else {
MapValue x1Value = findDataMapValue2(mapRecord, x1);
MapValue x2value = findDataMapValue(mapRecord, prevSample);
interpolate(sampler.nextTimestamp(prevSample), hiSample, mapRecord, x1, prevSample, x1Value, x2value);
}
break;
}
}
}
}
}
cursor.of(baseCursor, dataMap.getCursor());
return cursor;
} catch (Throwable e) {
baseCursor.close();
throw e;
}
}
use of io.questdb.griffin.SqlExecutionInterruptor in project questdb by bluestreak01.
the class AbstractSampleByFillRecordCursorFactory method getCursor.
@Override
public RecordCursor getCursor(SqlExecutionContext executionContext) throws SqlException {
final RecordCursor baseCursor = base.getCursor(executionContext);
final SqlExecutionInterruptor interruptor = executionContext.getSqlExecutionInterruptor();
try {
map.clear();
// This factory fills gaps in data. To do that we
// have to know all possible key values. Essentially, every time
// we sample we return same set of key values with different
// aggregation results and timestamp
int n = groupByFunctions.size();
final Record baseCursorRecord = baseCursor.getRecord();
while (baseCursor.hasNext()) {
interruptor.checkInterrupted();
MapKey key = map.withKey();
mapSink.copy(baseCursorRecord, key);
MapValue value = key.createValue();
if (value.isNew()) {
// timestamp is always stored in value field 0
value.putLong(0, Numbers.LONG_NaN);
// this would set values for when keys are not found right away
for (int i = 0; i < n; i++) {
groupByFunctions.getQuick(i).setNull(value);
}
}
}
// empty map? this means that base cursor was empty
if (map.size() == 0) {
baseCursor.close();
return EmptyTableNoSizeRecordCursor.INSTANCE;
}
// because we pass base cursor twice we have to go back to top
// for the second run
baseCursor.toTop();
boolean next = baseCursor.hasNext();
// we know base cursor has value
assert next;
return initFunctionsAndCursor(executionContext, baseCursor);
} catch (Throwable ex) {
baseCursor.close();
throw ex;
}
}
use of io.questdb.griffin.SqlExecutionInterruptor in project questdb by bluestreak01.
the class SortedLightRecordCursor method of.
@Override
public void of(RecordCursor base, SqlExecutionContext executionContext) {
this.base = base;
this.baseRecord = base.getRecord();
final Record placeHolderRecord = base.getRecordB();
SqlExecutionInterruptor interruptor = executionContext.getSqlExecutionInterruptor();
chain.clear();
while (base.hasNext()) {
interruptor.checkInterrupted();
// Tree chain is liable to re-position record to
// other rows to do record comparison. We must use our
// own record instance in case base cursor keeps
// state in the record it returns.
chain.put(baseRecord, base, placeHolderRecord, comparator);
}
chainCursor.toTop();
}
use of io.questdb.griffin.SqlExecutionInterruptor in project questdb by bluestreak01.
the class SortedRecordCursor method of.
@Override
public void of(RecordCursor base, SqlExecutionContext executionContext) {
try {
this.chainCursor = chain.getCursor(base);
final Record record = base.getRecord();
SqlExecutionInterruptor interruptor = executionContext.getSqlExecutionInterruptor();
chain.clear();
while (base.hasNext()) {
interruptor.checkInterrupted();
// Tree chain is liable to re-position record to
// other rows to do record comparison. We must use our
// own record instance in case base cursor keeps
// state in the record it returns.
chain.put(record);
}
chainCursor.toTop();
} catch (Throwable ex) {
base.close();
throw ex;
}
}
Aggregations