mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Show sort ordering options in EXPLAIN output.
Up to now, EXPLAIN has contented itself with printing the sort expressions in a Sort or Merge Append plan node. This patch improves that by annotating the sort keys with COLLATE, DESC, USING, and/or NULLS FIRST/LAST whenever nondefault sort ordering options are used. The output is now a reasonably close approximation of an ORDER BY clause equivalent to the plan's ordering. Marius Timmer, Lukas Kreft, and Arne Scheffer; reviewed by Mike Blackwell. Some additional hacking by me.
This commit is contained in:
		@@ -14,12 +14,14 @@
 | 
				
			|||||||
#include "postgres.h"
 | 
					#include "postgres.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "access/xact.h"
 | 
					#include "access/xact.h"
 | 
				
			||||||
 | 
					#include "catalog/pg_collation.h"
 | 
				
			||||||
#include "catalog/pg_type.h"
 | 
					#include "catalog/pg_type.h"
 | 
				
			||||||
#include "commands/createas.h"
 | 
					#include "commands/createas.h"
 | 
				
			||||||
#include "commands/defrem.h"
 | 
					#include "commands/defrem.h"
 | 
				
			||||||
#include "commands/prepare.h"
 | 
					#include "commands/prepare.h"
 | 
				
			||||||
#include "executor/hashjoin.h"
 | 
					#include "executor/hashjoin.h"
 | 
				
			||||||
#include "foreign/fdwapi.h"
 | 
					#include "foreign/fdwapi.h"
 | 
				
			||||||
 | 
					#include "nodes/nodeFuncs.h"
 | 
				
			||||||
#include "optimizer/clauses.h"
 | 
					#include "optimizer/clauses.h"
 | 
				
			||||||
#include "parser/parsetree.h"
 | 
					#include "parser/parsetree.h"
 | 
				
			||||||
#include "rewrite/rewriteHandler.h"
 | 
					#include "rewrite/rewriteHandler.h"
 | 
				
			||||||
@@ -31,6 +33,7 @@
 | 
				
			|||||||
#include "utils/ruleutils.h"
 | 
					#include "utils/ruleutils.h"
 | 
				
			||||||
#include "utils/snapmgr.h"
 | 
					#include "utils/snapmgr.h"
 | 
				
			||||||
#include "utils/tuplesort.h"
 | 
					#include "utils/tuplesort.h"
 | 
				
			||||||
 | 
					#include "utils/typcache.h"
 | 
				
			||||||
#include "utils/xml.h"
 | 
					#include "utils/xml.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -83,7 +86,10 @@ static void show_group_keys(GroupState *gstate, List *ancestors,
 | 
				
			|||||||
				ExplainState *es);
 | 
									ExplainState *es);
 | 
				
			||||||
static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
 | 
					static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
 | 
				
			||||||
					 int nkeys, AttrNumber *keycols,
 | 
										 int nkeys, AttrNumber *keycols,
 | 
				
			||||||
 | 
										 Oid *sortOperators, Oid *collations, bool *nullsFirst,
 | 
				
			||||||
					 List *ancestors, ExplainState *es);
 | 
										 List *ancestors, ExplainState *es);
 | 
				
			||||||
 | 
					static void show_sortorder_options(StringInfo buf, Node *sortexpr,
 | 
				
			||||||
 | 
										   Oid sortOperator, Oid collation, bool nullsFirst);
 | 
				
			||||||
static void show_sort_info(SortState *sortstate, ExplainState *es);
 | 
					static void show_sort_info(SortState *sortstate, ExplainState *es);
 | 
				
			||||||
static void show_hash_info(HashState *hashstate, ExplainState *es);
 | 
					static void show_hash_info(HashState *hashstate, ExplainState *es);
 | 
				
			||||||
