Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Nested query using = should return error message if there's more than one row returned #1372

Open
jkosh44 opened this issue Dec 4, 2020 · 0 comments
Labels
bug Something isn't working (correctness). Mark issues with this.

Comments

@jkosh44
Copy link
Contributor

jkosh44 commented Dec 4, 2020

Bug Report

Summary

When using a nested query in the WHERE clause you can use an operator such as = or < as long as the nested query returns a single row. However if the nested query returns more than one row than we should return an error to the user. Currently NoisePage crashes instead of returning an error message.

Environment

OS: Ubuntu (LTS) 20.04

Compiler: GCC 7.0+

CMake Profile: Debug

Jenkins/CI: N/A

Steps to Reproduce

noisepage=# CREATE TABLE foo1 (a int);
CREATE TABLE
noisepage=# CREATE TABLE foo2 (a int);
CREATE TABLE
noisepage=# INSERT INTO foo1 VALUES (1);
INSERT 0 1
noisepage=# INSERT INTO foo1 VALUES (2);
INSERT 0 1
noisepage=# INSERT INTO foo2 VALUES (1);
INSERT 0 1
noisepage=# INSERT INTO foo2 VALUES (2);
INSERT 0 1
noisepage=# SELECT * FROM foo1 WHERE a = (SELECT a FROM foo2);
server closed the connection unexpectedly
	This probably means the server terminated abnormally
	before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
!> 

The DB crashes with a Segmentation Fault

Expected Behavior

postgres=# CREATE TABLE foo1 (a int);
CREATE TABLE
postgres=# CREATE TABLE foo2 (a int);
CREATE TABLE
postgres=# INSERT INTO foo1 VALUES (1);
INSERT 0 1
postgres=# INSERT INTO foo1 VALUES (2);
INSERT 0 1
postgres=# INSERT INTO foo2 VALUES (1);
INSERT 0 1
postgres=# INSERT INTO foo2 VALUES (2);
INSERT 0 1
postgres=# SELECT * FROM foo1 WHERE a = (SELECT a FROM foo2);
ERROR:  more than one row returned by a subquery used as an expression

Note: the DB doesn't crash from this, just prints an error to the terminal

Notes

There are other similar queries that cause errors (They all start with the same creates and inserts). In Postgres they all have the same behavior of printing the same error message to the terminal.

  • SELECT * FROM foo1 WHERE a > (SELECT a FROM foo2); Also causes a segfault
  • SELECT * FROM foo1 WHERE a = (SELECT MIN(a) FROM foo2 GROUP BY a); Errors out and prints the following to the console:
[2020-12-03 20:30:59.532] [execution_logger] [error] ERROR: Line: 62, Col: 0 => function 'aggHTIterInit' expects argument of type '*AggregationHashTable' in position '1', received type '*TableVectorIterator'

struct OutputStruct {
    out0: Integer
}
struct AggPayload {
    gb_term_attr0 : Integer
    agg_term_attr0: IntegerMinAggregate
}
struct AggValues {
    gb_term_attr0 : Integer
    agg_term_attr0: Integer
}
struct QueryState {
    execCtx     : *ExecutionContext
    aggHashTable: AggregationHashTable
}
struct P2_State {
    aggHashTable1: AggregationHashTable
}
struct P1_State {
    output_buffer: *OutputBuffer
}
fun Query4_Pipeline1_KeyCheckPartial(lhs: *AggPayload, rhs: *AggPayload) -> bool {
    if (SqlBoolToBool(lhs.gb_term_attr0 != rhs.gb_term_attr0)) {
        return false
    }
    return true
}

