commit aca5ff23bb3352f5441e76597e637033a0f0a53d
Author: Robert Haas <rhaas@postgresql.org>
Date:   Thu Nov 9 16:32:12 2017 -0500

    adjustments

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b0f1038c20..daba66c187 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2881,11 +2881,10 @@ VALUES ('Albany', NULL, NULL, 'NY');
 
        <listitem>
         <para>
-         The table is partitioned by specifying a modulus and a remainder for each
-         partition. Each partition will hold the rows for which the hash value of
-         the partition key divided by the specified modulus will produce the specified
-         remainder. Refer to <xref linkend="sql-createtable-partition">
-         for additional clarification on modulus and remainder.
+         The table is partitioned by specifying a modulus and a remainder for
+         each partition. Each partition will hold the rows for which the hash
+         value of the partition key divided by the specified modulus will
+         produce the specified remainder.
         </para>
        </listitem>
       </varlistentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index b476afd3f7..bbb3a51def 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -257,8 +257,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       Creates the table as a <firstterm>partition</firstterm> of the specified
       parent table. The table can be created either as a partition for specific
       values using <literal>FOR VALUES</literal> or as a default partition
-      using <literal>DEFAULT</literal>.  Hash partitioned tables do not support
-      a default partition.
+      using <literal>DEFAULT</literal>.  This option is not available for
+      hash-partitioned tables.
      </para>
 
      <para>
@@ -266,9 +266,9 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       must correspond to the partitioning method and partition key of the
       parent table, and must not overlap with any existing partition of that
       parent.  The form with <literal>IN</literal> is used for list partitioning,
-      the form with <literal>FROM</literal> and <literal>TO</literal> is used for
-      range partitioning, and the form with <literal>WITH</literal> is used for
-      hash partitioning.
+      the form with <literal>FROM</literal> and <literal>TO</literal> is used
+      for range partitioning, and the form with <literal>WITH</literal> is used
+      for hash partitioning.
      </para>
 
      <para>
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index b8e800db7e..cff59ed055 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -172,8 +172,8 @@ static int partition_bound_bsearch(PartitionKey key,
 						void *probe, bool probe_is_bound, bool *is_equal);
 static void get_partition_dispatch_recurse(Relation rel, Relation parent,
 							   List **pds, List **leaf_part_oids);
-static int get_partition_bound_num_indexes(PartitionBoundInfo b);
-static int get_greatest_modulus(PartitionBoundInfo b);
+static int	get_partition_bound_num_indexes(PartitionBoundInfo b);
+static int	get_greatest_modulus(PartitionBoundInfo b);
 static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
 
 /* SQL-callable function for use in hash partition CHECK constraints */
