Sorry , previous version has bugs. It stamps over the stack and
crashesX( The attached is the bug fixed version, with no
substantial changes.

> On Tue, Jul 15, 2014 at 2:17 PM, Kyotaro HORIGUCHI <
> horiguchi.kyot...@lab.ntt.co.jp> wrote:
> >
> > Hi, the attached is the revised version.
> 
> Thanks Horiguchi-San for the updated patch.

# By the way, this style of calling a person is quite common
# among Japanese since the first-name basis implies very close
# relationship or it frequently conveys offensive shade. But I'm
# not sure what should I call others who're not Japases native.

Anyway,

> Today while looking into updated patch, I was wondering why can't
> we eliminate useless keys in query_pathkeys when we actually build
> the same in function standard_qp_callback(), basically somewhat
> similar to what we do in
> standard_qp_callback->make_pathkeys_for_sortclauses->pathkey_is_redundant().

I agree that standard_qp_callback is basically more preferable.

> We already have index information related to base_rels before building
> query pathkeys.  I noticed that you mentioned the below in your original
> mail which indicates that information related to inheritance tables is built
> only after set_base_rel_sizes()
> 
> "These steps take place between set_base_rel_sizes() and
> set_base_rel_pathlists() in make_one_rel(). The reason for the
> position is that the step 2 above needs all inheritence tables to
> be extended in PlannerInfo and set_base_rel_sizes (currently)
> does that".

Sorry, my memory had been worn down. After some reconfirmation,
this description found to be a bit (quite?) wrong.

collect_common_primary_pathkeys needs root->eq_classes to be set
up beforehand, not appendrels. Bacause build_index_pathkeys
doesn't work before every EC member for all relation involved is
already registered.

standard_qp_callback registers EC members in the following path
but only for the primary(?) tlist of the subquery, so EC members
for the child relations are not registered at the time.

.->make_pathekeys_sortclauses->make_pathkey_from_sortop
   ->make_pathkey_from_sortinfo->get_eclass_for_sort_expr

EC members for the child rels are registered in

 set_base_rel_sizes->set_rel_size->set_append_rel_size
   ->add_child_rel_equivalences

So trim_query_pathkeys (collect_common...) doesn't work before
set_base_rel_sizes(). These EC members needed to be registered at
least if root->query_pathkeys exists so this seems to be done in
standard_qp_callback adding a separate inheritance tree walk.

# rel->has_elcass_joins seems not working but it is not the topic
# here.

> Could you please explain me why the index information built in above
> path is not sufficient or is there any other case which I am missing?

Is the description above enough to be the explaination for the
place? Sorry for the inaccurate description.

regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9573a9b..546e112 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1789,9 +1789,11 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node)
 	/* indexprs is redundant since we print indextlist */
 	WRITE_NODE_FIELD(indpred);
 	WRITE_NODE_FIELD(indextlist);
+	/* cached pathkeys are omitted as indexkeys */
 	WRITE_BOOL_FIELD(predOK);
 	WRITE_BOOL_FIELD(unique);
 	WRITE_BOOL_FIELD(immediate);
+	WRITE_BOOL_FIELD(allnotnull);
 	WRITE_BOOL_FIELD(hypothetical);
 	/* we don't bother with fields copied from the pg_am entry */
 }
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index c81efe9..c12d0e7 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -58,6 +58,7 @@ int			geqo_threshold;
 join_search_hook_type join_search_hook = NULL;
 
 
+static List *collect_common_primary_pathkeys(PlannerInfo *root);
 static void set_base_rel_sizes(PlannerInfo *root);
 static void set_base_rel_pathlists(PlannerInfo *root);
 static void set_rel_size(PlannerInfo *root, RelOptInfo *rel,
@@ -66,6 +67,7 @@ static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 				 Index rti, RangeTblEntry *rte);
 static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel,
 				   RangeTblEntry *rte);
+static void trim_query_pathkeys(PlannerInfo * root);
 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 					   RangeTblEntry *rte);
 static void set_foreign_size(PlannerInfo *root, RelOptInfo *rel,
@@ -112,6 +114,203 @@ static void recurse_push_qual(Node *setOp, Query *topquery,
 				  RangeTblEntry *rte, Index rti, Node *qual);
 static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel);
 
