void ConnectionPoolStats::appendToBSON(mongo::BSONObjBuilder& result) {
    result.appendNumber("totalInUse", totalInUse);
    result.appendNumber("totalAvailable", totalAvailable);
    result.appendNumber("totalCreated", totalCreated);
    result.appendNumber("totalRefreshing", totalRefreshing);

    {
        BSONObjBuilder poolBuilder(result.subobjStart("pools"));
        for (auto&& pool : statsByPool) {
            BSONObjBuilder poolInfo(poolBuilder.subobjStart(pool.first));
            auto poolStats = pool.second;
            poolInfo.appendNumber("poolInUse", poolStats.inUse);
            poolInfo.appendNumber("poolAvailable", poolStats.available);
            poolInfo.appendNumber("poolCreated", poolStats.created);
            poolInfo.appendNumber("poolRefreshing", poolStats.refreshing);
            for (auto&& host : statsByPoolHost[pool.first]) {
                BSONObjBuilder hostInfo(poolInfo.subobjStart(host.first.toString()));
                auto hostStats = host.second;
                hostInfo.appendNumber("inUse", hostStats.inUse);
                hostInfo.appendNumber("available", hostStats.available);
                hostInfo.appendNumber("created", hostStats.created);
                hostInfo.appendNumber("refreshing", hostStats.refreshing);
            }
        }
    }
    {
        BSONObjBuilder hostBuilder(result.subobjStart("hosts"));
        for (auto&& host : statsByHost) {
            BSONObjBuilder hostInfo(hostBuilder.subobjStart(host.first.toString()));
            auto hostStats = host.second;
            hostInfo.appendNumber("inUse", hostStats.inUse);
            hostInfo.appendNumber("available", hostStats.available);
            hostInfo.appendNumber("created", hostStats.created);
            hostInfo.appendNumber("refreshing", hostStats.refreshing);
        }
    }
}
void ConnectionPoolStats::appendToBSON(mongo::BSONObjBuilder& result) {
    result.appendNumber("totalInUse", totalInUse);
    result.appendNumber("totalAvailable", totalAvailable);
    result.appendNumber("totalCreated", totalCreated);

    BSONObjBuilder hostBuilder(result.subobjStart("hosts"));
    for (auto&& host : statsByHost) {
        BSONObjBuilder hostInfo(hostBuilder.subobjStart(host.first.toString()));

        auto hostStats = host.second;
        hostInfo.appendNumber("inUse", hostStats.inUse);
        hostInfo.appendNumber("available", hostStats.available);
        hostInfo.appendNumber("created", hostStats.created);
    }
}
void datum_to_bson(const char* field_name, mongo::BSONObjBuilder& builder,
    Datum val, bool is_null, Oid typid)
{
    PGBSON_LOG << "BEGIN datum_to_bson, field_name=" << field_name << ", typeid=" << typid << PGBSON_ENDL;

    if (field_name == NULL)
    {
        field_name = "";
    }

    if (is_null)
    {
        builder.appendNull(field_name);
    }
    else
    {
        switch(typid)
        {
            case BOOLOID:
                 builder.append(field_name, DatumGetBool(val));
                 break;

            case CHAROID:
            {
                char c = DatumGetChar(val);
                builder.append(field_name, &c, 1);
                break;
            }

            case INT8OID:
                builder.append(field_name, (long long)DatumGetInt64(val));
                break;

            case INT2OID:
                builder.append(field_name, DatumGetInt16(val));
                break;

            case INT4OID:
                builder.append(field_name, DatumGetInt32(val));
                break;

            case TEXTOID:
            case JSONOID:
            case XMLOID:
            {
                text* t = DatumGetTextP(val);
                builder.append(field_name, VARDATA(t), VARSIZE(t)-VARHDRSZ+1);
                break;
            }

            case FLOAT4OID:
                builder.append(field_name, DatumGetFloat4(val));
                break;

            case FLOAT8OID:
                builder.append(field_name, DatumGetFloat8(val));
                break;

            case RECORDOID:
            {
                mongo::BSONObjBuilder sub(builder.subobjStart(field_name));
                composite_to_bson(sub, val);
                sub.done();
                break;
            }

            case TIMESTAMPOID:
            {
                Timestamp ts = DatumGetTimestamp(val);
                #ifdef HAVE_INT64_TIMESTAMP
                mongo::Date_t date(ts);
                #else
                mongo::Date_t date(ts * 1000);
                #endif

                builder.append(field_name, date);
                break;
            }

            default:
            {
                PGBSON_LOG << "datum_to_bson - unknown type, using text output." << PGBSON_ENDL;
                PGBSON_LOG << "datum_to_bson - type=" << get_typename(typid) << PGBSON_ENDL;
                if (get_typename(typid) == "bson")
                {
                    bytea* data = DatumGetBson(val);
                    mongo::BSONObj obj(VARDATA_ANY(data));
                    builder.append(field_name, obj);
                }
                else
                {
                    // use text output for the type
                    bool typisvarlena = false;
                    Oid typoutput;
                    getTypeOutputInfo(typid, &typoutput, &typisvarlena);
                    PGBSON_LOG << "datum_to_bson - typisvarlena=" << std::boolalpha << typisvarlena << PGBSON_ENDL;
                    Datum out_val = val;
                    /*
                     * If we have a toasted datum, forcibly detoast it here to avoid
                     * memory leakage inside the type's output routine.
                     */
                    if (typisvarlena)
                    {
                        out_val = PointerGetDatum(PG_DETOAST_DATUM(val));
                        PGBSON_LOG << "datum_to_bson - var len valuie detoasted" << PGBSON_ENDL;
                    }

                    char* outstr = OidOutputFunctionCall(typoutput, out_val);
                    builder.append(field_name, outstr);

                    /* Clean up detoasted copy, if any */
                    if (val != out_val)
                        pfree(DatumGetPointer(out_val));
                }
            }
        } // switch
    } // if not null

    PGBSON_LOG << "END datum_to_bson, field_name=" << field_name << PGBSON_ENDL;

}