Skip to content

Commit 6aba94d

Browse files
committed
fix pg_statistic_server affect source tables
1 parent a3653d0 commit 6aba94d

4 files changed

Lines changed: 256 additions & 157 deletions

File tree

src/pg/videx/stats.cc

Lines changed: 169 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ find_rti_by_rte(PlannerInfo *root, RangeTblEntry *rte)
347347

348348
static const int PGSTAT_MAX_SLOTS = 5;
349349

350+
350351
static void
351352
pgstat_apply_slots(VidexStringMap &res_json, Datum *values, bool *nulls)
352353
{
@@ -356,40 +357,30 @@ pgstat_apply_slots(VidexStringMap &res_json, Datum *values, bool *nulls)
356357
if (slots_iter != res_json.end())
357358
{
358359
const std::string &raw = slots_iter->second;
359-
// elog(INFO, "slots: %s", raw.c_str());
360360
if (!raw.empty())
361361
{
362362
nlohmann::json parsed = nlohmann::json::parse(raw, nullptr, false);
363363
if (parsed.is_discarded() || !parsed.is_array())
364364
{
365365
std::string normalized = raw;
366366
for (char &ch : normalized)
367-
if (ch == '\'')
368-
ch = '"';
369-
370-
auto replace_token = [&normalized](const std::string &from, const std::string &to)
371-
{
367+
if (ch == '\'') ch = '"';
368+
auto replace_token = [&normalized](const std::string &from, const std::string &to) {
372369
size_t pos = 0;
373-
while ((pos = normalized.find(from, pos)) != std::string::npos)
374-
{
370+
while ((pos = normalized.find(from, pos)) != std::string::npos) {
375371
normalized.replace(pos, from.length(), to);
376372
pos += to.length();
377373
}
378374
};
379-
380375
replace_token("True", "true");
381376
replace_token("False", "false");
382377
replace_token("None", "null");
383-
384378
parsed = nlohmann::json::parse(normalized, nullptr, false);
385379
}
386-
387380
if (!parsed.is_discarded() && parsed.is_array())
388381
slots_json = parsed;
389382
else
390-
elog(WARNING,
391-
"BuildPGStatisticTuple: invalid slots payload: %s",
392-
raw.c_str());
383+
elog(WARNING, "BuildPGStatisticTuple: invalid slots payload: %s", raw.c_str());
393384
}
394385
}
395386

@@ -399,57 +390,52 @@ pgstat_apply_slots(VidexStringMap &res_json, Datum *values, bool *nulls)
399390
{
400391
const nlohmann::json &slot = slots_json[i];
401392

402-
bool isnull = false;
403-
Datum datum = (Datum) 0;
404-
if (slot.contains("kind") && !slot["kind"].is_null())
405-
{
406-
try {
407-
datum = Int16GetDatum(slot["kind"].is_string()
408-
? static_cast<int16>(std::stoi(slot["kind"].get<std::string>()))
409-
: static_cast<int16>(slot["kind"].get<int>()));
410-
} catch (...) {
411-
isnull = true;
393+
// stakindN (int16), default 0
394+
bool kind_err = false;
395+
Datum kind_datum = (Datum) 0;
396+
try {
397+
if (slot.contains("kind") && !slot["kind"].is_null()) {
398+
kind_datum = Int16GetDatum(slot["kind"].is_string()
399+
? static_cast<int16>(std::stoi(slot["kind"].get<std::string>()))
400+
: static_cast<int16>(slot["kind"].get<int>()));
401+
} else {
402+
kind_datum = Int16GetDatum(0);
412403
}
413-
}
414-
else
415-
isnull = true;
416-
values[Anum_pg_statistic_stakind1 - 1 + i] = datum;
417-
nulls[Anum_pg_statistic_stakind1 - 1 + i] = isnull;
418-
419-
isnull = false;
420-
datum = (Datum) 0;
421-
if (slot.contains("op") && !slot["op"].is_null())
422-
{
423-
try {
424-
datum = ObjectIdGetDatum(slot["op"].is_string()
425-
? static_cast<Oid>(std::stoul(slot["op"].get<std::string>()))
426-
: static_cast<Oid>(slot["op"].get<long>()));
427-
} catch (...) {
428-
isnull = true;
404+
} catch (...) { kind_err = true; }
405+
values[Anum_pg_statistic_stakind1 - 1 + i] = kind_datum;
406+
nulls[Anum_pg_statistic_stakind1 - 1 + i] = kind_err;
407+
408+
// staopN (Oid), default 0
409+
bool op_err = false;
410+
Datum op_datum = (Datum) 0;
411+
try {
412+
if (slot.contains("op") && !slot["op"].is_null()) {
413+
op_datum = ObjectIdGetDatum(slot["op"].is_string()
414+
? static_cast<Oid>(std::stoul(slot["op"].get<std::string>()))
415+
: static_cast<Oid>(slot["op"].get<long>()));
416+
} else {
417+
op_datum = ObjectIdGetDatum((Oid)0);
429418
}
430-
}
431-
else
432-
isnull = true;
433-
values[Anum_pg_statistic_staop1 - 1 + i] = datum;
434-
nulls[Anum_pg_statistic_staop1 - 1 + i] = isnull;
435-
436-
isnull = false;
437-
datum = (Datum) 0;
438-
if (slot.contains("coll") && !slot["coll"].is_null())
439-
{
440-
try {
441-
datum = ObjectIdGetDatum(slot["coll"].is_string()
442-
? static_cast<Oid>(std::stoul(slot["coll"].get<std::string>()))
443-
: static_cast<Oid>(slot["coll"].get<long>()));
444-
} catch (...) {
445-
isnull = true;
419+
} catch (...) { op_err = true; }
420+
values[Anum_pg_statistic_staop1 - 1 + i] = op_datum;
421+
nulls[Anum_pg_statistic_staop1 - 1 + i] = op_err;
422+
423+
// stacollN (Oid), default 0
424+
bool coll_err = false;
425+
Datum coll_datum = (Datum) 0;
426+
try {
427+
if (slot.contains("coll") && !slot["coll"].is_null()) {
428+
coll_datum = ObjectIdGetDatum(slot["coll"].is_string()
429+
? static_cast<Oid>(std::stoul(slot["coll"].get<std::string>()))
430+
: static_cast<Oid>(slot["coll"].get<long>()));
431+
} else {
432+
coll_datum = ObjectIdGetDatum((Oid)0);
446433
}
447-
}
448-
else
449-
isnull = true;
450-
values[Anum_pg_statistic_stacoll1 - 1 + i] = datum;
451-
nulls[Anum_pg_statistic_stacoll1 - 1 + i] = isnull;
434+
} catch (...) { coll_err = true; }
435+
values[Anum_pg_statistic_stacoll1 - 1 + i] = coll_datum;
436+
nulls[Anum_pg_statistic_stacoll1 - 1 + i] = coll_err;
452437

438+
// stanumbersN (float4[]), default NULL if empty/missing
453439
if (slot.contains("numbers") && slot["numbers"].is_array() && !slot["numbers"].empty())
454440
{
455441
std::vector<Datum> elems;
@@ -458,14 +444,11 @@ pgstat_apply_slots(VidexStringMap &res_json, Datum *values, bool *nulls)
458444
{
459445
try {
460446
float4 val = item.is_string()
461-
? static_cast<float4>(std::stof(item.get<std::string>()))
462-
: static_cast<float4>(item.get<double>());
447+
? static_cast<float4>(std::stof(item.get<std::string>()))
448+
: static_cast<float4>(item.get<double>());
463449
elems.push_back(Float4GetDatum(val));
464-
} catch (...) {
465-
/* ignore invalid entry */
466-
}
450+
} catch (...) { /* ignore invalid */ }
467451
}
468-
469452
if (!elems.empty())
470453
{
471454
ArrayType *arr = construct_array(elems.data(),
@@ -481,8 +464,11 @@ pgstat_apply_slots(VidexStringMap &res_json, Datum *values, bool *nulls)
481464
nulls[Anum_pg_statistic_stanumbers1 - 1 + i] = true;
482465
}
483466
else
467+
{
484468
nulls[Anum_pg_statistic_stanumbers1 - 1 + i] = true;
469+
}
485470

471+
// stavaluesN (text[]), default NULL if empty/missing
486472
if (slot.contains("values") && slot["values"].is_array() && !slot["values"].empty())
487473
{
488474
std::vector<Datum> elems;
@@ -496,10 +482,8 @@ pgstat_apply_slots(VidexStringMap &res_json, Datum *values, bool *nulls)
496482
text = item["value"].get<std::string>();
497483
else
498484
text = item.dump();
499-
500485
elems.push_back(CStringGetTextDatum(text.c_str()));
501486
}
502-
503487
if (!elems.empty())
504488
{
505489
ArrayType *arr = construct_array(elems.data(),
@@ -515,14 +499,20 @@ pgstat_apply_slots(VidexStringMap &res_json, Datum *values, bool *nulls)
515499
nulls[Anum_pg_statistic_stavalues1 - 1 + i] = true;
516500
}
517501
else
502+
{
518503
nulls[Anum_pg_statistic_stavalues1 - 1 + i] = true;
504+
}
519505
}
520506
else
521507
{
522-
nulls[Anum_pg_statistic_stakind1 - 1 + i] = true;
523-
nulls[Anum_pg_statistic_staop1 - 1 + i] = true;
524-
nulls[Anum_pg_statistic_stacoll1 - 1 + i] = true;
525-
nulls[Anum_pg_statistic_stanumbers1 - 1 + i] = true;
508+
values[Anum_pg_statistic_stakind1 - 1 + i] = Int16GetDatum(0);
509+
values[Anum_pg_statistic_staop1 - 1 + i] = ObjectIdGetDatum((Oid)0);
510+
values[Anum_pg_statistic_stacoll1 - 1 + i] = ObjectIdGetDatum((Oid)0);
511+
nulls[Anum_pg_statistic_stakind1 - 1 + i] = false;
512+
nulls[Anum_pg_statistic_staop1 - 1 + i] = false;
513+
nulls[Anum_pg_statistic_stacoll1 - 1 + i] = false;
514+
515+
nulls[Anum_pg_statistic_stanumbers1- 1 + i] = true;
526516
nulls[Anum_pg_statistic_stavalues1 - 1 + i] = true;
527517
}
528518
}
@@ -575,18 +565,69 @@ BuildPGStatisticTuple(VidexStringMap &res_json, Oid relid, int attnum)
575565
return tuple;
576566
}
577567

568+
static void
569+
videx_upsert_pg_statistic(Oid relid, AttrNumber attnum, HeapTuple newtuple)
570+
{
571+
if (!HeapTupleIsValid(newtuple))
572+
return;
573+
574+
Relation stat_rel = table_open(StatisticRelationId, RowExclusiveLock);
575+
if (!RelationIsValid(stat_rel))
576+
elog(ERROR, "failed to open pg_statistic");
577+
578+
HeapTuple oldtup = SearchSysCache3(STATRELATTINH,
579+
ObjectIdGetDatum(relid),
580+
Int16GetDatum(attnum),
581+
BoolGetDatum(false));
582+
583+
CatalogIndexState indstate = CatalogOpenIndexes(stat_rel);
584+
585+
if (HeapTupleIsValid(oldtup))
586+
{
587+
CatalogTupleUpdateWithInfo(stat_rel, &oldtup->t_self, newtuple, indstate);
588+
ReleaseSysCache(oldtup);
589+
}
590+
else
591+
{
592+
CatalogTupleInsertWithInfo(stat_rel, newtuple, indstate);
593+
}
594+
595+
CatalogCloseIndexes(indstate);
596+
table_close(stat_rel, RowExclusiveLock);
597+
598+
CacheInvalidateRelcacheByRelid(relid);
599+
CommandCounterIncrement();
600+
}
601+
602+
static void
603+
set_vardata_stats_from_syscache(VariableStatData *vardata, Oid relid, AttrNumber attnum, bool stainherit)
604+
{
605+
vardata->statsTuple = SearchSysCache3(STATRELATTINH,
606+
ObjectIdGetDatum(relid),
607+
Int16GetDatum(attnum),
608+
BoolGetDatum(stainherit));
609+
vardata->freefunc = ReleaseSysCache;
610+
}
611+
578612
static bool videx_get_relation_stats(PlannerInfo *root,
579613
RangeTblEntry *rte,
580614
AttrNumber attnum,
581615
VariableStatData *vardata){
582616
Oid nspoid = get_rel_namespace(rte->relid);
583617
if (IsCatalogNamespace(nspoid) || IsToastNamespace(nspoid)) {
584-
if (prev_get_relation_stats_hook)
585-
return prev_get_relation_stats_hook(root, rte, attnum, vardata);
586-
else
587-
return false;
618+
set_vardata_stats_from_syscache(vardata, rte->relid, attnum, rte->inh);
619+
return true;
588620
}
621+
589622
if (rte->rtekind == RTE_RELATION){
623+
Relation rel = table_open(rte->relid, AccessShareLock);
624+
const TableAmRoutine *am = rel->rd_tableam;
625+
bool is_videx_am = (am == &videxam_methods);
626+
table_close(rel, AccessShareLock);
627+
if (!is_videx_am) {
628+
set_vardata_stats_from_syscache(vardata, rte->relid, attnum, rte->inh);
629+
return true;
630+
}
590631
/*fetch HeapTuple of pg_statistic from videx_statistic_server*/
591632
char *dbname = get_database_name(MyDatabaseId);
592633
char *relname = get_rel_name(rte->relid);
@@ -602,21 +643,17 @@ static bool videx_get_relation_stats(PlannerInfo *root,
602643

603644
int error = ask_from_videx_http(request_item, res_json);
604645
if (error) {
605-
std::cout << "ask_from_videx_http error. videx_get_realtion_stats" << std::endl;
606-
return prev_get_relation_stats_hook ?
607-
prev_get_relation_stats_hook(root, rte, attnum, vardata) :
608-
false;
646+
elog(WARNING, "videx_get_realtion_stats ask_from_videx_http error.");
647+
set_vardata_stats_from_syscache(vardata, rte->relid, attnum, rte->inh);
648+
} else {
649+
vardata->statsTuple = BuildPGStatisticTuple(res_json,rte->relid,attnum);
650+
vardata->freefunc = heap_freetuple;
609651
}
610-
vardata->statsTuple = BuildPGStatisticTuple(res_json,rte->relid,attnum);
611-
vardata->freefunc = heap_freetuple;
612652
/**
613-
* TODO:
614-
* 1. may be we can also update local cache of pg_statistic here
615-
* 2. we need more stawidth in pg_statistic for select list to calucate width in query plan
653+
* TODO:
654+
* 1. we need more stawidth in pg_statistic for select list to calucate width in query plan
616655
* */
617-
if (HeapTupleIsValid(vardata->statsTuple)) {
618-
vardata->acl_ok = true;
619-
}
656+
videx_upsert_pg_statistic(rte->relid, attnum, vardata->statsTuple);
620657
} else if ((rte->rtekind == RTE_SUBQUERY && !rte->inh) ||
621658
(rte->rtekind == RTE_CTE && !rte->self_reference)){
622659
PlannerInfo *subroot;
@@ -710,8 +747,47 @@ static bool videx_get_index_stats(PlannerInfo *root,
710747
Oid indexOid,
711748
AttrNumber indexattnum,
712749
VariableStatData *vardata){
713-
return prev_get_index_stats_hook ?
714-
prev_get_index_stats_hook(root, indexOid, indexattnum, vardata) :
715-
false;
716-
}
750+
Oid nspoid = get_rel_namespace(indexOid);
751+
if (IsCatalogNamespace(nspoid) || IsToastNamespace(nspoid))
752+
{
753+
set_vardata_stats_from_syscache(vardata, indexOid, indexattnum, false);
754+
return true;
755+
}
717756

757+
Relation rel = table_open(indexOid, AccessShareLock);
758+
const TableAmRoutine *am = rel->rd_tableam;
759+
bool is_videx_am = (am == &videxam_methods);
760+
table_close(rel, AccessShareLock);
761+
if (!is_videx_am) {
762+
set_vardata_stats_from_syscache(vardata, indexOid, indexattnum, false);
763+
return true;
764+
}
765+
766+
char *dbname = get_database_name(MyDatabaseId);
767+
char *relname = get_rel_name(indexOid);
768+
char *nspname = get_namespace_name(get_rel_namespace(indexOid));
769+
char *colname = get_attname(indexOid, indexattnum, false);
770+
/*To adapt with videx-statistic-server, we use schema_name.table_name to instead of table_name*/
771+
std::string ns_relname = std::string(nspname) + "." + std::string(relname);
772+
773+
VidexStringMap res_json;
774+
VidexJsonItem request_item = construct_request(dbname,nspname,ns_relname.c_str(),__PRETTY_FUNCTION__);
775+
VidexJsonItem * keyItem = request_item.create("colname");
776+
keyItem->add_property("name", colname);
777+
778+
int error = ask_from_videx_http(request_item, res_json);
779+
if (error) {
780+
std::cout << "ask_from_videx_http error. videx_get_index_stats" << std::endl;
781+
if (prev_get_index_stats_hook)
782+
return prev_get_index_stats_hook(root, indexOid, indexattnum, vardata);
783+
return false;
784+
}
785+
vardata->statsTuple = BuildPGStatisticTuple(res_json, indexOid,indexattnum);
786+
vardata->freefunc = heap_freetuple;
787+
788+
if (HeapTupleIsValid(vardata->statsTuple)) {
789+
vardata->acl_ok = true;
790+
}
791+
videx_upsert_pg_statistic(indexOid, indexattnum, vardata->statsTuple);
792+
return true;
793+
}

src/pg/videx/videxam.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ videx_table_block_relation_estimate_size(Relation rel, int32 *attr_widths,
139139
curpages = (BlockNumber) std::stoi(res_json["relpages"]);
140140
relpages = (BlockNumber) std::stoi(res_json["relpages"]);
141141
reltuples = (double) std::stof(res_json["reltuples"]);
142-
relallvisible = (BlockNumber) (res_json["relallvisible"] == "True" ? 1 : 0);
142+
relallvisible = (BlockNumber) std::stoi(res_json["relallvisible"]);
143143
vac_update_relstats(rel,
144144
curpages,
145145
reltuples,
@@ -151,6 +151,7 @@ videx_table_block_relation_estimate_size(Relation rel, int32 *attr_widths,
151151
NULL,
152152
false);
153153
} else {
154+
elog(WARNING, "videx_table_block_relation_estimate_size ask_from_videx_http error");
154155
/* it should have storage, so we can call the smgr */
155156
curpages = (BlockNumber) rel->rd_rel->relpages;
156157

0 commit comments

Comments
 (0)