Re: Less-silly selectivity for JSONB matching operators

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Less-silly selectivity for JSONB matching operators
Дата
Msg-id 18337.1585677217@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Less-silly selectivity for JSONB matching operators  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: Less-silly selectivity for JSONB matching operators  (Alexey Bashtanov <bashtanov@imap.cc>)
Список pgsql-hackers
Renamed "matchsel" to "matchingsel" etc, added DEFAULT_MATCHING_SEL,
rebased over commit 911e70207.  Since that commit already created
new versions of the relevant contrib modules, I think we can just
redefine what those versions contain, rather than making yet-newer
versions.  (Of course, that assumes we're going to include this in
v13.)

            regards, tom lane

diff --git a/contrib/ltree/ltree_op.c b/contrib/ltree/ltree_op.c
index 34e6e4b..f606761 100644
--- a/contrib/ltree/ltree_op.c
+++ b/contrib/ltree/ltree_op.c
@@ -566,8 +566,6 @@ ltree2text(PG_FUNCTION_ARGS)
 }


-#define DEFAULT_PARENT_SEL 0.001
-
 /*
  *    ltreeparentsel - Selectivity of parent relationship for ltree data types.
  */
@@ -578,101 +576,12 @@ ltreeparentsel(PG_FUNCTION_ARGS)
     Oid            operator = PG_GETARG_OID(1);
     List       *args = (List *) PG_GETARG_POINTER(2);
     int            varRelid = PG_GETARG_INT32(3);
-    VariableStatData vardata;
-    Node       *other;
-    bool        varonleft;
     double        selec;

-    /*
-     * If expression is not variable <@ something or something <@ variable,
-     * then punt and return a default estimate.
-     */
-    if (!get_restriction_variable(root, args, varRelid,
-                                  &vardata, &other, &varonleft))
-        PG_RETURN_FLOAT8(DEFAULT_PARENT_SEL);
-
-    /*
-     * If the something is a NULL constant, assume operator is strict and
-     * return zero, ie, operator will never return TRUE.
-     */
-    if (IsA(other, Const) &&
-        ((Const *) other)->constisnull)
-    {
-        ReleaseVariableStats(vardata);
-        PG_RETURN_FLOAT8(0.0);
-    }
-
-    if (IsA(other, Const))
-    {
-        /* Variable is being compared to a known non-null constant */
-        Datum        constval = ((Const *) other)->constvalue;
-        FmgrInfo    contproc;
-        double        mcvsum;
-        double        mcvsel;
-        double        nullfrac;
-        int            hist_size;
-
-        fmgr_info(get_opcode(operator), &contproc);
-
-        /*
-         * Is the constant "<@" to any of the column's most common values?
-         */
-        mcvsel = mcv_selectivity(&vardata, &contproc, constval, varonleft,
-                                 &mcvsum);
-
-        /*
-         * If the histogram is large enough, see what fraction of it the
-         * constant is "<@" to, and assume that's representative of the
-         * non-MCV population.  Otherwise use the default selectivity for the
-         * non-MCV population.
-         */
-        selec = histogram_selectivity(&vardata, &contproc,
-                                      constval, varonleft,
-                                      10, 1, &hist_size);
-        if (selec < 0)
-        {
-            /* Nope, fall back on default */
-            selec = DEFAULT_PARENT_SEL;
-        }
-        else if (hist_size < 100)
-        {
-            /*
-             * For histogram sizes from 10 to 100, we combine the histogram
-             * and default selectivities, putting increasingly more trust in
-             * the histogram for larger sizes.
-             */
-            double        hist_weight = hist_size / 100.0;
-
-            selec = selec * hist_weight +
-                DEFAULT_PARENT_SEL * (1.0 - hist_weight);
-        }
-
-        /* In any case, don't believe extremely small or large estimates. */
-        if (selec < 0.0001)
-            selec = 0.0001;
-        else if (selec > 0.9999)
-            selec = 0.9999;
-
-        if (HeapTupleIsValid(vardata.statsTuple))
-            nullfrac = ((Form_pg_statistic) GETSTRUCT(vardata.statsTuple))->stanullfrac;
-        else
-            nullfrac = 0.0;
-
-        /*
-         * Now merge the results from the MCV and histogram calculations,
-         * realizing that the histogram covers only the non-null values that
-         * are not listed in MCV.
-         */
-        selec *= 1.0 - nullfrac - mcvsum;
-        selec += mcvsel;
-    }
-    else
-        selec = DEFAULT_PARENT_SEL;
-
-    ReleaseVariableStats(vardata);
-
-    /* result should be in range, but make sure... */
-    CLAMP_PROBABILITY(selec);
+    /* Use generic restriction selectivity logic, with default 0.001. */
+    selec = generic_restriction_selectivity(root, operator,
+                                            args, varRelid,
+                                            0.001);

     PG_RETURN_FLOAT8((float8) selec);
 }