static void show_tidbitmap_info(BitmapHeapScanState *planstate,
 | 
					static void show_tidbitmap_info(BitmapHeapScanState *planstate,
 | 
				
			||||||
@@ -1781,6 +1787,8 @@ show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	show_sort_group_keys((PlanState *) sortstate, "Sort Key",
 | 
						show_sort_group_keys((PlanState *) sortstate, "Sort Key",
 | 
				
			||||||
						 plan->numCols, plan->sortColIdx,
 | 
											 plan->numCols, plan->sortColIdx,
 | 
				
			||||||
 | 
											 plan->sortOperators, plan->collations,
 | 
				
			||||||
 | 
											 plan->nullsFirst,
 | 
				
			||||||
						 ancestors, es);
 | 
											 ancestors, es);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1795,6 +1803,8 @@ show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	show_sort_group_keys((PlanState *) mstate, "Sort Key",
 | 
						show_sort_group_keys((PlanState *) mstate, "Sort Key",
 | 
				
			||||||
						 plan->numCols, plan->sortColIdx,
 | 
											 plan->numCols, plan->sortColIdx,
 | 
				
			||||||
 | 
											 plan->sortOperators, plan->collations,
 | 
				
			||||||
 | 
											 plan->nullsFirst,
 | 
				
			||||||
						 ancestors, es);
 | 
											 ancestors, es);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1813,6 +1823,7 @@ show_agg_keys(AggState *astate, List *ancestors,
 | 
				
			|||||||
		ancestors = lcons(astate, ancestors);
 | 
							ancestors = lcons(astate, ancestors);
 | 
				
			||||||
		show_sort_group_keys(outerPlanState(astate), "Group Key",
 | 
							show_sort_group_keys(outerPlanState(astate), "Group Key",
 | 
				
			||||||
							 plan->numCols, plan->grpColIdx,
 | 
												 plan->numCols, plan->grpColIdx,
 | 
				
			||||||
 | 
												 NULL, NULL, NULL,
 | 
				
			||||||
							 ancestors, es);
 | 
												 ancestors, es);
 | 
				
			||||||
		ancestors = list_delete_first(ancestors);
 | 
							ancestors = list_delete_first(ancestors);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1831,29 +1842,34 @@ show_group_keys(GroupState *gstate, List *ancestors,
 | 
				
			|||||||
	ancestors = lcons(gstate, ancestors);
 | 
						ancestors = lcons(gstate, ancestors);
 | 
				
			||||||
	show_sort_group_keys(outerPlanState(gstate), "Group Key",
 | 
						show_sort_group_keys(outerPlanState(gstate), "Group Key",
 | 
				
			||||||
						 plan->numCols, plan->grpColIdx,
 | 
											 plan->numCols, plan->grpColIdx,
 | 
				
			||||||
 | 
											 NULL, NULL, NULL,
 | 
				
			||||||
						 ancestors, es);
 | 
											 ancestors, es);
 | 
				
			||||||
	ancestors = list_delete_first(ancestors);
 | 
						ancestors = list_delete_first(ancestors);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Common code to show sort/group keys, which are represented in plan nodes
 | 
					 * Common code to show sort/group keys, which are represented in plan nodes
 | 
				
			||||||
 * as arrays of targetlist indexes
 | 
					 * as arrays of targetlist indexes.  If it's a sort key rather than a group
 | 
				
			||||||
 | 
					 * key, also pass sort operators/collations/nullsFirst arrays.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
show_sort_group_keys(PlanState *planstate, const char *qlabel,
 | 
					show_sort_group_keys(PlanState *planstate, const char *qlabel,
 | 
				
			||||||
					 int nkeys, AttrNumber *keycols,
 | 
										 int nkeys, AttrNumber *keycols,
 | 
				
			||||||
 | 
										 Oid *sortOperators, Oid *collations, bool *nullsFirst,
 | 
				
			||||||
					 List *ancestors, ExplainState *es)
 | 
										 List *ancestors, ExplainState *es)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Plan	   *plan = planstate->plan;
 | 
						Plan	   *plan = planstate->plan;
 | 
				
			||||||
	List	   *context;
 | 
						List	   *context;
 | 
				
			||||||
	List	   *result = NIL;
 | 
						List	   *result = NIL;
 | 
				
			||||||
 | 
						StringInfoData sortkeybuf;
 | 
				
			||||||
	bool		useprefix;
 | 
						bool		useprefix;
 | 
				
			||||||
	int			keyno;
 | 
						int			keyno;
 | 
				
			||||||
	char	   *exprstr;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nkeys <= 0)
 | 
						if (nkeys <= 0)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						initStringInfo(&sortkeybuf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set up deparsing context */
 | 
						/* Set up deparsing context */
 | 
				
			||||||
	context = set_deparse_context_planstate(es->deparse_cxt,
 | 
						context = set_deparse_context_planstate(es->deparse_cxt,
 | 
				
			||||||
											(Node *) planstate,
 | 
																(Node *) planstate,
 | 
				
			||||||
@@ -1866,18 +1882,86 @@ show_sort_group_keys(PlanState *planstate, const char *qlabel,
 | 
				
			|||||||
		AttrNumber	keyresno = keycols[keyno];
 | 
							AttrNumber	keyresno = keycols[keyno];
 | 
				
			||||||
		TargetEntry *target = get_tle_by_resno(plan->targetlist,
 | 
							TargetEntry *target = get_tle_by_resno(plan->targetlist,
 | 
				
			||||||
											   keyresno);
 | 
																   keyresno);
 | 
				
			||||||
 | 
							char	   *exprstr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!target)
 | 
							if (!target)
 | 
				
			||||||
			elog(ERROR, "no tlist entry for key %d", keyresno);
 | 
								elog(ERROR, "no tlist entry for key %d", keyresno);
 | 
				
			||||||
		/* Deparse the expression, showing any top-level cast */
 | 
							/* Deparse the expression, showing any top-level cast */
 | 
				
			||||||
		exprstr = deparse_expression((Node *) target->expr, context,
 | 
							exprstr = deparse_expression((Node *) target->expr, context,
 | 
				
			||||||
									 useprefix, true);
 | 
														 useprefix, true);
 | 
				
			||||||
		result = lappend(result, exprstr);
 | 
							resetStringInfo(&sortkeybuf);
 | 
				
			||||||
 | 
							appendStringInfoString(&sortkeybuf, exprstr);
 | 
				
			||||||
 | 
							/* Append sort order information, if relevant */
 | 
				
			||||||
 | 
							if (sortOperators != NULL)
 | 
				
			||||||
 | 
								show_sortorder_options(&sortkeybuf,
 | 
				
			||||||
 | 
													   (Node *) target->expr,
 | 
				
			||||||
 | 
													   sortOperators[keyno],
 | 
				
			||||||
 | 
													   collations[keyno],
 | 
				
			||||||
 | 
													   nullsFirst[keyno]);
 | 
				
			||||||
 | 
							/* Emit one property-list item per sort key */
 | 
				
			||||||
 | 
							result = lappend(result, pstrdup(sortkeybuf.data));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ExplainPropertyList(qlabel, result, es);
 | 
						ExplainPropertyList(qlabel, result, es);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Append nondefault characteristics of the sort ordering of a column to buf
 | 
				
			||||||
 | 
					 * (collation, direction, NULLS FIRST/LAST)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					show_sortorder_options(StringInfo buf, Node *sortexpr,
 | 
				
			||||||
 | 
										   Oid sortOperator, Oid collation, bool nullsFirst)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Oid			sortcoltype = exprType(sortexpr);
 | 
				
			||||||
 | 
						bool		reverse = false;
 | 
				
			||||||
 | 
						TypeCacheEntry *typentry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typentry = lookup_type_cache(sortcoltype,
 | 
				
			||||||
 | 
													 TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Print COLLATE if it's not default.  There are some cases where this is
 | 
				
			||||||
 | 
						 * redundant, eg if expression is a column whose declared collation is
 | 
				
			||||||
 | 
						 * that collation, but it's hard to distinguish that here.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							char	   *collname = get_collation_name(collation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (collname == NULL)
 | 
				
			||||||
 | 
								elog(ERROR, "cache lookup failed for collation %u", collation);
 | 
				
			||||||
 | 
							appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Print direction if not ASC, or USING if non-default sort operator */
 | 
				
			||||||
 | 
						if (sortOperator == typentry->gt_opr)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							appendStringInfoString(buf, " DESC");
 | 
				
			||||||
 | 
							reverse = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (sortOperator != typentry->lt_opr)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							char	   *opname = get_opname(sortOperator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (opname == NULL)
 | 
				
			||||||
 | 
								elog(ERROR, "cache lookup failed for operator %u", sortOperator);
 | 
				
			||||||
 | 
							appendStringInfo(buf, " USING %s", opname);
 | 
				
			||||||
 | 
							/* Determine whether operator would be considered ASC or DESC */
 | 
				
			||||||
 | 
							(void) get_equality_op_for_ordering_op(sortOperator, &reverse);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Add NULLS FIRST/LAST only if it wouldn't be default */
 | 
				
			||||||
 | 
						if (nullsFirst && !reverse)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							appendStringInfoString(buf, " NULLS FIRST");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (!nullsFirst && reverse)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							appendStringInfoString(buf, " NULLS LAST");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
 | 
					 * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -735,7 +735,7 @@ explain (costs off)
 | 
				
			|||||||
                             QUERY PLAN                              
 | 
					                             QUERY PLAN                              
 | 
				
			||||||
---------------------------------------------------------------------
 | 
					---------------------------------------------------------------------
 | 
				
			||||||
 Sort
 | 
					 Sort
 | 
				
			||||||
   Sort Key: (generate_series(1, 3))
 | 
					   Sort Key: (generate_series(1, 3)) DESC
 | 
				
			||||||
   InitPlan 1 (returns $0)
 | 
					   InitPlan 1 (returns $0)
 | 
				
			||||||
     ->  Limit
 | 
					     ->  Limit
 | 
				
			||||||
           ->  Index Only Scan Backward using tenk1_unique2 on tenk1
 | 
					           ->  Index Only Scan Backward using tenk1_unique2 on tenk1
 | 
				
			||||||
@@ -784,7 +784,7 @@ explain (costs off)
 | 
				
			|||||||
   InitPlan 2 (returns $1)
 | 
					   InitPlan 2 (returns $1)
 | 
				
			||||||
     ->  Limit
 | 
					     ->  Limit
 | 
				
			||||||
           ->  Merge Append
 | 
					           ->  Merge Append
 | 
				
			||||||
                 Sort Key: minmaxtest_1.f1
 | 
					                 Sort Key: minmaxtest_1.f1 DESC
 | 
				
			||||||
                 ->  Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_1
 | 
					                 ->  Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_1
 | 
				
			||||||
                       Index Cond: (f1 IS NOT NULL)
 | 
					                       Index Cond: (f1 IS NOT NULL)
 | 
				
			||||||
                 ->  Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest1_1
 | 
					                 ->  Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest1_1
 | 
				
			||||||
@@ -823,7 +823,7 @@ explain (costs off)
 | 
				
			|||||||
   InitPlan 2 (returns $1)
 | 
					   InitPlan 2 (returns $1)
 | 
				
			||||||
     ->  Limit
 | 
					     ->  Limit
 | 
				
			||||||
           ->  Merge Append
 | 
					           ->  Merge Append
 | 
				
			||||||
                 Sort Key: minmaxtest_1.f1
 | 
					                 Sort Key: minmaxtest_1.f1 DESC
 | 
				
			||||||
                 ->  Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_1
 | 
					                 ->  Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_1
 | 
				
			||||||
                       Index Cond: (f1 IS NOT NULL)
 | 
					                       Index Cond: (f1 IS NOT NULL)
 | 
				
			||||||
                 ->  Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest1_1
 | 
					                 ->  Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest1_1
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -603,6 +603,25 @@ ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20;
 | 
				
			|||||||
RESET enable_seqscan;
 | 
					RESET enable_seqscan;
 | 
				
			||||||
RESET enable_hashjoin;
 | 
					RESET enable_hashjoin;
 | 
				
			||||||
RESET enable_nestloop;
 | 
					RESET enable_nestloop;
 | 
				
			||||||
 | 
					-- EXPLAIN
 | 
				
			||||||
 | 
					EXPLAIN (COSTS OFF)
 | 
				
			||||||
 | 
					  SELECT * FROM collate_test10 ORDER BY x, y;
 | 
				
			||||||
 | 
					                  QUERY PLAN                  
 | 
				
			||||||
 | 
					----------------------------------------------
 | 
				
			||||||
 | 
					 Sort
 | 
				
			||||||
 | 
					   Sort Key: x COLLATE "C", y COLLATE "POSIX"
 | 
				
			||||||
 | 
					   ->  Seq Scan on collate_test10
 | 
				
			||||||
 | 
					(3 rows)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXPLAIN (COSTS OFF)
 | 
				
			||||||
 | 
					  SELECT * FROM collate_test10 ORDER BY x DESC, y COLLATE "C" ASC NULLS FIRST;
 | 
				
			||||||
 | 
					                        QUERY PLAN                         
 | 
				
			||||||
 | 
					-----------------------------------------------------------
 | 
				
			||||||
 | 
					 Sort
 | 
				
			||||||
 | 
					   Sort Key: x COLLATE "C" DESC, y COLLATE "C" NULLS FIRST
 | 
				
			||||||
 | 
					   ->  Seq Scan on collate_test10
 | 
				
			||||||
 | 
					(3 rows)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- 9.1 bug with useless COLLATE in an expression subject to length coercion
 | 
					-- 9.1 bug with useless COLLATE in an expression subject to length coercion
 | 
				
			||||||
CREATE TEMP TABLE vctable (f1 varchar(25));
 | 
					CREATE TEMP TABLE vctable (f1 varchar(25));
 | 
				
			||||||
INSERT INTO vctable VALUES ('foo' COLLATE "C");
 | 
					INSERT INTO vctable VALUES ('foo' COLLATE "C");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -319,7 +319,7 @@ explain (costs off)
 | 
				
			|||||||
                     ->  Index Scan using ec1_expr4 on ec1 ec1_3
 | 
					                     ->  Index Scan using ec1_expr4 on ec1 ec1_3
 | 
				
			||||||
               ->  Materialize
 | 
					               ->  Materialize
 | 
				
			||||||
                     ->  Sort
 | 
					                     ->  Sort
 | 
				
			||||||
                           Sort Key: ec1.f1
 | 
					                           Sort Key: ec1.f1 USING <
 | 
				
			||||||
                           ->  Index Scan using ec1_pkey on ec1
 | 
					                           ->  Index Scan using ec1_pkey on ec1
 | 
				
			||||||
                                 Index Cond: (ff = 42::bigint)
 | 
					                                 Index Cond: (ff = 42::bigint)
 | 
				
			||||||
(20 rows)
 | 
					(20 rows)
 | 
				
			||||||
@@ -376,7 +376,7 @@ explain (costs off)
 | 
				
			|||||||
         ->  Index Scan using ec1_expr4 on ec1 ec1_3
 | 
					         ->  Index Scan using ec1_expr4 on ec1 ec1_3
 | 
				
			||||||
   ->  Materialize
 | 
					   ->  Materialize
 | 
				
			||||||
         ->  Sort
 | 
					         ->  Sort
 | 
				
			||||||
               Sort Key: ec1.f1
 | 
					               Sort Key: ec1.f1 USING <
 | 
				
			||||||
               ->  Index Scan using ec1_pkey on ec1
 | 
					               ->  Index Scan using ec1_pkey on ec1
 | 
				
			||||||
                     Index Cond: (ff = 42::bigint)
 | 
					                     Index Cond: (ff = 42::bigint)
 | 
				
			||||||
(14 rows)
 | 
					(14 rows)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -220,6 +220,15 @@ RESET enable_seqscan;
 | 
				
			|||||||
RESET enable_hashjoin;
 | 
					RESET enable_hashjoin;
 | 
				
			||||||
RESET enable_nestloop;
 | 
					RESET enable_nestloop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- EXPLAIN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXPLAIN (COSTS OFF)
 | 
				
			||||||
 | 
					  SELECT * FROM collate_test10 ORDER BY x, y;
 | 
				
			||||||
 | 
					EXPLAIN (COSTS OFF)
 | 
				
			||||||
 | 
					  SELECT * FROM collate_test10 ORDER BY x DESC, y COLLATE "C" ASC NULLS FIRST;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- 9.1 bug with useless COLLATE in an expression subject to length coercion
 | 
					-- 9.1 bug with useless COLLATE in an expression subject to length coercion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TEMP TABLE vctable (f1 varchar(25));
 | 
					CREATE TEMP TABLE vctable (f1 varchar(25));
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user