2626#include "utils/array.h"
2727#include "utils/builtins.h"
2828#include "utils/lsyscache.h"
29+ #include "utils/numeric.h"
2930#include "utils/ruleutils.h"
31+ #include "utils/syscache.h"
3032
3133
3234static char * deparse_constraint (Oid relid ,Node * expr );
@@ -48,7 +50,9 @@ static void modify_range_constraint(Oid child_relid,
4850const Bound * upper );
4951static char * get_qualified_rel_name (Oid relid );
5052static void drop_table_by_oid (Oid relid );
51-
53+ static bool interval_is_trivial (Oid atttype ,
54+ Datum interval ,
55+ Oid interval_type );
5256
5357/* Function declarations */
5458
@@ -620,10 +624,9 @@ validate_interval_value(PG_FUNCTION_ARGS)
620624Oid partrel = PG_GETARG_OID (0 );
621625text * attname = PG_GETARG_TEXT_P (1 );
622626PartType parttype = DatumGetPartType (PG_GETARG_DATUM (2 ));
623- Datum range_interval = PG_GETARG_DATUM (3 );
624-
625- char * attname_cstr ;
626- Oid atttype ;/* type of partitioned attribute */
627+ Datum interval_text = PG_GETARG_DATUM (3 );
628+ Datum interval_value ;
629+ Oid interval_type ;
627630
628631if (PG_ARGISNULL (0 ))
629632elog (ERROR ,"'partrel' should not be NULL" );
@@ -634,21 +637,130 @@ validate_interval_value(PG_FUNCTION_ARGS)
634637if (PG_ARGISNULL (2 ))
635638elog (ERROR ,"'parttype' should not be NULL" );
636639
637- /* it's OK if interval is NULL and table is HASH-partitioned */
638- if (PG_ARGISNULL (3 ))
639- PG_RETURN_BOOL (parttype == PT_HASH );
640+ /*
641+ * NULL interval is fine for both HASH and RANGE. But for RANGE we need
642+ * to make some additional checks
643+ */
644+ if (!PG_ARGISNULL (3 ))
645+ {
646+ char * attname_cstr ;
647+ Oid atttype ;/* type of partitioned attribute */
648+
649+ if (parttype == PT_HASH )
650+ elog (ERROR ,"interval must be NULL for HASH partitioned table" );
640651
641- /* Convert attname to CSTRING and fetch column's type */
642- attname_cstr = text_to_cstring (attname );
643- atttype = get_attribute_type (partrel ,attname_cstr , false);
652+ /* Convert attname to CSTRING and fetch column's type */
653+ attname_cstr = text_to_cstring (attname );
654+ atttype = get_attribute_type (partrel ,attname_cstr , false);
644655
645- /* Try converting textual representation */
646- extract_binary_interval_from_text (range_interval ,atttype ,NULL );
656+ /* Try converting textual representation */
657+ interval_value = extract_binary_interval_from_text (interval_text ,
658+ atttype ,
659+ & interval_type );
660+
661+ /* Check that interval isn't trivial */
662+ if (interval_is_trivial (atttype ,interval_value ,interval_type ))
663+ elog (ERROR ,"interval must not be trivial" );
664+ }
647665
648666PG_RETURN_BOOL (true);
649667}
650668
651669
670+ /*
671+ * Check if interval is insignificant to avoid infinite loops while adding
672+ * new partitions
673+ *
674+ * The main idea behind this function is to add specified interval to some
675+ * default value (zero for numeric types and current date/timestamp for datetime
676+ * types) and look if it is changed. If it is then return true.
677+ */
678+ static bool
679+ interval_is_trivial (Oid atttype ,Datum interval ,Oid interval_type )
680+ {
681+ Datum default_value ;
682+ Datum op_result ;
683+ Oid op_result_type ;
684+ Operator op ;
685+ Oid op_func ;
686+ FmgrInfo cmp_func ;
687+
688+ /* Generate default value */
689+ switch (atttype )
690+ {
691+ case INT2OID :
692+ case INT4OID :
693+ case INT8OID :
694+ default_value = Int16GetDatum (0 );
695+ break ;
696+ case FLOAT4OID :
697+ default_value = Float4GetDatum (0 );
698+ break ;
699+ case FLOAT8OID :
700+ default_value = Float8GetDatum (0 );
701+ break ;
702+ case NUMERICOID :
703+ default_value = NumericGetDatum (0 );
704+ break ;
705+ case TIMESTAMPOID :
706+ case TIMESTAMPTZOID :
707+ default_value = TimestampGetDatum (GetCurrentTimestamp ());
708+ break ;
709+ case DATEOID :
710+ {
711+ Datum ts = TimestampGetDatum (GetCurrentTimestamp ());
712+
713+ default_value = perform_type_cast (ts ,TIMESTAMPTZOID ,DATEOID ,NULL );
714+ }
715+ break ;
716+ default :
717+ return false;
718+ }
719+
720+ /* Find suitable addition operator for default value and interval */
721+ op = get_binary_operator ("+" ,atttype ,interval_type );
722+ if (!op )
723+ elog (ERROR ,"missing \"+\" operator for types %s and %s" ,
724+ format_type_be (atttype ),
725+ format_type_be (interval_type ));
726+
727+ op_func = oprfuncid (op );
728+ op_result_type = get_operator_ret_type (op );
729+ ReleaseSysCache (op );
730+
731+ /* Invoke addition operator and get a result*/
732+ op_result = OidFunctionCall2 (op_func ,default_value ,interval );
733+
734+ /*
735+ * If operator result type isn't the same as original value then
736+ * convert it. We need this to make sure that specified interval would
737+ * change the _origianal_ value somehow. For example, if we add one second
738+ * to a date then we'll get a timestamp which is one second later than
739+ * original date (obviously). But when we convert it back to a date we will
740+ * get the same original value meaning that one second interval wouldn't
741+ * change original value anyhow. We should consider such interval
742+ * as trivial
743+ */
744+ if (op_result_type != atttype )
745+ {
746+ op_result = perform_type_cast (op_result ,op_result_type ,atttype ,NULL );
747+ op_result_type = atttype ;
748+ }
749+
750+ /*
751+ * Compare it to the default_value. If they are the same then obviously
752+ * interval is trivial
753+ */
754+ fill_type_cmp_fmgr_info (& cmp_func ,
755+ getBaseType (atttype ),
756+ getBaseType (op_result_type ));
757+ if (DatumGetInt32 (FunctionCall2 (& cmp_func ,default_value ,op_result ))== 0 )
758+ return true;
759+
760+ return false;
761+ }
762+
763+
652764/*
653765 * ------------------
654766 * Helper functions