Skip to content

Commit c08c648

Browse files
committed
introduce subsystem compat/relation_tags
1 parent 0a0d2cf commit c08c648

File tree

6 files changed

+328
-120
lines changed

6 files changed

+328
-120
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ OBJS = src/init.o src/relation_info.o src/utils.o src/partition_filter.o \
77
src/pl_funcs.o src/pl_range_funcs.o src/pl_hash_funcs.o src/pathman_workers.o \
88
src/hooks.o src/nodes_common.o src/xact_handling.o src/utility_stmt_hooking.o \
99
src/planner_tree_modification.o src/debug_print.o src/partition_creation.o \
10-
src/compat/pg_compat.o $(WIN32RES)
10+
src/compat/pg_compat.o src/compat/relation_tags.o $(WIN32RES)
1111

1212
PG_CPPFLAGS = -I$(CURDIR)/src/include
1313

src/compat/relation_tags.c

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/* ------------------------------------------------------------------------
2+
*
3+
* relation_tags.c
4+
* Attach custom (Key, Value) pairs to an arbitrary RangeTblEntry
5+
*
6+
* Copyright (c) 2017, Postgres Professional
7+
*
8+
* ------------------------------------------------------------------------
9+
*/
10+
11+
#include "compat/relation_tags.h"
12+
#include "planner_tree_modification.h"
13+
14+
#include "nodes/nodes.h"
15+
16+
17+
/*
18+
* This table is used to ensure that partitioned relation
19+
* cant't be used with both and without ONLY modifiers.
20+
*/
21+
static HTAB *per_table_relation_tags = NULL;
22+
static int per_table_relation_tags_refcount = 0;
23+
24+
25+
/* private struct stored by parenthood lists */
26+
typedef struct
27+
{
28+
Oid relid; /* key (part #1) */
29+
uint32 queryId; /* key (part #2) */
30+
List *relation_tags;
31+
} relation_tags_entry;
32+
33+
34+
/* Look through RTE's relation tags */
35+
List *
36+
rte_fetch_tag(const uint32 query_id,
37+
const RangeTblEntry *rte,
38+
const char *key)
39+
{
40+
relation_tags_entry *htab_entry,
41+
htab_key = { rte->relid, query_id, NIL /* unused */ };
42+
43+
AssertArg(rte);
44+
AssertArg(key);
45+
46+
/* Skip if table is not initialized */
47+
if (per_table_relation_tags)
48+
{
49+
/* Search by 'htab_key' */
50+
htab_entry = hash_search(per_table_relation_tags,
51+
&htab_key, HASH_FIND, NULL);
52+
53+
if (htab_entry)
54+
return relation_tags_search(htab_entry->relation_tags, key);
55+
}
56+
57+
/* Not found, return stub value */
58+
return NIL;
59+
}
60+
61+
/* Attach new relation tag to RTE. Returns KVP with duplicate key. */
62+
List *
63+
rte_attach_tag(const uint32 query_id,
64+
RangeTblEntry *rte,
65+
List *key_value_pair)
66+
{
67+
relation_tags_entry *htab_entry,
68+
htab_key = { rte->relid, query_id, NIL /* unused */ };
69+
bool found;
70+
MemoryContext old_mcxt;
71+
72+
AssertArg(rte);
73+
AssertArg(key_value_pair && list_length(key_value_pair) == 2);
74+
75+
/* We prefer to initialize this table lazily */
76+
if (!per_table_relation_tags)
77+
{
78+
const long start_elems = 50;
79+
HASHCTL hashctl;
80+
81+
memset(&hashctl, 0, sizeof(HASHCTL));
82+
hashctl.entrysize = sizeof(relation_tags_entry);
83+
hashctl.keysize = offsetof(relation_tags_entry, relation_tags);
84+
hashctl.hcxt = TAG_MEMORY_CONTEXT;
85+
86+
per_table_relation_tags = hash_create("Custom tags for RangeTblEntry",
87+
start_elems, &hashctl,
88+
HASH_ELEM | HASH_BLOBS);
89+
}
90+
91+
/* Search by 'htab_key' */
92+
htab_entry = hash_search(per_table_relation_tags,
93+
&htab_key, HASH_ENTER, &found);
94+
95+
if (found)
96+
{
97+
const char *current_key;
98+
99+
/* Extract key of this KVP */
100+
rte_deconstruct_tag(key_value_pair, &current_key, NULL);
101+
102+
/* Check if this KVP already exists */
103+
return relation_tags_search(htab_entry->relation_tags, current_key);
104+
}
105+
106+
/* Don't forget to initialize list! */
107+
else htab_entry->relation_tags = NIL;
108+
109+
/* Add this KVP */
110+
old_mcxt = MemoryContextSwitchTo(TAG_MEMORY_CONTEXT);
111+
htab_entry->relation_tags = lappend(htab_entry->relation_tags,
112+
key_value_pair);
113+
MemoryContextSwitchTo(old_mcxt);
114+
115+
/* Success! */
116+
return NIL;
117+
}
118+
119+
120+
121+
/* Extract key & value from 'key_value_pair' */
122+
void
123+
rte_deconstruct_tag(const List *key_value_pair,
124+
const char **key, /* ret value #1 */
125+
const Value **value) /* ret value #2 */
126+
{
127+
const char *r_key;
128+
const Value *r_value;
129+
130+
AssertArg(key_value_pair && list_length(key_value_pair) == 2);
131+
132+
r_key = (const char *) strVal(linitial(key_value_pair));
133+
r_value = (const Value *) lsecond(key_value_pair);
134+
135+
/* Check that 'key' is valid */
136+
Assert(IsA(linitial(key_value_pair), String));
137+
138+
/* Check that 'value' is valid or NULL */
139+
Assert(r_value == NULL ||
140+
IsA(r_value, Integer) ||
141+
IsA(r_value, Float) ||
142+
IsA(r_value, String));
143+
144+
/* Finally return key & value */
145+
if (key) *key = r_key;
146+
if (value) *value = r_value;
147+
}
148+
149+
/* Search through list of 'relation_tags' */
150+
List *
151+
relation_tags_search(List *relation_tags, const char *key)
152+
{
153+
ListCell *lc;
154+
155+
AssertArg(key);
156+
157+
/* Scan KVP list */
158+
foreach (lc, relation_tags)
159+
{
160+
List *current_kvp = (List *) lfirst(lc);
161+
const char *current_key;
162+
163+
/* Extract key of this KVP */
164+
rte_deconstruct_tag(current_kvp, &current_key, NULL);
165+
166+
/* Check if this is the KVP we're looking for */
167+
if (strcmp(key, current_key) == 0)
168+
return current_kvp;
169+
}
170+
171+
/* Nothing! */
172+
return NIL;
173+
}
174+
175+
176+
177+
/* Increate usage counter by 1 */
178+
void
179+
incr_refcount_relation_tags(void)
180+
{
181+
/* Increment reference counter */
182+
if (++per_table_relation_tags_refcount <= 0)
183+
elog(WARNING, "imbalanced %s",
184+
CppAsString(incr_refcount_relation_tags));
185+
}
186+
187+
/* Return current value of usage counter */
188+
uint32
189+
get_refcount_relation_tags(void)
190+
{
191+
/* incr_refcount_parenthood_statuses() is called by pathman_planner_hook() */
192+
return per_table_relation_tags_refcount;
193+
}
194+
195+
/* Reset all cached statuses if needed (query end) */
196+
void
197+
decr_refcount_relation_tags(void)
198+
{
199+
/* Decrement reference counter */
200+
if (--per_table_relation_tags_refcount < 0)
201+
elog(WARNING, "imbalanced %s",
202+
CppAsString(decr_refcount_relation_tags));
203+
204+
/* Free resources if no one is using them */
205+
if (per_table_relation_tags_refcount == 0)
206+
{
207+
reset_query_id_generator();
208+
209+
hash_destroy(per_table_relation_tags);
210+
per_table_relation_tags = NULL;
211+
}
212+
}

