|
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 | returnresult;
|
1150 | 1151 | }
|
1151 | 1152 |
|
| 1153 | +Datum |
| 1154 | +hash_numeric(PG_FUNCTION_ARGS) |
| 1155 | +{ |
| 1156 | +Numerickey=PG_GETARG_NUMERIC(0); |
| 1157 | +Datumdigit_hash; |
| 1158 | +Datumresult; |
| 1159 | +intweight; |
| 1160 | +intstart_offset; |
| 1161 | +intend_offset; |
| 1162 | +inti; |
| 1163 | +inthash_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((unsignedchar*) (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 | *
|
|