+/*
+ * collect_common_primary_pathkeys: Collects common unique and non-null index
+ * pathkeys from all base relations in current root.
+ */
+static List *
+collect_common_primary_pathkeys(PlannerInfo *root)
+{
+	List *result = NIL;
+	Index rti;
+	int   nindex = 0;
+	List **pathkeys_array0 = NULL;
+	List **pathkeys_array = NULL;
+	List **pathkeys_array2 = NULL;
+	bool do_conjunction = (root->simple_rel_array_size > 2);
+
+	for (rti = 1 ; rti < root->simple_rel_array_size ; rti++)
+	{
+		RelOptInfo *rel = root->simple_rel_array[rti];
+		int prev_nindex = 0;
+		ListCell *lc;
+
+		if (rel == NULL)
+			continue;
+
+		Assert (rel->relid == rti);
+
+		prev_nindex = nindex;
+		nindex = 0;
+
+		/* Scan all base relations except parent relations. */
+		if (root->simple_rte_array[rti]->inh ||
+			(rel->reloptkind != RELOPT_BASEREL &&
+			 rel->reloptkind != RELOPT_OTHER_MEMBER_REL))
+			continue;
+
+		if (do_conjunction)
+		{
+			if (pathkeys_array2)
+				memset(pathkeys_array2, 0, sizeof(List*) * nindex);
+			else
+			{
+				/* Rig for AND (conjunction) operation. */
+				int nindex_tmp = 0;
+
+				foreach (lc, rel->indexlist)
+				{
+					IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
+					
+					if (index->allnotnull &&
+						index->unique && index->immediate &&
+						index->indpred == NIL)
+						nindex_tmp++;
+				}
+
+				if (nindex_tmp == 0)
+					return NULL; /* No common primary pathkeys exists.*/
+
+				pathkeys_array0 = palloc0(sizeof(List*) * nindex_tmp * 2);
+				pathkeys_array  = pathkeys_array0;
+				pathkeys_array2 = pathkeys_array0 + nindex_tmp;
+			}
+		}
+
+		foreach (lc, rel->indexlist)
+		{
+			IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
+			List *idx_pathkeys;
+				
+			/* Only indexes that can unique sort are needed. */
+			if (!index->allnotnull ||
+				!(index->unique && index->immediate) ||
+				index->indpred != NIL)
+				continue;
+
+			idx_pathkeys = build_index_pathkeys(root, index,
+												ForwardScanDirection);
+			if (!idx_pathkeys)
+				continue;
+
+			if (!do_conjunction)
+			{
+				result = lappend(result, idx_pathkeys);
+				nindex++;
+			}
+			else
+			{
+				/*
+				 * AND operation: drop pathkeys which don't see identical
+				 * one in this relation.
+				 */
+				int i;
+
+				for (i = 0 ; i < prev_nindex ; i++)
+				{
+					if (compare_pathkeys(pathkeys_array[i],
+										 idx_pathkeys,
+										 false) == PATHKEYS_EQUAL)
+						break;
+				}
+				if (prev_nindex < 1 || i < prev_nindex)
+					pathkeys_array2[nindex++] = idx_pathkeys;
+			}
+		}
+
+		if (nindex == 0)
+		{
+			/* No common primary pathkeys remained, exit. */
+			if (pathkeys_array0)
+				pfree(pathkeys_array0);
+			return NULL;
+		}
+
+		if (do_conjunction)
+		{
+			List **pktmp;
+
+			/* swap two pathkeys arrays for the next iteration */
+			pktmp = pathkeys_array2;
+			pathkeys_array2 = pathkeys_array;
+			pathkeys_array = pktmp;
+		}
+	}
+
+	if (do_conjunction)
+	{
+		int i;
+
+		for (i = 0 ; i < nindex ; i++)
+		{
+			if (pathkeys_array[i])
+				result = lappend(result, pathkeys_array[i]);
+		}
+		pfree(pathkeys_array0);
+	}
+
+	return result;
+}
+
+/*
+ * trim_query_pathkeys: trim pathkeys following common primary pathkeys.
+ *
+ * Primary pathkeys are pathkeys which perfectly sorts tuples in its owner
+ * table so the query pathkeys can be replaced with the primary pathkeys when
+ * the latter prefixes the former. Common primary pathkeys are the primary
+ * pathkeys shared among all base relations accessed in current root.
+ *
+ * This function doesn't trim RowExpr pathkeys.
+ */
+static void
+trim_query_pathkeys(PlannerInfo * root)
+{
+	ListCell   *lc;
+
+	foreach(lc, collect_common_primary_pathkeys(root))
+	{
+		List *pk_guide = (List*) lfirst(lc);
+
+		if (root->sort_pathkeys)
+		{
+			/* sort_pathkeys is not allowd to flip sorting directions
+			 * differently from other pathkeys. So try to trim sort_pathkeys
+			 * first preserving per-key directions, then try to trim other
+			 * pathkeys using the trimmed sort_pathkeys as the guide.
+			 */
+
+			root->sort_pathkeys = 
+				shorten_pathkeys_following(root, root->sort_pathkeys, pk_guide,
+										   false);
+
+			/*
+			 * Replace guide pathkeys with new sort_pathkeys if they differs
+			 * from each other only by sorting directions.
+			 */
+			if (compare_pathkeys(root->sort_pathkeys,
+								 pk_guide, false) !=  PATHKEYS_EQUAL &&
+				compare_pathkeys(root->sort_pathkeys,
+								 pk_guide, true) == PATHKEYS_EQUAL)
+				pk_guide = root->sort_pathkeys;
+		}			
+
+		root->group_pathkeys =
+			shorten_pathkeys_following(root, root->group_pathkeys, pk_guide,
+									   true);
+
+		root->window_pathkeys =
+			shorten_pathkeys_following(root, root->window_pathkeys, pk_guide,
+									   true);
+
+		root->distinct_pathkeys =
+			shorten_pathkeys_following(root, root->distinct_pathkeys, pk_guide,
+									   true);
+
+		root->query_pathkeys =
+			shorten_pathkeys_following(root, root->query_pathkeys, pk_guide,
+									   true);
+	}	
+}
 
 /*
  * make_one_rel
@@ -149,6 +348,7 @@ make_one_rel(PlannerInfo *root, List *joinlist)
 	 * Generate access paths for the base rels.
 	 */
 	set_base_rel_sizes(root);