fun Query4_Pipeline1_MergePartitions(queryState: *QueryState, aggHashTable: *AggregationHashTable, ahtOvfIter: *AHTOverflowPartitionIterator) -> nil {
    for (; @aggPartIterHasNext(ahtOvfIter); @aggPartIterNext(ahtOvfIter)) {
        var hashVal = @aggPartIterGetHash(ahtOvfIter)
        var partialRow = @ptrCast(*AggPayload, @aggPartIterGetRow(ahtOvfIter))
        var aggPayload = @ptrCast(*AggPayload, @aggHTLookup(aggHashTable, hashVal, Query4_Pipeline1_KeyCheckPartial, partialRow))
        if (aggPayload == nil) {
            @aggHTLink(aggHashTable, @aggPartIterGetRowEntry(ahtOvfIter))
        } else {
            @aggMerge(&aggPayload.agg_term_attr0, &partialRow.agg_term_attr0)
        }
    }
    return
}

fun Query4_Pipeline1_KeyCheck(aggPayload: *AggPayload, aggValues: *AggValues) -> bool {
    if (SqlBoolToBool(aggPayload.gb_term_attr0 != aggValues.gb_term_attr0)) {
        return false
    }
    return true
}

fun Query4_Init(queryState: *QueryState) -> nil {
    @aggHTInit(&queryState.aggHashTable, queryState.execCtx, @sizeOf(AggPayload))
    return
}

fun Query4_Pipeline2_InitPipelineState(queryState: *QueryState, pipelineState: *P2_State) -> nil {
    @aggHTInit(&pipelineState.aggHashTable1, queryState.execCtx, @sizeOf(AggPayload))
    return
}

fun Query4_Pipeline2_TearDownPipelineState(queryState: *QueryState, pipelineState: *P2_State) -> nil {
    @aggHTFree(&pipelineState.aggHashTable1)
    return
}

fun Query4_Pipeline2_ParallelWork(queryState: *QueryState, pipelineState: *P2_State, tvi: *TableVectorIterator) -> nil {
    var slot: TupleSlot
    for (@tableIterAdvance(tvi)) {
        var vpi = @tableIterGetVPI(tvi)
        for (; @vpiHasNext(vpi); @vpiAdvance(vpi)) {
            slot = @vpiGetSlot(vpi)
            var aggValues: AggValues
            aggValues.gb_term_attr0 = @vpiGetIntNull(vpi, 0)
            aggValues.agg_term_attr0 = @vpiGetIntNull(vpi, 0)
            var hashVal = @hash(aggValues.gb_term_attr0)
            var aggPayload = @ptrCast(*AggPayload, @aggHTLookup(&pipelineState.aggHashTable1, hashVal, Query4_Pipeline1_KeyCheck, &aggValues))
            if (aggPayload == nil) {
                aggPayload = @ptrCast(*AggPayload, @aggHTInsert(&pipelineState.aggHashTable1, hashVal, true))
                aggPayload.gb_term_attr0 = aggValues.gb_term_attr0
                @aggInit(&aggPayload.agg_term_attr0)
            }
            @aggAdvance(&aggPayload.agg_term_attr0, &aggValues.agg_term_attr0)
        }
        var vpi_num_tuples = @tableIterGetVPINumTuples(tvi)
    }
    return
}

fun Query4_Pipeline2_Init(queryState: *QueryState) -> nil {
    var threadStateContainer = @execCtxGetTLS(queryState.execCtx)
    @tlsReset(threadStateContainer, @sizeOf(P2_State), Query4_Pipeline2_InitPipelineState, Query4_Pipeline2_TearDownPipelineState, queryState)
    return
}

fun Query4_Pipeline2_Run(queryState: *QueryState) -> nil {
    var pipelineState = @ptrCast(*P2_State, @tlsGetCurrentThreadState(@execCtxGetTLS(queryState.execCtx)))
    var col_oids: [1]uint32
    col_oids[0] = 1
    @iterateTableParallel(1061, col_oids, queryState, queryState.execCtx, Query4_Pipeline2_ParallelWork)
    @aggHTMoveParts(&queryState.aggHashTable, @execCtxGetTLS(queryState.execCtx), @offsetOf(P2_State, aggHashTable1), Query4_Pipeline1_MergePartitions)
    return
}

