|
12 | 12 | #include "utils.h"
|
13 | 13 | #include "pathman.h"
|
14 | 14 | #include "partition_creation.h"
|
| 15 | +#include "partition_filter.h" |
15 | 16 | #include "relation_info.h"
|
16 | 17 | #include "xact_handling.h"
|
17 | 18 |
|
18 |
| -#include "access/htup_details.h" |
| 19 | +#include "access/tupconvert.h" |
19 | 20 | #include "access/nbtree.h"
|
| 21 | +#include "access/htup_details.h" |
20 | 22 | #include "access/xact.h"
|
21 | 23 | #include "catalog/indexing.h"
|
22 | 24 | #include "catalog/pg_type.h"
|
|
32 | 34 | #include "utils/syscache.h"
|
33 | 35 |
|
34 | 36 |
|
| 37 | +static Oid get_partition_for_key(const PartRelationInfo *prel, Datum key); |
| 38 | + |
| 39 | + |
35 | 40 | /* Function declarations */
|
36 | 41 |
|
37 | 42 | PG_FUNCTION_INFO_V1( on_partitions_created );
|
@@ -69,6 +74,8 @@ PG_FUNCTION_INFO_V1( check_security_policy );
|
69 | 74 | PG_FUNCTION_INFO_V1( debug_capture );
|
70 | 75 | PG_FUNCTION_INFO_V1( get_pathman_lib_version );
|
71 | 76 |
|
| 77 | +PG_FUNCTION_INFO_V1( update_trigger_func ); |
| 78 | + |
72 | 79 |
|
73 | 80 | /*
|
74 | 81 | * User context for function show_partition_list_internal().
|
@@ -926,3 +933,112 @@ get_pathman_lib_version(PG_FUNCTION_ARGS)
|
926 | 933 | {
|
927 | 934 | PG_RETURN_CSTRING(psprintf("%x", CURRENT_LIB_VERSION));
|
928 | 935 | }
|
| 936 | + |
| 937 | +/* |
| 938 | + * Update trigger |
| 939 | + */ |
| 940 | +Datum |
| 941 | +update_trigger_func(PG_FUNCTION_ARGS) |
| 942 | +{ |
| 943 | + const PartRelationInfo *prel; |
| 944 | + PartParentSearch parent_search; |
| 945 | + Oid parent; |
| 946 | + TriggerData *trigdata = (TriggerData *) fcinfo->context; |
| 947 | + char *key_name; |
| 948 | + Datum key; |
| 949 | + bool isnull; |
| 950 | + TupleConversionMap *conversion_map; |
| 951 | + |
| 952 | + TupleDesc source_tupdesc; |
| 953 | + HeapTuple source_tuple; |
| 954 | + Oid source_relid; |
| 955 | + AttrNumber source_key; |
| 956 | + |
| 957 | + Relation target_rel; |
| 958 | + TupleDesc target_tupdesc; |
| 959 | + HeapTuple target_tuple; |
| 960 | + Oid target_relid; |
| 961 | + |
| 962 | + /* This function can only be invoked as a trigger */ |
| 963 | + if (!CALLED_AS_TRIGGER(fcinfo)) |
| 964 | + elog(ERROR, "Function invoked not in a trigger context"); |
| 965 | + |
| 966 | + /* Make sure that trigger was fired during UPDATE command */ |
| 967 | + if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) |
| 968 | + elog(ERROR, "This function must only be used as UPDATE trigger"); |
| 969 | + |
| 970 | + source_relid = trigdata->tg_relation->rd_id; |
| 971 | + source_tuple = trigdata->tg_newtuple; |
| 972 | + source_tupdesc = trigdata->tg_relation->rd_att; |
| 973 | + |
| 974 | + /* Find parent relation and partitioning info */ |
| 975 | + parent = get_parent_of_partition(source_relid, &parent_search); |
| 976 | + if (parent_search != PPS_ENTRY_PART_PARENT) |
| 977 | + elog(ERROR, "relation \"%s\" is not a partition", |
| 978 | + get_rel_name_or_relid(source_relid)); |
| 979 | + prel = get_pathman_relation_info(parent); |
| 980 | + shout_if_prel_is_invalid(parent, prel, PT_INDIFFERENT); |
| 981 | + |
| 982 | + /* |
| 983 | + * Find partitioning key attribute of source partition. Keep in mind that |
| 984 | + * there could be dropped columns in parent relation or partition and so |
| 985 | + * key attribute may have different number |
| 986 | + */ |
| 987 | + key_name = get_attname(parent, prel->attnum); |
| 988 | + source_key = get_attnum(source_relid, key_name); |
| 989 | + key = heap_getattr(source_tuple, source_key, source_tupdesc, &isnull); |
| 990 | + |
| 991 | + /* Find partition it should go into */ |
| 992 | + target_relid = get_partition_for_key(prel, key); |
| 993 | + |
| 994 | + /* If target partition is the same then do nothing */ |
| 995 | + if (target_relid == source_relid) |
| 996 | + return PointerGetDatum(source_tuple); |
| 997 | + |
| 998 | + target_rel = heap_open(target_relid, RowExclusiveLock); |
| 999 | + target_tupdesc = target_rel->rd_att; |
| 1000 | + |
| 1001 | + /* |
| 1002 | + * Else if it's a different partition then build a TupleConversionMap |
| 1003 | + * between original partition and new one. And then do a convertation |
| 1004 | + */ |
| 1005 | + conversion_map = convert_tuples_by_name(source_tupdesc, |
| 1006 | + target_tupdesc, |
| 1007 | + "Failed to convert tuple"); |
| 1008 | + target_tuple = do_convert_tuple(source_tuple, conversion_map); |
| 1009 | + |
| 1010 | + /* |
| 1011 | + * To make an UPDATE on a tuple in case when the tuple should be moved from |
| 1012 | + * one partition to another we need to perform two actions. First, remove |
| 1013 | + * old tuple from original partition and then insert updated version |
| 1014 | + * of tuple to the target partition |
| 1015 | + */ |
| 1016 | + simple_heap_delete(trigdata->tg_relation, &trigdata->tg_trigtuple->t_self); |
| 1017 | + simple_heap_insert(target_rel, target_tuple); |
| 1018 | + |
| 1019 | + heap_close(target_rel, RowExclusiveLock); |
| 1020 | + PG_RETURN_VOID(); |
| 1021 | +} |
| 1022 | + |
| 1023 | +/* |
| 1024 | + * Returns Oid of partition corresponding to partitioning key value. Throws |
| 1025 | + * an error if no partition found |
| 1026 | + */ |
| 1027 | +static Oid |
| 1028 | +get_partition_for_key(const PartRelationInfo *prel, Datum key) |
| 1029 | +{ |
| 1030 | + Oid *parts; |
| 1031 | + int nparts; |
| 1032 | + |
| 1033 | + /* Search for matching partitions */ |
| 1034 | + parts = find_partitions_for_value(key, prel->atttype, prel, &nparts); |
| 1035 | + |
| 1036 | + if (nparts > 1) |
| 1037 | + elog(ERROR, ERR_PART_ATTR_MULTIPLE); |
| 1038 | + else if (nparts == 0) |
| 1039 | + elog(ERROR, |
| 1040 | + "There is not partition to fit partition key \"%s\"", |
| 1041 | + datum_to_cstring(key, prel->atttype)); |
| 1042 | + else |
| 1043 | + return parts[0]; |
| 1044 | +} |
0 commit comments