+	trim_query_pathkeys(root);
 	set_base_rel_pathlists(root);
 
 	/*
@@ -755,7 +955,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 					List	   *existing_pathkeys = (List *) lfirst(lpk);
 
 					if (compare_pathkeys(existing_pathkeys,
-										 childkeys) == PATHKEYS_EQUAL)
+										 childkeys, false) == PATHKEYS_EQUAL)
 					{
 						found = true;
 						break;
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 5d953df..6cc9754 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -275,10 +275,13 @@ make_pathkey_from_sortop(PlannerInfo *root,
  *	  one is "better" than the other.
  *
  *	  We assume the pathkeys are canonical, and so they can be checked for
- *	  equality by simple pointer comparison.
+ *	  equality by simple pointer comparison when ignore_strategy is false.
+ *
+ *	  If ignore_strategy is true, the two pathkeys with no difference other
+ *	  than strategy are regarded as the same.
  */
 PathKeysComparison
-compare_pathkeys(List *keys1, List *keys2)
+compare_pathkeys(List *keys1, List *keys2, bool ignore_strategy)
 {
 	ListCell   *key1,
 			   *key2;
@@ -296,7 +299,23 @@ compare_pathkeys(List *keys1, List *keys2)
 		PathKey    *pathkey1 = (PathKey *) lfirst(key1);
 		PathKey    *pathkey2 = (PathKey *) lfirst(key2);
 
-		if (pathkey1 != pathkey2)
+		if (pathkey1 == pathkey2)
+			continue;
+
+		if (!ignore_strategy)
+			return PATHKEYS_DIFFERENT;	/* no need to keep looking */
+
+		Assert(pathkey1->pk_strategy == BTGreaterStrategyNumber ||
+			   pathkey1->pk_strategy == BTLessStrategyNumber);
+		Assert(pathkey2->pk_strategy == BTGreaterStrategyNumber ||
+			   pathkey2->pk_strategy == BTLessStrategyNumber);
+
+		if (pathkey1->pk_eclass   != pathkey2->pk_eclass ||
+			pathkey1->pk_opfamily != pathkey2->pk_opfamily ||
+			!((pathkey1->pk_strategy    == pathkey2->pk_strategy &&
+			   pathkey1->pk_nulls_first == pathkey2->pk_nulls_first) ||
+			  (pathkey1->pk_strategy    != pathkey2->pk_strategy &&
+			   pathkey1->pk_nulls_first != pathkey2->pk_nulls_first)))
 			return PATHKEYS_DIFFERENT;	/* no need to keep looking */
 	}
 