diff --git a/doc/src/sgml/xoper.sgml b/doc/src/sgml/xoper.sgml
index 132056f..56b0849 100644
--- a/doc/src/sgml/xoper.sgml
+++ b/doc/src/sgml/xoper.sgml
@@ -283,6 +283,18 @@ column OP constant
    </para>

    <para>
+    Another useful built-in selectivity estimation function
+    is <function>matchingsel</function>, which will work for almost any
+    binary operator, if standard MCV and/or histogram statistics are
+    collected for the input data type(s).  Its default estimate is set to
+    twice the default estimate used in <function>eqsel</function>, making
+    it most suitable for comparison operators that are somewhat less
+    strict than equality.  (Or you could call the
+    underlying <function>generic_restriction_selectivity</function>
+    function, providing a different default estimate.)
+   </para>
+
+   <para>
     There are additional selectivity estimation functions designed for geometric
     operators in <filename>src/backend/utils/adt/geo_selfuncs.c</filename>: <function>areasel</function>,
<function>positionsel</function>,
     and <function>contsel</function>.  At this writing these are just stubs, but you might want
@@ -319,6 +331,7 @@ table1.column1 OP table2.column2
       <member><function>scalarlejoinsel</function> for <literal><=</literal></member>
       <member><function>scalargtjoinsel</function> for <literal>></literal></member>
       <member><function>scalargejoinsel</function> for <literal>>=</literal></member>
