|
14 | 14 | * Copyright (c) 1998-2007, PostgreSQL Global Development Group
|
15 | 15 | *
|
16 | 16 | * IDENTIFICATION
|
17 |
| - * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.101 2007/02/27 23:48:08 tgl Exp $ |
| 17 | + * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.102 2007/05/08 18:56:47 neilc Exp $ |
18 | 18 | *
|
19 | 19 | *-------------------------------------------------------------------------
|
20 | 20 | */
|
|
26 | 26 | #include <limits.h>
|
27 | 27 | #include <math.h>
|
28 | 28 |
|
| 29 | +#include "access/hash.h" |
29 | 30 | #include "catalog/pg_type.h"
|
30 | 31 | #include "libpq/pqformat.h"
|
31 | 32 | #include "utils/array.h"
|
@@ -1149,6 +1150,81 @@ cmp_numerics(Numeric num1, Numeric num2)
|
1149 | 1150 | return result;
|
1150 | 1151 | }
|
1151 | 1152 |
|
| 1153 | +Datum |
| 1154 | +hash_numeric(PG_FUNCTION_ARGS) |
| 1155 | +{ |
| 1156 | + Numeric key = PG_GETARG_NUMERIC(0); |
| 1157 | + Datum digit_hash; |
| 1158 | + Datum result; |
| 1159 | + int weight; |
| 1160 | + int start_offset; |
| 1161 | + int end_offset; |
| 1162 | + int i; |
| 1163 | + int hash_len; |
| 1164 | + |
| 1165 | + /* If it's NaN, don't try to hash the rest of the fields */ |
| 1166 | + if (NUMERIC_IS_NAN(key)) |
| 1167 | + PG_RETURN_UINT32(0); |
| 1168 | + |
| 1169 | + weight = key->n_weight; |
| 1170 | + start_offset = 0; |
| 1171 | + end_offset = 0; |
| 1172 | + |
| 1173 | + /* |
| 1174 | + * Omit any leading or trailing zeros from the input to the |
| 1175 | + * hash. The numeric implementation *should* guarantee that |
| 1176 | + * leading and trailing zeros are suppressed, but we're |
| 1177 | + * paranoid. Note that we measure the starting and ending offsets |
| 1178 | + * in units of NumericDigits, not bytes. |
| 1179 | + */ |
| 1180 | + for (i = 0; i < NUMERIC_NDIGITS(key); i++) |
| 1181 | + { |
| 1182 | + if (NUMERIC_DIGITS(key)[i] != (NumericDigit) 0) |
| 1183 | + break; |
| 1184 | + |
| 1185 | + start_offset++; |
| 1186 | + /* |
| 1187 | + * The weight is effectively the # of digits before the |
| 1188 | + * decimal point, so decrement it for each leading zero we |
| 1189 | + * skip. |
| 1190 | + */ |
| 1191 | + weight--; |
| 1192 | + } |
| 1193 | + |
| 1194 | + /* |
| 1195 | + * If there are no non-zero digits, then the value of the number |
| 1196 | + * is zero, regardless of any other fields. |
| 1197 | + */ |
| 1198 | + if (NUMERIC_NDIGITS(key) == start_offset) |
| 1199 | + PG_RETURN_UINT32(-1); |
| 1200 | + |
| 1201 | + for (i = NUMERIC_NDIGITS(key) - 1; i >= 0; i--) |
| 1202 | + { |
| 1203 | + if (NUMERIC_DIGITS(key)[i] != (NumericDigit) 0) |
| 1204 | + break; |
| 1205 | + |
| 1206 | + end_offset++; |
| 1207 | + } |
| 1208 | + |
| 1209 | + /* If we get here, there should be at least one non-zero digit */ |
| 1210 | + Assert(start_offset + end_offset < NUMERIC_NDIGITS(key)); |
| 1211 | + |
| 1212 | + /* |
| 1213 | + * Note that we don't hash on the Numeric's scale, since two |
| 1214 | + * numerics can compare equal but have different scales. We also |
| 1215 | + * don't hash on the sign, although we could: since a sign |
| 1216 | + * difference implies inequality, this shouldn't affect correctness. |
| 1217 | + */ |
| 1218 | + hash_len = NUMERIC_NDIGITS(key) - start_offset - end_offset; |
| 1219 | + digit_hash = hash_any((unsigned char *) (NUMERIC_DIGITS(key) + start_offset), |
| 1220 | + hash_len * sizeof(NumericDigit)); |
| 1221 | + |
| 1222 | + /* Mix in the weight, via XOR */ |
| 1223 | + result = digit_hash ^ weight; |
| 1224 | + |
| 1225 | + PG_RETURN_DATUM(result); |
| 1226 | +} |
| 1227 | + |
1152 | 1228 |
|
1153 | 1229 | /* ----------------------------------------------------------------------
|
1154 | 1230 | *
|
|
0 commit comments