@@ -319,7 +338,7 @@ compare_pathkeys(List *keys1, List *keys2)
 bool
 pathkeys_contained_in(List *keys1, List *keys2)
 {
-	switch (compare_pathkeys(keys1, keys2))
+	switch (compare_pathkeys(keys1, keys2, false))
 	{
 		case PATHKEYS_EQUAL:
 		case PATHKEYS_BETTER2:
@@ -440,10 +459,15 @@ build_index_pathkeys(PlannerInfo *root,
 	List	   *retval = NIL;
 	ListCell   *lc;
 	int			i;
+	List	   *cached_pathkeys = (ScanDirectionIsBackward(scandir) ?
+								   index->indbwpathkeys : index->indfwpathkeys);
 
 	if (index->sortopfamily == NULL)
 		return NIL;				/* non-orderable index */
 
+	if (cached_pathkeys)
+		return cached_pathkeys; /* Return cached pathkeys if any */
+
 	i = 0;
 	foreach(lc, index->indextlist)
 	{
@@ -498,6 +522,12 @@ build_index_pathkeys(PlannerInfo *root,
 		i++;
 	}
 
+	/* Store created pathkeys for future use */
+	if (ScanDirectionIsBackward(scandir))
+		index->indbwpathkeys = retval;
+	else
+		index->indfwpathkeys = retval;
+
 	return retval;
 }
 
@@ -1525,3 +1555,87 @@ has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel)
 		return true;			/* might be able to use them for ordering */
 	return false;				/* definitely useless */
 }
+
+/*
+ * shorten_pathkeys_following: returns pk_guide if it prefixes pk_target
+ * regardless of the ordering directions of the pathkeys.
+ * allow_partial_flip allows ordering directions to be partially different
+ * between pk_target and pk_guide.
+ */
+List *
+shorten_pathkeys_following(PlannerInfo *root, List *pk_target, List *pk_guide,
+	bool allow_partial_flip)
+{
+	ListCell *tlc, *glc;
+	int same = 0, reverse = 0;
+	List *result;
+
+	if (pk_target == NIL)
+		return NIL;
+
+	if (list_length(pk_target) < list_length(pk_guide))
+		return pk_target;
+
+	forboth(tlc, pk_target, glc, pk_guide)
+	{
+		PathKey *tpk = (PathKey *) lfirst(tlc);
+		PathKey *gpk = (PathKey *) lfirst(glc);
+
+		if (tpk == gpk)
+		{
+			same++;
+			continue;
+		}
+
+		Assert(tpk->pk_strategy == BTGreaterStrategyNumber ||
+			   tpk->pk_strategy == BTLessStrategyNumber);
+		Assert(gpk->pk_strategy == BTGreaterStrategyNumber ||
+			   gpk->pk_strategy == BTLessStrategyNumber);
+
+		if (tpk->pk_eclass == gpk->pk_eclass &&
+			tpk->pk_opfamily == gpk->pk_opfamily &&
+			tpk->pk_strategy != gpk->pk_strategy &&
+			tpk->pk_nulls_first != gpk->pk_nulls_first)
+		{
+			reverse++;
+			continue;
+		}
+
+		return pk_target;
+	}
+
+	/* trailing members in pk_target don't matter */
+
+	/* reverse == 0 means that pk_guide exactly prefixes pk_target */
+	if (allow_partial_flip || reverse == 0)
+		return pk_guide;
+
+	/*
+	 * Don't replace pathkeys if partial flip is not allowed but partially
+	 * flipped.
+	 */
+	Assert(reverse > 0);
+	if (same > 0)
+		return pk_target;
+
+	/* make then reutrn the reverse pathkeys of pk_guide */
+	result = NIL;
+	foreach (glc, pk_guide)
+	{
+		PathKey *gpk = (PathKey *) lfirst(glc);
+		PathKey *pk;
+		EquivalenceClass *ec = gpk->pk_eclass;
+		int revstr;
+
+		if (gpk->pk_strategy == BTGreaterStrategyNumber)
+			revstr = BTLessStrategyNumber;
+		else
+			revstr = BTGreaterStrategyNumber;
+
+		pk = make_canonical_pathkey(root, ec, gpk->pk_opfamily, revstr,
+									!gpk->pk_nulls_first);
+
+		result = lappend(result, pk);
+	}
+	return result;
+}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index d129f8d..9433a40 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -312,14 +312,14 @@ set_cheapest(RelOptInfo *parent_rel)
 			if (cmp > 0 ||
 				(cmp == 0 &&
 				 compare_pathkeys(cheapest_startup_path->pathkeys,
-								  path->pathkeys) == PATHKEYS_BETTER2))
+								  path->pathkeys, false) == PATHKEYS_BETTER2))
 				cheapest_startup_path = path;
 
 			cmp = compare_path_costs(cheapest_total_path, path, TOTAL_COST);
 			if (cmp > 0 ||
 				(cmp == 0 &&
 				 compare_pathkeys(cheapest_total_path->pathkeys,
-								  path->pathkeys) == PATHKEYS_BETTER2))
+								  path->pathkeys, false) == PATHKEYS_BETTER2))
 				cheapest_total_path = path;
 		}
 	}
