Index access method not receiving an orderbys ScanKey

Поиск
Список
Период
Сортировка
От Chris Cleveland
Тема Index access method not receiving an orderbys ScanKey
Дата
Msg-id CABSN6Ve=u426YFyjJDyfDFAjxvYtLYG4q5eWi-pXixy_zDthZw@mail.gmail.com
обсуждение исходный текст
Ответы Re: Index access method not receiving an orderbys ScanKey  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
Can anyone tell me why my index access method isn't seeing an order_by ScanKey when there is a query with an ORDER BY clause that uses an operator that the access method supports?

(Apologies for the volume of code below. I just don't know what is important. This is all in Rust.)


CREATE OPERATOR pg_catalog.<===> (
    FUNCTION = rdb.userquery_match,
    LEFTARG = record,
    RIGHTARG = rdb.userqueryspec
);

CREATE OPERATOR pg_catalog.<<=>> (
    FUNCTION = rdb.test_sort_match,
    LEFTARG = record,
    RIGHTARG = rdb.TestSort
);

CREATE OPERATOR CLASS rdb_ops DEFAULT FOR TYPE record USING rdb AS
    OPERATOR 1 pg_catalog.<===> (record, rdb.userqueryspec),
    OPERATOR 2 pg_catalog.<<=>> (record, rdb.testsort) FOR ORDER BY pg_catalog.float_ops;


#[derive(Serialize, Deserialize, PostgresType, Debug)]
pub struct TestSort {
    foo: f32,
}

#[pg_extern(
sql = "CREATE FUNCTION rdb.test_sort_match(rec record, testsort rdb.TestSort) RETURNS bool IMMUTABLE STRICT PARALLEL SAFE  LANGUAGE c AS 'MODULE_PATHNAME', 'dummysort_match_wrapper';",
requires = [DummySortSpec]
)]
fn test_sort_match(_fcinfo: pg_sys::FunctionCallInfo, testsort: TestSort) -> f32 {
    testsort.foo + 2.0
}

#[pg_extern(immutable, strict, parallel_safe)]
fn get_testsort(foo: f32) -> TestSort {
    TestSort { foo }
}

Here's the query:

SELECT title
FROM products
WHERE products <===> rdb.userquery('teddy')
ORDER BY products <<=>> rdb.get_testsort(5.0);

The amhander gets and uses the WHERE clause just fine. The problem is that ambeginscan() and amrescan() both get a norderbys parameter equal to 0.

Doing an EXPLAIN on the query above yields this:

 Sort  (cost=1000027.67..1000028.92 rows=500 width=28)
   Sort Key: ((products.* <<=>> '{"foo":5.0}'::rdb.testsort))
   ->  Index Scan using product_idx on products  (cost=0.00..1000005.26 rows=500 width=28)
         Index Cond: (products.* <===> '{"query_str":"teddy", /* snip */ }'::rdb.userqueryspec)

So, the sort isn't getting passed to the index scan. Weirdly, the test_sort_match() method never gets invoked, so I'm not sure how the system thinks it's actually doing the sort.

The fact that the operators work on a RECORD type does not seem to be significant. If I swap it for a TEXT data type then I get the same behavior.

I'm sure I'm missing something small.

Here's the amhandler definition:

#[pg_extern(sql = "
    CREATE OR REPLACE FUNCTION amhandler(internal) RETURNS index_am_handler PARALLEL SAFE IMMUTABLE STRICT LANGUAGE c AS 'MODULE_PATHNAME', '@FUNCTION_NAME@';
    CREATE ACCESS METHOD rdb TYPE INDEX HANDLER amhandler;
")]
fn amhandler(_fcinfo: pg_sys::FunctionCallInfo) -> PgBox<pg_sys::IndexAmRoutine> {
    let mut amroutine =
        unsafe { PgBox::<pg_sys::IndexAmRoutine>::alloc_node(pg_sys::NodeTag::T_IndexAmRoutine) };

    amroutine.amstrategies = OPERATOR_COUNT;
    amroutine.amsupport = PROC_COUNT;
    amroutine.amoptsprocnum = OPTIONS_PROC;
    amroutine.amcanorder = false;
    amroutine.amcanorderbyop = true;
    amroutine.amcanbackward = false;
    amroutine.amcanunique = false;
    amroutine.amcanmulticol = true;
    amroutine.amoptionalkey = true;
    amroutine.amsearcharray = false;
    amroutine.amsearchnulls = false;
    amroutine.amstorage = false;
    amroutine.amclusterable = false;
    amroutine.ampredlocks = false;
    amroutine.amcanparallel = false;
    amroutine.amcaninclude = true;
    amroutine.amusemaintenanceworkmem = false;
    amroutine.amparallelvacuumoptions = 0;
    amroutine.amkeytype = pg_sys::InvalidOid;

    /* interface functions */
    amroutine.ambuild = Some(build::ambuild);
    amroutine.ambuildempty = Some(build::ambuildempty);
    amroutine.aminsert = Some(insert::aminsert);
    amroutine.ambulkdelete = Some(delete::ambulkdelete);
    amroutine.amvacuumcleanup = Some(delete::amvacuumcleanup);
    amroutine.amcanreturn = Some(scan::amcanreturn); /* test if can return a particular col in index-only scan */
    amroutine.amcostestimate = Some(amcostestimate);
    amroutine.amoptions = Some(index_options::amoptions);

    amroutine.amproperty = None;
    amroutine.ambuildphasename = None;
    amroutine.amvalidate = Some(amvalidate);
    // amroutine.amadjustmembers = None; omit so we can compile earlier versions

    amroutine.ambeginscan = Some(scan::ambeginscan);
    amroutine.amrescan = Some(scan::amrescan);
    amroutine.amgettuple = Some(scan::amgettuple);
    amroutine.amgetbitmap = None; // Some(scan::ambitmapscan);
    amroutine.amendscan = Some(scan::amendscan);

    amroutine.ammarkpos = None;
    amroutine.amrestrpos = None;

    /* interface functions to support parallel index scans */
    amroutine.amestimateparallelscan = None;
    amroutine.aminitparallelscan = None;
    amroutine.amparallelrescan = None;

    amroutine.into_pg_boxed()
}



--
Chris Cleveland
312-339-2677 mobile

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Re: BitmapHeapScan streaming read user and prelim refactoring
Следующее
От: Tom Lane
Дата:
Сообщение: Re: BitmapHeapScan streaming read user and prelim refactoring