@@ -80,7 +80,10 @@ static const struct am_propname am_propnames[] =
8080 },
8181 {
8282 "can_exclude" , AMPROP_CAN_EXCLUDE
83- }
83+ },
84+ {
85+ "can_include" , AMPROP_CAN_INCLUDE
86+ },
8487};
8588
8689static IndexAMProperty
@@ -101,7 +104,8 @@ lookup_prop_name(const char *name)
101104/*
102105 * Common code for properties that are just bit tests of indoptions.
103106 *
104- * relid/attno: identify the index column to test the indoptions of.
107+ * tuple: the pg_index heaptuple
108+ * attno: identify the index column to test the indoptions of.
105109 * guard: if false, a boolean false result is forced (saves code in caller).
106110 * iopt_mask: mask for interesting indoption bit.
107111 * iopt_expect: value for a "true" result (should be 0 or iopt_mask).
@@ -110,12 +114,10 @@ lookup_prop_name(const char *name)
110114 * otherwise sets *res to the boolean value to return.
111115 */
112116static bool
113- test_indoption (Oid relid , int attno , bool guard ,
117+ test_indoption (HeapTuple tuple , int attno , bool guard ,
114118 int16 iopt_mask , int16 iopt_expect ,
115119 bool * res )
116120{
117- HeapTuple tuple ;
118- Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY ;
119121 Datum datum ;
120122 bool isnull ;
121123 int2vector * indoption ;
@@ -127,14 +129,6 @@ test_indoption(Oid relid, int attno, bool guard,
127129 return true;
128130 }
129131
130- tuple = SearchSysCache1 (INDEXRELID , ObjectIdGetDatum (relid ));
131- if (!HeapTupleIsValid (tuple ))
132- return false;
133- rd_index = (Form_pg_index ) GETSTRUCT (tuple );
134-
135- Assert (relid == rd_index -> indexrelid );
136- Assert (attno > 0 && attno <= rd_index -> indnatts );
137-
138132 datum = SysCacheGetAttr (INDEXRELID , tuple ,
139133 Anum_pg_index_indoption , & isnull );
140134 Assert (!isnull );
@@ -144,8 +138,6 @@ test_indoption(Oid relid, int attno, bool guard,
144138
145139 * res = (indoption_val & iopt_mask ) == iopt_expect ;
146140
147- ReleaseSysCache (tuple );
148-
149141 return true;
150142}
151143
@@ -195,9 +187,10 @@ indexam_property(FunctionCallInfo fcinfo,
195187 }
196188
197189 /*
198- * At this point, either index_oid == InvalidOid or it's a valid index
199- * OID. Also, after this test, either attno == 0 for index-wide or
200- * AM-wide tests, or it's a valid column number in a valid index.
190+ * At this point, either index_oid == InvalidOid or it's a valid index OID.
191+ * Also, after this test and the one below, either attno == 0 for
192+ * index-wide or AM-wide tests, or it's a valid column number in a valid
193+ * index.
201194 */
202195 if (attno < 0 || attno > natts )
203196 PG_RETURN_NULL ();
@@ -224,80 +217,138 @@ indexam_property(FunctionCallInfo fcinfo,
224217
225218 if (attno > 0 )
226219 {
227- /* Handle column-level properties */
220+ HeapTuple tuple ;
221+ Form_pg_index rd_index ;
222+ bool iskey = true;
223+
224+ /*
225+ * Handle column-level properties. Many of these need the pg_index row
226+ * (which we also need to use to check for nonkey atts) so we fetch
227+ * that first.
228+ */
229+ tuple = SearchSysCache1 (INDEXRELID , ObjectIdGetDatum (index_oid ));
230+ if (!HeapTupleIsValid (tuple ))
231+ PG_RETURN_NULL ();
232+ rd_index = (Form_pg_index ) GETSTRUCT (tuple );
233+
234+ Assert (index_oid == rd_index -> indexrelid );
235+ Assert (attno > 0 && attno <= rd_index -> indnatts );
236+
237+ isnull = true;
238+
239+ /*
240+ * If amcaninclude, we might be looking at an attno for a nonkey
241+ * column, for which we (generically) assume that most properties are
242+ * null.
243+ */
244+ if (routine -> amcaninclude
245+ && attno > rd_index -> indnkeyatts )
246+ iskey = false;
247+
228248 switch (prop )
229249 {
230250 case AMPROP_ASC :
231- if (test_indoption (index_oid , attno , routine -> amcanorder ,
251+ if (iskey &&
252+ test_indoption (tuple , attno , routine -> amcanorder ,
232253 INDOPTION_DESC , 0 , & res ))
233- PG_RETURN_BOOL ( res ) ;
234- PG_RETURN_NULL () ;
254+ isnull = false ;
255+ break ;
235256
236257 case AMPROP_DESC :
237- if (test_indoption (index_oid , attno , routine -> amcanorder ,
258+ if (iskey &&
259+ test_indoption (tuple , attno , routine -> amcanorder ,
238260 INDOPTION_DESC , INDOPTION_DESC , & res ))
239- PG_RETURN_BOOL ( res ) ;
240- PG_RETURN_NULL () ;
261+ isnull = false ;
262+ break ;
241263
242264 case AMPROP_NULLS_FIRST :
243- if (test_indoption (index_oid , attno , routine -> amcanorder ,
265+ if (iskey &&
266+ test_indoption (tuple , attno , routine -> amcanorder ,
244267 INDOPTION_NULLS_FIRST , INDOPTION_NULLS_FIRST , & res ))
245- PG_RETURN_BOOL ( res ) ;
246- PG_RETURN_NULL () ;
268+ isnull = false ;
269+ break ;
247270
248271 case AMPROP_NULLS_LAST :
249- if (test_indoption (index_oid , attno , routine -> amcanorder ,
272+ if (iskey &&
273+ test_indoption (tuple , attno , routine -> amcanorder ,
250274 INDOPTION_NULLS_FIRST , 0 , & res ))
251- PG_RETURN_BOOL ( res ) ;
252- PG_RETURN_NULL () ;
275+ isnull = false ;
276+ break ;
253277
254278 case AMPROP_ORDERABLE :
255- PG_RETURN_BOOL (routine -> amcanorder );
279+ /*
280+ * generic assumption is that nonkey columns are not orderable
281+ */
282+ res = iskey ? routine -> amcanorder : false;
283+ isnull = false;
284+ break ;
256285
257286 case AMPROP_DISTANCE_ORDERABLE :
258287
259288 /*
260289 * The conditions for whether a column is distance-orderable
261290 * are really up to the AM (at time of writing, only GiST
262- * supports it at all). The planner has its own idea based on
291+ * supports it at all). The planner has its own idea based on
263292 * whether it finds an operator with amoppurpose 'o', but
264293 * getting there from just the index column type seems like a
265- * lot of work. So instead we expect the AM to handle this in
266- * its amproperty routine. The generic result is to return
267- * false if the AM says it never supports this, and null
268- * otherwise (meaning we don't know).
294+ * lot of work. So instead we expect the AM to handle this in
295+ * its amproperty routine. The generic result is to return
296+ * false if the AM says it never supports this, or if this is a
297+ * nonkey column, and null otherwise (meaning we don't know).
269298 */
270- if (!routine -> amcanorderbyop )
271- PG_RETURN_BOOL (false);
272- PG_RETURN_NULL ();
299+ if (!iskey || !routine -> amcanorderbyop )
300+ {
301+ res = false;
302+ isnull = false;
303+ }
304+ break ;
273305
274306 case AMPROP_RETURNABLE :
275- if (!routine -> amcanreturn )
276- PG_RETURN_BOOL (false);
277307
278- /*
279- * If possible, the AM should handle this test in its
280- * amproperty function without opening the rel. But this is
281- * the generic fallback if it does not.
282- */
308+ /* note that we ignore iskey for this property */
309+
310+ isnull = false;
311+ res = false;
312+
313+ if (routine -> amcanreturn )
283314 {
315+ /*
316+ * If possible, the AM should handle this test in its
317+ * amproperty function without opening the rel. But this is the
318+ * generic fallback if it does not.
319+ */
284320 Relation indexrel = index_open (index_oid , AccessShareLock );
285321
286322 res = index_can_return (indexrel , attno );
287323 index_close (indexrel , AccessShareLock );
288324 }
289-
290- PG_RETURN_BOOL (res );
325+ break ;
291326
292327 case AMPROP_SEARCH_ARRAY :
293- PG_RETURN_BOOL (routine -> amsearcharray );
328+ if (iskey )
329+ {
330+ res = routine -> amsearcharray ;
331+ isnull = false;
332+ }
333+ break ;
294334
295335 case AMPROP_SEARCH_NULLS :
296- PG_RETURN_BOOL (routine -> amsearchnulls );
336+ if (iskey )
337+ {
338+ res = routine -> amsearchnulls ;
339+ isnull = false;
340+ }
341+ break ;
297342
298343 default :
299- PG_RETURN_NULL () ;
344+ break ;
300345 }
346+
347+ ReleaseSysCache (tuple );
348+
349+ if (!isnull )
350+ PG_RETURN_BOOL (res );
351+ PG_RETURN_NULL ();
301352 }
302353
303354 if (OidIsValid (index_oid ))
@@ -344,6 +395,9 @@ indexam_property(FunctionCallInfo fcinfo,
344395 case AMPROP_CAN_EXCLUDE :
345396 PG_RETURN_BOOL (routine -> amgettuple ? true : false);
346397
398+ case AMPROP_CAN_INCLUDE :
399+ PG_RETURN_BOOL (routine -> amcaninclude );
400+
347401 default :
348402 PG_RETURN_NULL ();
349403 }
0 commit comments