@@ -455,7 +455,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 
 			old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
 			keyscmp = compare_pathkeys(new_path_pathkeys,
-									   old_path_pathkeys);
+									   old_path_pathkeys, false);
 			if (keyscmp != PATHKEYS_DIFFERENT)
 			{
 				switch (costcmp)
@@ -653,7 +653,7 @@ add_path_precheck(RelOptInfo *parent_rel,
 
 				old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
 				keyscmp = compare_pathkeys(new_path_pathkeys,
-										   old_path_pathkeys);
+										   old_path_pathkeys, false);
 				if (keyscmp == PATHKEYS_EQUAL ||
 					keyscmp == PATHKEYS_BETTER2)
 				{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index b2becfa..1cebd2b 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -241,12 +241,28 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns);
 				info->nulls_first = (bool *) palloc(sizeof(bool) * ncolumns);
 
+				info->allnotnull = true;
 				for (i = 0; i < ncolumns; i++)
 				{
 					int16		opt = indexRelation->rd_indoption[i];
+					int16		attno = info->indexkeys[i];
 
 					info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
 					info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
+
+					if (!info->allnotnull) continue;
+
+					/* Check if there's no nullable key column */
+					if (attno > 0)
+					{
+						if (!relation->rd_att->attrs[attno - 1]->attnotnull)
+							info->allnotnull = false;
+					}
+					else if (attno != ObjectIdAttributeNumber)
+					{
+						/* OID is apparently not-null */
+						info->allnotnull = false; 
+					}
 				}
 			}
 			else if (indexRelation->rd_am->amcanorder)
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index dacbe9c..4855667 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -524,10 +524,13 @@ typedef struct IndexOptInfo
 	List	   *indpred;		/* predicate if a partial index, else NIL */
 
 	List	   *indextlist;		/* targetlist representing index columns */
+	List	   *indfwpathkeys;	/* index pathkeys for forward scan */
+	List	   *indbwpathkeys;	/* index pathkeys for backwad scan */
 
 	bool		predOK;			/* true if predicate matches query */
 	bool		unique;			/* true if a unique index */
 	bool		immediate;		/* is uniqueness enforced immediately? */
+	bool		allnotnull;		/* all columns are "NOT NULL" ? */
 	bool		hypothetical;	/* true if index doesn't really exist */
 	bool		canreturn;		/* can index return IndexTuples? */
 	bool		amcanorderbyop; /* does AM support order by operator result? */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9b22fda..f637fc3 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -146,7 +146,10 @@ typedef enum
 	PATHKEYS_DIFFERENT			/* neither pathkey includes the other */
 } PathKeysComparison;
 
-extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2);
+extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2,
+										   bool ignore_strategy);
+extern PathKeysComparison compare_pathkeys_ignoring_strategy(List *keys1,
+															 List *keys2);
 extern bool pathkeys_contained_in(List *keys1, List *keys2);
 extern Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys,
 							   Relids required_outer,
@@ -187,5 +190,8 @@ extern List *truncate_useless_pathkeys(PlannerInfo *root,
 						  RelOptInfo *rel,
 						  List *pathkeys);
 extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel);
+extern List *shorten_pathkeys_following(PlannerInfo *root, 
+										List *pk_target, List *pk_guide,
+										bool allow_partial_flip);
 
 #endif   /* PATHS_H */
diff --git a/src/test/isolation/expected/drop-index-concurrently-1.out b/src/test/isolation/expected/drop-index-concurrently-1.out
index 75dff56..ab96fa0 100644
--- a/src/test/isolation/expected/drop-index-concurrently-1.out
+++ b/src/test/isolation/expected/drop-index-concurrently-1.out
@@ -19,10 +19,8 @@ Sort
 step explains: EXPLAIN (COSTS OFF) EXECUTE getrow_seq;
 QUERY PLAN     
 
-Sort           
-  Sort Key: id, data
-  ->  Seq Scan on test_dc
-        Filter: ((data)::text = '34'::text)
+Index Scan using test_dc_pkey on test_dc
+  Filter: ((data)::text = '34'::text)
 step select2: SELECT * FROM test_dc WHERE data=34 ORDER BY id,data;
 id             data           
 
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to