fun Query4_Pipeline2_TearDown(queryState: *QueryState) -> nil {
    @tlsClear(@execCtxGetTLS(queryState.execCtx))
    @ensureTrackersStopped(queryState.execCtx)
    return
}

fun Query4_Pipeline1_InitPipelineState(queryState: *QueryState, pipelineState: *P1_State) -> nil {
    pipelineState.output_buffer = @resultBufferNew(queryState.execCtx)
    return
}

fun Query4_Pipeline1_TearDownPipelineState(queryState: *QueryState, pipelineState: *P1_State) -> nil {
    @resultBufferFree(pipelineState.output_buffer)
    return
}

fun Query4_Pipeline1_ParallelWork(queryState: *QueryState, pipelineState: *P1_State, tvi1: *TableVectorIterator) -> nil {
    var slot1: TupleSlot
    for (@tableIterAdvance(tvi1)) {
        var vpi1 = @tableIterGetVPI(tvi1)
        for (; @vpiHasNext(vpi1); @vpiAdvance(vpi1)) {
            slot1 = @vpiGetSlot(vpi1)
            var iterBase: AHTIterator
            var iter = &iterBase
            for (@aggHTIterInit(iter, tvi1); @aggHTIterHasNext(iter); @aggHTIterNext(iter)) {
                var aggRow = @ptrCast(*AggPayload, @aggHTIterGetRow(iter))
                if (SqlBoolToBool(@vpiGetIntNull(vpi1, 0) == @aggResult(&aggRow.agg_term_attr0))) {
                    var outRow = @ptrCast(*OutputStruct, @resultBufferAllocRow(pipelineState.output_buffer))
                    outRow.out0 = @vpiGetIntNull(vpi1, 0)
                }
            }
            @aggHTIterClose(iter)
        }
        var vpi_num_tuples = @tableIterGetVPINumTuples(tvi1)
    }
    @resultBufferFinalize(pipelineState.output_buffer)
    return
}

fun Query4_Pipeline1_Init(queryState: *QueryState) -> nil {
    var threadStateContainer = @execCtxGetTLS(queryState.execCtx)
    @tlsReset(threadStateContainer, @sizeOf(P1_State), Query4_Pipeline1_InitPipelineState, Query4_Pipeline1_TearDownPipelineState, queryState)
    return
}

fun Query4_Pipeline1_Run(queryState: *QueryState) -> nil {
    var pipelineState = @ptrCast(*P1_State, @tlsGetCurrentThreadState(@execCtxGetTLS(queryState.execCtx)))
    var col_oids1: [1]uint32
    col_oids1[0] = 1
    @iterateTableParallel(1060, col_oids1, queryState, queryState.execCtx, Query4_Pipeline1_ParallelWork)
    @resultBufferFinalize(pipelineState.output_buffer)
    return
}

fun Query4_Pipeline1_TearDown(queryState: *QueryState) -> nil {
    @tlsClear(@execCtxGetTLS(queryState.execCtx))
    @ensureTrackersStopped(queryState.execCtx)
    return
}

fun Query4_TearDown(queryState: *QueryState) -> nil {
    @aggHTFree(&queryState.aggHashTable)
    return
}


noisepage: ../src/execution/compiler/executable_query.cpp:139: void noisepage::execution::compiler::ExecutableQuery::Setup(std::vector<std::unique_ptr<noisepage::execution::compiler::ExecutableQuery::Fragment> >&&, std::size_t, std::unique_ptr<noisepage::selfdriving::PipelineOperatingUnits>): Assertion `(std::all_of(fragments.begin(), fragments.end(), [](const auto &fragment) { return fragment->IsCompiled(); })) && ("All query fragments are not compiled!")' failed.
Signal: SIGABRT (Aborted)
@jkosh44 jkosh44 added the bug Something isn't working (correctness). Mark issues with this. label Dec 4, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working (correctness). Mark issues with this.
Projects
None yet
Development

No branches or pull requests

1 participant