@@ -547,7 +547,7 @@ RelationBuildPartitionDesc(Relation rel)
 			case PARTITION_STRATEGY_HASH:
 				{
 					/* Modulus are stored in ascending order */
-					int		greatest_modulus = hbounds[ndatums - 1]->modulus;
+					int			greatest_modulus = hbounds[ndatums - 1]->modulus;
 
 					boundinfo->indexes = (int *) palloc(greatest_modulus *
 														sizeof(int));
@@ -755,11 +755,10 @@ partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval,
 		int			greatest_modulus;
 
 		/*
-		 * If two hash partitioned tables have different greatest moduli or
-		 * same moduli with different number of partitions, their partition
-		 * schemes don't match.  For hash partitioned table, the greatest
-		 * modulus is given by the last datum and number of partitions is
-		 * given by ndatums.
+		 * If two hash partitioned tables have different greatest moduli,
+		 * their partition schemes don't match.  For hash partitioned table,
+		 * the greatest modulus is given by the last datum and number of
+		 * partitions is given by ndatums.
 		 */
 		if (b1->datums[b1->ndatums - 1][0] != b2->datums[b2->ndatums - 1][0])
 			return false;
@@ -853,11 +852,11 @@ extern PartitionBoundInfo
 partition_bounds_copy(PartitionBoundInfo src,
 					  PartitionKey key)
 {
-	PartitionBoundInfo	dest;
-	int		i;
-	int		ndatums;
-	int		partnatts;
-	int		num_indexes;
+	PartitionBoundInfo dest;
+	int			i;
+	int			ndatums;
+	int			partnatts;
+	int			num_indexes;
 
 	dest = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
 
@@ -875,11 +874,11 @@ partition_bounds_copy(PartitionBoundInfo src,
 	if (src->kind != NULL)
 	{
 		dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
-												sizeof(PartitionRangeDatumKind *));
+														 sizeof(PartitionRangeDatumKind *));
 		for (i = 0; i < ndatums; i++)
 		{
 			dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
-												sizeof(PartitionRangeDatumKind));
+															   sizeof(PartitionRangeDatumKind));
 
 			memcpy(dest->kind[i], src->kind[i],
 				   sizeof(PartitionRangeDatumKind) * key->partnatts);
@@ -890,26 +889,26 @@ partition_bounds_copy(PartitionBoundInfo src,
 
 	for (i = 0; i < ndatums; i++)
 	{
-		int		j;
+		int			j;
 
 		/*
 		 * For a corresponding to hash partition, datums array will have two
 		 * elements - modulus and remainder.
 		 */
-		bool	hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
-		int		natts = hash_part? 2 : partnatts;
+		bool		hash_part = (key->strategy == PARTITION_STRATEGY_HASH);
+		int			natts = hash_part ? 2 : partnatts;
 
 		dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts);
 
 		for (j = 0; j < natts; j++)
 		{
-			bool	byval;
-			int		typlen;
+			bool		byval;
+			int			typlen;
 
 			if (hash_part)
 			{
 				typlen = sizeof(int32); /* Always int4 */
-				byval = true;           /* int4 is pass-by-value */
+				byval = true;	/* int4 is pass-by-value */
 			}
 			else
 			{
@@ -1803,8 +1802,7 @@ make_partition_op_expr(PartitionKey key, int keynum,
  * The partition constraint for a hash partition is always a call to the
  * built-in function satisfies_hash_partition().  The first two arguments are
  * the modulus and remainder for the partition; the remaining arguments are the
- * hash values computed for each column of the partition key using the extended
- * hash function from the appropriate opclass.
+ * values to be hashed.
  */
 static List *
 get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
@@ -2754,9 +2752,9 @@ get_partition_for_tuple(PartitionDispatch *pd,
 			case PARTITION_STRATEGY_HASH:
 				{
 					PartitionBoundInfo boundinfo = partdesc->boundinfo;
-					int		greatest_modulus = get_greatest_modulus(boundinfo);
-					uint64	rowHash = compute_hash_value(key, values,
-														 isnull);
+					int			greatest_modulus = get_greatest_modulus(boundinfo);
+					uint64		rowHash = compute_hash_value(key, values,
+															 isnull);
 
 					cur_index = boundinfo->indexes[rowHash % greatest_modulus];
 				}
@@ -3294,13 +3292,14 @@ get_proposed_default_constraint(List *new_part_constraints)
 static int
 get_partition_bound_num_indexes(PartitionBoundInfo bound)
 {
-	int num_indexes;
+	int			num_indexes;
 
 	Assert(bound);
 
 	switch (bound->strategy)
 	{
 		case PARTITION_STRATEGY_HASH:
+
 			/*
 			 * The number of the entries in the indexes array is same as the
 			 * greatest modulus.
@@ -3359,7 +3358,7 @@ compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
 	{
 		if (!isnull[i])
 		{
-			Datum	hash;
+			Datum		hash;
 
 			Assert(OidIsValid(key->partsupfunc[i].fn_oid));
 
@@ -3397,7 +3396,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 	{
 		Oid			relid;
 		int16		nkeys;
-		FmgrInfo   partsupfunc[PARTITION_MAX_KEYS];
+		FmgrInfo	partsupfunc[PARTITION_MAX_KEYS];
 	}			ColumnsHashData;
 	Oid			parentId = PG_GETARG_OID(0);
 	int			modulus = PG_GETARG_INT32(1);
@@ -3417,6 +3416,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 	{
 		Relation	parent;
 		PartitionKey key;
+		int j;
 
 		fcinfo->flinfo->fn_extra =
 			MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
@@ -3431,17 +3431,19 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 		key = RelationGetPartitionKey(parent);
 
 		Assert(key->partnatts == nkeys);
-		memcpy(my_extra->partsupfunc, key->partsupfunc, nkeys * sizeof(FmgrInfo));
+		for (j = 0; j < nkeys; ++j)
+			fmgr_info_copy(&my_extra->partsupfunc[j],
+						   key->partsupfunc,
+						   fcinfo->flinfo->fn_mcxt);
 
-		/* TODO: Should we hold lock until commit? */
-		heap_close(parent, AccessShareLock);
+		/* Hold lock until commit */
+		heap_close(parent, NoLock);
 	}
 
-	/* Get TypeCacheEntry for each partition column. */
 	for (i = 0; i < nkeys; i++)
 	{
 		/* keys start from fourth argument of function. */
-		int argno = i + 3;
+		int			argno = i + 3;
 
 		if (!PG_ARGISNULL(argno))
 		{
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 831eb05644..453f25964a 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1463,7 +1463,7 @@ have_partkey_equi_join(RelOptInfo *rel1, RelOptInfo *rel2, JoinType jointype,
 			continue;
 
 		/* Skip clauses which are not equality conditions. */
-		if (!rinfo->mergeopfamilies)
+		if (!rinfo->mergeopfamilies && !OidIsValid(rinfo->hashjoinoperator))
 			continue;
 
 		opexpr = (OpExpr *) rinfo->clause;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 542b298fd0..8461da490a 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3343,7 +3343,7 @@ transformPartitionBound(ParseState *pstate, Relation parent,
 		if (spec->remainder >= spec->modulus)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-					 errmsg("modulus for hash partition must be greater than remainder")));
+					 errmsg("remainder for hash partition must be less than modulus")));
 	}
 	else if (strategy == PARTITION_STRATEGY_LIST)
 	{
@@ -3510,7 +3510,7 @@ transformPartitionBound(ParseState *pstate, Relation parent,
 static void
 validateInfiniteBounds(ParseState *pstate, List *blist)
 {
-	ListCell *lc;
+	ListCell   *lc;
 	PartitionRangeDatumKind kind = PARTITION_RANGE_DATUM_VALUE;
 
 	foreach(lc, blist)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 39c70b415a..bd4014a69d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201711091
+#define CATALOG_VERSION_NO	201711092
 
 #endif
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 96f20d3168..a68ec91c68 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -245,7 +245,6 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 #define PG_GETARG_FLOAT4(n)  DatumGetFloat4(PG_GETARG_DATUM(n))
 #define PG_GETARG_FLOAT8(n)  DatumGetFloat8(PG_GETARG_DATUM(n))
 #define PG_GETARG_INT64(n)	 DatumGetInt64(PG_GETARG_DATUM(n))
-#define PG_GETARG_UINT64(n)  DatumGetUInt64(PG_GETARG_DATUM(n))
 /* use this if you want the raw, possibly-toasted input datum: */
 #define PG_GETARG_RAW_VARLENA_P(n)	((struct varlena *) PG_GETARG_POINTER(n))
 /* use this if you want the input datum de-toasted: */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index b4af2227e3..11f0baa11b 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3598,7 +3598,7 @@ CREATE TABLE quuux2 PARTITION OF quuux FOR VALUES IN (2);
 INFO:  updated partition constraint for default partition "quuux_default1" is implied by existing constraints
 DROP TABLE quuux;
 -- check validation when attaching hash partitions
--- The default hash functions as they exist today aren't portable, they can
+-- The default hash functions as they exist today aren't portable; they can
 -- return different results on different machines.  Depending upon how the
 -- values are hashed, the row may map to different partitions, which result in
 -- regression failure.  To avoid this, let's create a non-default hash function
@@ -3646,7 +3646,7 @@ CREATE TABLE fail_part(LIKE hash_parted);
 ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 0, REMAINDER 1);
 ERROR:  modulus for hash partition must be a positive integer
 ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 8, REMAINDER 8);
-ERROR:  modulus for hash partition must be greater than remainder
+ERROR:  remainder for hash partition must be less than modulus
 ALTER TABLE hash_parted ATTACH PARTITION fail_part FOR VALUES WITH (MODULUS 3, REMAINDER 2);
 ERROR:  every hash partition modulus must be a factor of the next larger modulus
 DROP TABLE fail_part;
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 87aac1ae98..335cd37e18 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -340,6 +340,11 @@ CREATE TABLE partitioned (
 ) PARTITION BY RANGE (const_func());
 ERROR:  cannot use constant expression as partition key
 DROP FUNCTION const_func();
+-- only accept valid partitioning strategy
+CREATE TABLE partitioned (
+    a int
+) PARTITION BY MAGIC (a);
+ERROR:  unrecognized partitioning strategy "magic"
 -- specified column must be present in the table
 CREATE TABLE partitioned (
 	a int
@@ -677,7 +682,7 @@ CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REM
 ERROR:  modulus for hash partition must be a positive integer
 -- remainder must be greater than or equal to zero and less than modulus
 CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 8);
-ERROR:  modulus for hash partition must be greater than remainder
+ERROR:  remainder for hash partition must be less than modulus
 -- check schema propagation from parent
 CREATE TABLE parted (
 	a text,
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index da1c1441bd..02a33ca7c4 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2335,7 +2335,7 @@ DROP TABLE quuux;
 
 -- check validation when attaching hash partitions
 
--- The default hash functions as they exist today aren't portable, they can
+-- The default hash functions as they exist today aren't portable; they can
 -- return different results on different machines.  Depending upon how the
 -- values are hashed, the row may map to different partitions, which result in
 -- regression failure.  To avoid this, let's create a non-default hash function
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
index 901d66c63b..b77b476436 100644
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -350,6 +350,11 @@ CREATE TABLE partitioned (
 ) PARTITION BY RANGE (const_func());
 DROP FUNCTION const_func();
 
+-- only accept valid partitioning strategy
+CREATE TABLE partitioned (
+    a int
+) PARTITION BY MAGIC (a);
+
 -- specified column must be present in the table
 CREATE TABLE partitioned (
 	a int
