@@ -347,6 +347,7 @@ find_rti_by_rte(PlannerInfo *root, RangeTblEntry *rte)
347347
348348static const int PGSTAT_MAX_SLOTS = 5 ;
349349
350+
350351static void
351352pgstat_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+
578612static 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+ }
0 commit comments