+      <member><function>matchingjoinsel</function> for generic matching operators</member>
       <member><function>areajoinsel</function> for 2D area-based comparisons</member>
       <member><function>positionjoinsel</function> for 2D position-based comparisons</member>
       <member><function>contjoinsel</function> for 2D containment-based comparisons</member>
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index e62b69d..4fdcb07 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -830,6 +830,132 @@ histogram_selectivity(VariableStatData *vardata, FmgrInfo *opproc,
 }

 /*
+ *    generic_restriction_selectivity        - Selectivity for almost anything
+ *
+ * This function estimates selectivity for operators that we don't have any
+ * special knowledge about, but are on data types that we collect standard
+ * MCV and/or histogram statistics for.  (Additional assumptions are that
+ * the operator is strict and immutable, or at least stable.)
+ *
+ * If we have "VAR OP CONST" or "CONST OP VAR", selectivity is estimated by
+ * applying the operator to each element of the column's MCV and/or histogram
+ * stats, and merging the results using the assumption that the histogram is
+ * a reasonable random sample of the column's non-MCV population.  Note that
+ * if the operator's semantics are related to the histogram ordering, this
+ * might not be such a great assumption; other functions such as
+ * scalarineqsel() are probably a better match in such cases.
+ *
+ * Otherwise, fall back to the default selectivity provided by the caller.
+ */
+double
+generic_restriction_selectivity(PlannerInfo *root, Oid operator,
+                                List *args, int varRelid,
+                                double default_selectivity)
+{
+    double        selec;
+    VariableStatData vardata;
+    Node       *other;
+    bool        varonleft;
+
+    /*
+     * If expression is not variable OP something or something OP variable,
+     * then punt and return the default estimate.
+     */
+    if (!get_restriction_variable(root, args, varRelid,
+                                  &vardata, &other, &varonleft))
+        return default_selectivity;
+
+    /*
+     * If the something is a NULL constant, assume operator is strict and
+     * return zero, ie, operator will never return TRUE.
+     */
+    if (IsA(other, Const) &&
+        ((Const *) other)->constisnull)
+    {
+        ReleaseVariableStats(vardata);
+        return 0.0;
+    }
+
+    if (IsA(other, Const))
+    {
+        /* Variable is being compared to a known non-null constant */
+        Datum        constval = ((Const *) other)->constvalue;
+        FmgrInfo    opproc;
+        double        mcvsum;
+        double        mcvsel;
+        double        nullfrac;
+        int            hist_size;
+
+        fmgr_info(get_opcode(operator), &opproc);
+
+        /*
+         * Calculate the selectivity for the column's most common values.
+         */
+        mcvsel = mcv_selectivity(&vardata, &opproc, constval, varonleft,
+                                 &mcvsum);
+
+        /*
+         * If the histogram is large enough, see what fraction of it matches
+         * the query, and assume that's representative of the non-MCV
+         * population.  Otherwise use the default selectivity for the non-MCV
+         * population.
+         */
+        selec = histogram_selectivity(&vardata, &opproc,
+                                      constval, varonleft,
+                                      10, 1, &hist_size);
+        if (selec < 0)
+        {
+            /* Nope, fall back on default */
+            selec = default_selectivity;
+        }
+        else if (hist_size < 100)
+        {
+            /*
+             * For histogram sizes from 10 to 100, we combine the histogram
+             * and default selectivities, putting increasingly more trust in
+             * the histogram for larger sizes.
+             */
+            double        hist_weight = hist_size / 100.0;
+
+            selec = selec * hist_weight +
+                default_selectivity * (1.0 - hist_weight);
+        }
+
+        /* In any case, don't believe extremely small or large estimates. */
+        if (selec < 0.0001)
+            selec = 0.0001;
+        else if (selec > 0.9999)
+            selec = 0.9999;
+
+        /* Don't forget to account for nulls. */
+        if (HeapTupleIsValid(vardata.statsTuple))
+            nullfrac = ((Form_pg_statistic) GETSTRUCT(vardata.statsTuple))->stanullfrac;
+        else
+            nullfrac = 0.0;
+
+        /*
+         * Now merge the results from the MCV and histogram calculations,
+         * realizing that the histogram covers only the non-null values that
+         * are not listed in MCV.
+         */
+        selec *= 1.0 - nullfrac - mcvsum;
+        selec += mcvsel;
+    }
+    else
+    {
+        /* Comparison value is not constant, so we can't do anything */
+        selec = default_selectivity;
+    }
+
+    ReleaseVariableStats(vardata);
+
+    /* result should be in range, but make sure... */
+    CLAMP_PROBABILITY(selec);
+
+    return selec;
+}
+
+/*
  *    ineq_histogram_selectivity    - Examine the histogram for scalarineqsel
  *
  * Determine the fraction of the variable's histogram population that
@@ -2917,6 +3043,40 @@ fail:


 /*
+ *    matchingsel -- generic matching-operator selectivity support
+ *
+ * Use these for any operators that (a) are on data types for which we collect
+ * standard statistics, and (b) have behavior for which the default estimate
+ * (twice DEFAULT_EQ_SEL) is sane.  Typically that is good for match-like
+ * operators.
+ */
+
+Datum
+matchingsel(PG_FUNCTION_ARGS)
+{
+    PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
+    Oid            operator = PG_GETARG_OID(1);
+    List       *args = (List *) PG_GETARG_POINTER(2);
+    int            varRelid = PG_GETARG_INT32(3);
+    double        selec;
+
+    /* Use generic restriction selectivity logic. */
+    selec = generic_restriction_selectivity(root, operator,
+                                            args, varRelid,
+                                            DEFAULT_MATCHING_SEL);
+
+    PG_RETURN_FLOAT8((float8) selec);
+}
+
+Datum
+matchingjoinsel(PG_FUNCTION_ARGS)
+{
+    /* Just punt, for the moment. */
+    PG_RETURN_FLOAT8(DEFAULT_MATCHING_SEL);
+}
+
+
+/*
  * Helper routine for estimate_num_groups: add an item to a list of
  * GroupVarInfos, but only if it's not known equal to any of the existing
  * entries.
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da..65c7fed 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3016,18 +3016,21 @@
 { oid => '3693', descr => 'contains',
   oprname => '@>', oprleft => 'tsquery', oprright => 'tsquery',
   oprresult => 'bool', oprcom => '<@(tsquery,tsquery)',
-  oprcode => 'tsq_mcontains', oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprcode => 'tsq_mcontains', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
 { oid => '3694', descr => 'is contained by',
   oprname => '<@', oprleft => 'tsquery', oprright => 'tsquery',
   oprresult => 'bool', oprcom => '@>(tsquery,tsquery)',
-  oprcode => 'tsq_mcontained', oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprcode => 'tsq_mcontained', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
 { oid => '3762', descr => 'text search match',
   oprname => '@@', oprleft => 'text', oprright => 'text', oprresult => 'bool',
-  oprcode => 'ts_match_tt', oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprcode => 'ts_match_tt', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
 { oid => '3763', descr => 'text search match',
   oprname => '@@', oprleft => 'text', oprright => 'tsquery',
-  oprresult => 'bool', oprcode => 'ts_match_tq', oprrest => 'contsel',
-  oprjoin => 'contjoinsel' },
+  oprresult => 'bool', oprcode => 'ts_match_tq', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },

 # generic record comparison operators
 { oid => '2988', oid_symbol => 'RECORD_EQ_OP', descr => 'equal',
@@ -3178,7 +3181,8 @@
 { oid => '3897', descr => 'is adjacent to',
   oprname => '-|-', oprleft => 'anyrange', oprright => 'anyrange',
   oprresult => 'bool', oprcom => '-|-(anyrange,anyrange)',
-  oprcode => 'range_adjacent', oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprcode => 'range_adjacent', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
 { oid => '3898', descr => 'range union',
   oprname => '+', oprleft => 'anyrange', oprright => 'anyrange',
   oprresult => 'anyrange', oprcom => '+(anyrange,anyrange)',
@@ -3258,22 +3262,23 @@
 { oid => '3246', descr => 'contains',
   oprname => '@>', oprleft => 'jsonb', oprright => 'jsonb', oprresult => 'bool',
   oprcom => '<@(jsonb,jsonb)', oprcode => 'jsonb_contains',
-  oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
 { oid => '3247', descr => 'key exists',
   oprname => '?', oprleft => 'jsonb', oprright => 'text', oprresult => 'bool',
-  oprcode => 'jsonb_exists', oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprcode => 'jsonb_exists', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
 { oid => '3248', descr => 'any key exists',
   oprname => '?|', oprleft => 'jsonb', oprright => '_text', oprresult => 'bool',
-  oprcode => 'jsonb_exists_any', oprrest => 'contsel',
-  oprjoin => 'contjoinsel' },
+  oprcode => 'jsonb_exists_any', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
 { oid => '3249', descr => 'all keys exist',
   oprname => '?&', oprleft => 'jsonb', oprright => '_text', oprresult => 'bool',
-  oprcode => 'jsonb_exists_all', oprrest => 'contsel',
-  oprjoin => 'contjoinsel' },
+  oprcode => 'jsonb_exists_all', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
 { oid => '3250', descr => 'is contained by',
   oprname => '<@', oprleft => 'jsonb', oprright => 'jsonb', oprresult => 'bool',
   oprcom => '@>(jsonb,jsonb)', oprcode => 'jsonb_contained',
-  oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
 { oid => '3284', descr => 'concatenate',
   oprname => '||', oprleft => 'jsonb', oprright => 'jsonb',
   oprresult => 'jsonb', oprcode => 'jsonb_concat' },
@@ -3292,10 +3297,10 @@
 { oid => '4012', descr => 'jsonpath exists',
   oprname => '@?', oprleft => 'jsonb', oprright => 'jsonpath',
   oprresult => 'bool', oprcode => 'jsonb_path_exists_opr(jsonb,jsonpath)',
-  oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
 { oid => '4013', descr => 'jsonpath match',
   oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
   oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
-  oprrest => 'contsel', oprjoin => 'contjoinsel' },
+  oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },

 ]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a6a708c..fe3df44 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10628,6 +10628,15 @@
   prosrc => 'shift_jis_2004_to_euc_jis_2004',
   probin => '$libdir/euc2004_sjis2004' },

+{ oid => '8387',
+  descr => 'restriction selectivity for generic matching operators',
+  proname => 'matchingsel', provolatile => 's', prorettype => 'float8',
+  proargtypes => 'internal oid internal int4', prosrc => 'matchingsel' },
+{ oid => '8388', descr => 'join selectivity for generic matching operators',
+  proname => 'matchingjoinsel', provolatile => 's', prorettype => 'float8',
+  proargtypes => 'internal oid internal int2 internal',
+  prosrc => 'matchingjoinsel' },
+
 # replication/origin.h
 { oid => '6003', descr => 'create a replication origin',
   proname => 'pg_replication_origin_create', provolatile => 'v',
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 1c9570f..1dd3ac1 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -42,6 +42,9 @@
 /* default selectivity estimate for pattern-match operators such as LIKE */
 #define DEFAULT_MATCH_SEL    0.005