src/hooks.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
#include "compat/pg_compat.h"
12+
#include "compat/relation_tags.h"
1213

1314
#include "hooks.h"
1415
#include "init.h"
@@ -214,8 +215,7 @@ pathman_rel_pathlist_hook(PlannerInfo *root,
214215
return;
215216

216217
/* Skip if this table is not allowed to act as parent (see FROM ONLY) */
217-
if (PARENTHOOD_DISALLOWED == get_rel_parenthood_status(root->parse->queryId,
218-
rte->relid))
218+
if (PARENTHOOD_DISALLOWED == get_rel_parenthood_status(root->parse->queryId, rte))
219219
return;
220220

221221
/* Proceed iff relation 'rel' is partitioned */
@@ -476,7 +476,7 @@ pathman_planner_hook(Query *parse, int cursorOptions, ParamListInfo boundParams)
476476
if (pathman_ready)
477477
{
478478
/* Increment parenthood_statuses refcount */
479-
incr_refcount_parenthood_statuses();
479+
incr_refcount_relation_tags();
480480

481481
/* Modify query tree if needed */
482482
pathman_transform_query(parse);
@@ -497,7 +497,7 @@ pathman_planner_hook(Query *parse, int cursorOptions, ParamListInfo boundParams)
497497
ExecuteForPlanTree(result, add_partition_filters);
498498

499499
/* Decrement parenthood_statuses refcount */
500-
decr_refcount_parenthood_statuses();
500+
decr_refcount_relation_tags();
501501

502502
/* HACK: restore queryId set by pg_stat_statements */
503503
result->queryId = query_id;
@@ -509,7 +509,7 @@ pathman_planner_hook(Query *parse, int cursorOptions, ParamListInfo boundParams)
509509
if (pathman_ready)
510510
{
511511
/* Caught an ERROR, decrease refcount */
512-
decr_refcount_parenthood_statuses();
512+
decr_refcount_relation_tags();
513513
}
514514

515515
/* Rethrow ERROR further */
@@ -552,7 +552,7 @@ pathman_post_parse_analysis_hook(ParseState *pstate, Query *query)
552552
}
553553

554554
/* Process inlined SQL functions (we've already entered planning stage) */
555-
if (IsPathmanReady() && get_refcount_parenthood_statuses() > 0)
555+
if (IsPathmanReady() && get_refcount_relation_tags() > 0)
556556
{
557557
/* Check that pg_pathman is the last extension loaded */
558558
if (post_parse_analyze_hook != pathman_post_parse_analysis_hook)

src/include/compat/relation_tags.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* ------------------------------------------------------------------------
2+
*
3+
* relation_tags.h
4+
* Attach custom (Key, Value) pairs to an arbitrary RangeTblEntry
5+
*
6+
* Copyright (c) 2017, Postgres Professional
7+
*
8+
* ------------------------------------------------------------------------
9+
*/
10+
11+
#ifndef RELATION_TAGS_H
12+
#define RELATION_TAGS_H
13+
14+
15+
#include "pathman.h"
16+
17+
#include "postgres.h"
18+
#include "nodes/relation.h"
19+
#include "nodes/value.h"
20+
#include "utils/memutils.h"
21+
22+
23+
24+
/* Memory context we're going to use for TAGs */
25+
#define TAG_MEMORY_CONTEXT TopTransactionContext
26+
27+
/* Safe TAG constructor (Integer) */
28+
static inline List *
29+
make_rte_tag_int(char *key, int value)
30+
{
31+
List *kvp;
32+
MemoryContext old_mcxt;
33+
34+
/* Allocate TAG in a persistent memory context */
35+
old_mcxt = MemoryContextSwitchTo(TAG_MEMORY_CONTEXT);
36+
kvp = list_make2(makeString(key), makeInteger(value));
37+
MemoryContextSwitchTo(old_mcxt);
38+
39+
return kvp;
40+
}
41+
42+
43+
44+
List *rte_fetch_tag(const uint32 query_id,
45+
const RangeTblEntry *rte,
46+
const char *key);
47+
48+
List *rte_attach_tag(const uint32 query_id,
49+
RangeTblEntry *rte,
50+
List *key_value_pair);
51+
52+
53+
List *relation_tags_search(List *custom_tags,
54+
const char *key);
55+
56+
void rte_deconstruct_tag(const List *key_value_pair,
57+
const char **key,
58+
const Value **value);
59+
60+
61+
void incr_refcount_relation_tags(void);
62+
uint32 get_refcount_relation_tags(void);
63+
void decr_refcount_relation_tags(void);
64+
65+
66+
#endif /* RELATION_TAGS_H */

src/include/planner_tree_modification.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include "nodes/nodeFuncs.h"
2121

2222

23+
/* Query ID generator */
24+
void assign_query_id(Query *query);
25+
void reset_query_id_generator(void);
26+
2327
/* Plan tree rewriting utility */
2428
void plan_tree_walker(Plan *plan,
2529
void (*visitor) (Plan *plan, void *context),
@@ -41,12 +45,12 @@ typedef enum
4145
PARENTHOOD_ALLOWED /* children are enabled (default) */
4246
} rel_parenthood_status;
4347

44-
void assign_rel_parenthood_status(uint32 query_id, Oid relid,
48+
#define PARENTHOOD_TAG CppAsString(PARENTHOOD)
49+
50+
void assign_rel_parenthood_status(uint32 query_id,
51+
RangeTblEntry *rte,
4552
rel_parenthood_status new_status);
46-
rel_parenthood_status get_rel_parenthood_status(uint32 query_id, Oid relid);
47-
void incr_refcount_parenthood_statuses(void);
48-
uint32 get_refcount_parenthood_statuses(void);
49-
void decr_refcount_parenthood_statuses(void);
53+
rel_parenthood_status get_rel_parenthood_status(uint32 query_id, RangeTblEntry *rte);
5054

5155

5256
#endif /* PLANNER_TREE_MODIFICATION_H */

0 commit comments

Comments
 (0)