]> granicus.if.org Git - postgresql/commitdiff
Add a transform function for numeric typmod coercisions.
authorRobert Haas <rhaas@postgresql.org>
Tue, 7 Feb 2012 17:08:26 +0000 (12:08 -0500)
committerRobert Haas <rhaas@postgresql.org>
Tue, 7 Feb 2012 17:08:26 +0000 (12:08 -0500)
This enables ALTER TABLE to skip table and index rebuilds when a column
is changed to an unconstrained numeric, or when the scale is unchanged
and the precision does not decrease.

Noah Misch, with a few stylistic changes and a fix for an OID
collision by me.

src/backend/utils/adt/numeric.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h

index 9121418190f002270424d5f4db42ac0f24e9284a..6885349de58ed56497ec76f8ccc52d4a6ad0dd1e 100644 (file)
@@ -30,6 +30,8 @@
 #include "catalog/pg_type.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_clause.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/int8.h"
@@ -712,6 +714,52 @@ numeric_send(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * numeric_transform() -
+ *
+ * Flatten calls to our length coercion function that solely represent
+ * increases in allowable precision.  Scale changes mutate every datum, so
+ * they are unoptimizable.  Some values, e.g. 1E-1001, can only fit into an
+ * unconstrained numeric, so a change from an unconstrained numeric to any
+ * constrained numeric is also unoptimizable.
+ */
+Datum
+numeric_transform(PG_FUNCTION_ARGS)
+{
+       FuncExpr   *expr = (FuncExpr *) PG_GETARG_POINTER(0);
+       Node       *typmod;
+       Node       *ret = NULL;
+
+       if (!IsA(expr, FuncExpr))
+               PG_RETURN_POINTER(ret);
+
+       Assert(list_length(expr->args) == 2);
+       typmod = lsecond(expr->args);
+
+       if (IsA(typmod, Const))
+       {
+               Node       *source = linitial(expr->args);
+               int32           old_typmod = exprTypmod(source);
+               int32           new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+               int32           old_scale = (old_typmod - VARHDRSZ) & 0xffff;
+               int32           new_scale = (new_typmod - VARHDRSZ) & 0xffff;
+               int32           old_precision = (old_typmod - VARHDRSZ) >> 16 & 0xffff;
+               int32           new_precision = (new_typmod - VARHDRSZ) >> 16 & 0xffff;
+
+               /*
+                * If new_typmod < VARHDRSZ, the destination is unconstrained; that's
+                * always OK.  If old_typmod >= VARHDRSZ, the source is constained.
+                * and we're OK if the scale is unchanged and the precison is not
+                * decreasing.  See further nodes in function header comment.
+                */
+               if (new_typmod < VARHDRSZ || (old_typmod >= VARHDRSZ &&
+                        new_scale == old_scale && new_precision >= old_precision))
+                       ret = relabel_to_typmod(source, new_typmod);
+       }
+
+       PG_RETURN_POINTER(ret);
+}
+
 /*
  * numeric() -
  *
index 23ab2f7fdc6435299752b3cdead03d57f60a79f4..a748c5e623ec78a2e8bbbacd9e98786e28446dda 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201201311
+#define CATALOG_VERSION_NO     201202071
 
 #endif
index 8fc4ddb4b569e39ae9d3a139f547ffbdde6b6182..006ed171824bb788192fdb22b15edba1c2a09764 100644 (file)
@@ -2142,8 +2142,10 @@ DATA(insert OID = 2917 (  numerictypmodin                PGNSP PGUID 12 1 0 0 0 f f f t f i 1
 DESCR("I/O typmod");
 DATA(insert OID = 2918 (  numerictypmodout             PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2275 "23" _null_ _null_ _null_ _null_    numerictypmodout _null_ _null_ _null_ ));
 DESCR("I/O typmod");
-DATA(insert OID = 1703 ( numeric                               PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 1700 "1700 23" _null_ _null_ _null_ _null_ numeric _null_ _null_ _null_ ));
+DATA(insert OID = 1703 ( numeric                               PGNSP PGUID 12 1 0 0 3157 f f f t f i 2 0 1700 "1700 23" _null_ _null_ _null_ _null_ numeric _null_ _null_ _null_ ));
 DESCR("adjust numeric to typmod precision/scale");
+DATA(insert OID = 3157 ( numeric_transform             PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ numeric_transform _null_ _null_ _null_ ));
+DESCR("transform a numeric length coercion");
 DATA(insert OID = 1704 ( numeric_abs                   PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 1700 "1700" _null_ _null_ _null_ _null_ numeric_abs _null_ _null_ _null_ ));
 DATA(insert OID = 1705 ( abs                                   PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 1700 "1700" _null_ _null_ _null_ _null_ numeric_abs _null_ _null_ _null_ ));
 DESCR("absolute value");
index 68179d550f899b6989a91f2609837e99312330f6..2c331ce5eb9b2dc2a0f5db63be57f8af0db74ad5 100644 (file)
@@ -913,6 +913,7 @@ extern Datum numeric_recv(PG_FUNCTION_ARGS);
 extern Datum numeric_send(PG_FUNCTION_ARGS);
 extern Datum numerictypmodin(PG_FUNCTION_ARGS);
 extern Datum numerictypmodout(PG_FUNCTION_ARGS);
+extern Datum numeric_transform(PG_FUNCTION_ARGS);
 extern Datum numeric (PG_FUNCTION_ARGS);
 extern Datum numeric_abs(PG_FUNCTION_ARGS);
 extern Datum numeric_uminus(PG_FUNCTION_ARGS);