+/* default selectivity estimate for other matching operators */
+#define DEFAULT_MATCHING_SEL    0.010
+
 /* default number of distinct values in a table */
 #define DEFAULT_NUM_DISTINCT  200

@@ -148,6 +151,9 @@ extern double histogram_selectivity(VariableStatData *vardata, FmgrInfo *opproc,
                                     Datum constval, bool varonleft,
                                     int min_hist_size, int n_skip,
                                     int *hist_size);
+extern double generic_restriction_selectivity(PlannerInfo *root, Oid operator,
+                                              List *args, int varRelid,
+                                              double default_selectivity);
 extern double ineq_histogram_selectivity(PlannerInfo *root,
                                          VariableStatData *vardata,
                                          FmgrInfo *opproc, bool isgt, bool iseq,
diff --git a/contrib/hstore/hstore--1.6--1.7.sql b/contrib/hstore/hstore--1.6--1.7.sql
index 0d126ef..3e5cb67 100644
--- a/contrib/hstore/hstore--1.6--1.7.sql
+++ b/contrib/hstore/hstore--1.6--1.7.sql
@@ -10,3 +10,18 @@ LANGUAGE C IMMUTABLE PARALLEL SAFE;

 ALTER OPERATOR FAMILY gist_hstore_ops USING gist
 ADD FUNCTION 10 (hstore) ghstore_options (internal);
+
+ALTER OPERATOR ? (hstore, text)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ?| (hstore, text[])
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ?& (hstore, text[])
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @> (hstore, hstore)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR <@ (hstore, hstore)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @ (hstore, hstore)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ~ (hstore, hstore)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
diff --git a/contrib/ltree/ltree--1.1--1.2.sql b/contrib/ltree/ltree--1.1--1.2.sql
index 7b4ea99..186381e 100644
--- a/contrib/ltree/ltree--1.1--1.2.sql
+++ b/contrib/ltree/ltree--1.1--1.2.sql
@@ -19,3 +19,84 @@ ADD FUNCTION 10 (ltree) ltree_gist_options (internal);
 ALTER OPERATOR FAMILY gist__ltree_ops USING gist
 ADD FUNCTION 10 (_ltree) _ltree_gist_options (internal);

+ALTER OPERATOR < (ltree, ltree)
+  SET (RESTRICT = scalarltsel, JOIN = scalarltjoinsel);
+ALTER OPERATOR <= (ltree, ltree)
+  SET (RESTRICT = scalarlesel, JOIN = scalarlejoinsel);
+ALTER OPERATOR >= (ltree, ltree)
+  SET (RESTRICT = scalargesel, JOIN = scalargejoinsel);
+ALTER OPERATOR > (ltree, ltree)
+  SET (RESTRICT = scalargtsel, JOIN = scalargtjoinsel);
+
+ALTER OPERATOR @> (ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^@> (ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR <@ (ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^<@ (ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ~ (ltree, lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ~ (lquery, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^~ (ltree, lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^~ (lquery, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ? (ltree, _lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ? (_lquery, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^? (ltree, _lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^? (_lquery, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @ (ltree, ltxtquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @ (ltxtquery, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^@ (ltree, ltxtquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^@ (ltxtquery, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @> (_ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR <@ (ltree, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR <@ (_ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @> (ltree, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ~ (_ltree, lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ~ (lquery, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ? (_ltree, _lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ? (_lquery, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @ (_ltree, ltxtquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR @ (ltxtquery, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^@> (_ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^<@ (ltree, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^<@ (_ltree, ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^@> (ltree, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^~ (_ltree, lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^~ (lquery, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^? (_ltree, _lquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^? (_lquery, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^@ (_ltree, ltxtquery)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR ^@ (ltxtquery, _ltree)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
diff --git a/contrib/ltree/ltree_op.c b/contrib/ltree/ltree_op.c
index f606761..4ac2ed5 100644
--- a/contrib/ltree/ltree_op.c
+++ b/contrib/ltree/ltree_op.c
@@ -568,6 +568,9 @@ ltree2text(PG_FUNCTION_ARGS)

 /*
  *    ltreeparentsel - Selectivity of parent relationship for ltree data types.
+ *
+ * This function is not used anymore, if the ltree extension has been
+ * updated to 1.2 or later.
  */
 Datum
 ltreeparentsel(PG_FUNCTION_ARGS)
diff --git a/contrib/pg_trgm/expected/pg_trgm.out b/contrib/pg_trgm/expected/pg_trgm.out
index 5746be0..923c326 100644
--- a/contrib/pg_trgm/expected/pg_trgm.out
+++ b/contrib/pg_trgm/expected/pg_trgm.out
@@ -5170,16 +5170,15 @@ SELECT similarity('Szczecin', 'Warsaw');
 EXPLAIN (COSTS OFF)
 SELECT DISTINCT city, similarity(city, 'Warsaw'), show_limit()
   FROM restaurants WHERE city % 'Warsaw';
-                         QUERY PLAN
--------------------------------------------------------------
- Unique
-   ->  Sort
-         Sort Key: city, (similarity(city, 'Warsaw'::text))
-         ->  Bitmap Heap Scan on restaurants
-               Recheck Cond: (city % 'Warsaw'::text)
-               ->  Bitmap Index Scan on restaurants_city_idx
-                     Index Cond: (city % 'Warsaw'::text)
-(7 rows)
+                            QUERY PLAN
+-------------------------------------------------------------------
+ HashAggregate
+   Group Key: city, similarity(city, 'Warsaw'::text), show_limit()
+   ->  Bitmap Heap Scan on restaurants
+         Recheck Cond: (city % 'Warsaw'::text)
+         ->  Bitmap Index Scan on restaurants_city_idx
+               Index Cond: (city % 'Warsaw'::text)
+(6 rows)

 SELECT set_limit(0.3);
  set_limit
diff --git a/contrib/pg_trgm/pg_trgm--1.4--1.5.sql b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
index 3804c3b..284f88d 100644
--- a/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
+++ b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
@@ -10,3 +10,14 @@ LANGUAGE C IMMUTABLE PARALLEL SAFE;

 ALTER OPERATOR FAMILY gist_trgm_ops USING gist
 ADD FUNCTION 10 (text) gtrgm_options (internal);
+
+ALTER OPERATOR % (text, text)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR <% (text, text)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR %> (text, text)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR <<% (text, text)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);
+ALTER OPERATOR %>> (text, text)
+  SET (RESTRICT = matchingsel, JOIN = matchingjoinsel);

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

Предыдущее
От: Fujii Masao
Дата:
Сообщение: Re: Planning counters in pg_stat_statements (using pgss_store)
Следующее
От: Tom Lane
Дата:
Сообщение: Re: [PATCH] Incremental sort (was: PoC: Partial sort)