Skip to content

Commit 6725ea4

Browse files
committed
refactor: move OidOutputFunctionCall logic to separate function
1 parent 535bf1a commit 6725ea4

File tree

1 file changed

+97
-77
lines changed

1 file changed

+97
-77
lines changed

src/variables.ts

Lines changed: 97 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,81 @@ export abstract class Variable {
14681468
return result;
14691469
}
14701470

1471+
private async legacyOidOutputFunctionCall(funcOid: string | number, datum: string) {
1472+
/*
1473+
* Older systems do not have OidOutputFunctionCall(), so use
1474+
* FunctionCall1() instead.
1475+
*/
1476+
1477+
/* Call function */
1478+
const result = await this.functionCall(funcOid, '(char *)', datum);
1479+
1480+
/*
1481+
* Now process the result.
1482+
* Note that function can return NULL and it's up to caller check
1483+
* if this is correct value.
1484+
*/
1485+
if (this.debug.isNull(result)) {
1486+
return null;
1487+
}
1488+
1489+
/* Free allocated string */
1490+
const ptr = this.debug.extractPtrFromString(result);
1491+
1492+
/*
1493+
* Function (funcOid) can return either NULL or a valid string.
1494+
* NULL checked previously, so now if we fail to extract value, then
1495+
* it is not valid string output.
1496+
*/
1497+
if (ptr === null) {
1498+
throw new EvaluationError(`could not extract pointer from result: ${result.result}`);
1499+
}
1500+
1501+
await this.pfree(ptr);
1502+
const str = this.debug.extractString(result);
1503+
if (str === null) {
1504+
throw new EvaluationError(`could not extract string from result: ${result.result}`);
1505+
}
1506+
1507+
return str;
1508+
}
1509+
1510+
async oidOutputFunctionCall(funcOid: string | number, datum: string) {
1511+
this.checkCanAlloc();
1512+
1513+
if (this.context.hasOidOutputFunctionCall) {
1514+
try {
1515+
const result = await this.evaluate(`OidOutputFunctionCall(${funcOid}, ${datum})`);
1516+
if (this.debug.isNull(result)) {
1517+
return null;
1518+
}
1519+
1520+
const ptr = this.debug.extractPtrFromString(result);
1521+
if (!ptr) {
1522+
throw new EvaluationError(`could not extract pointer from result: ${result.result}`);
1523+
}
1524+
1525+
await this.pfree(ptr);
1526+
const str = this.debug.extractString(result);
1527+
if (!str) {
1528+
throw new EvaluationError(`could not extract string from result: ${result.result}`);
1529+
}
1530+
return str;
1531+
} catch (err) {
1532+
if (!isEvaluationError(err)) {
1533+
throw err;
1534+
}
1535+
1536+
logger.error(err, 'could not invoke OidOutputFunctionCall - switching to legacy code');
1537+
const result = await this.legacyOidOutputFunctionCall(funcOid, datum);
1538+
this.context.hasOidOutputFunctionCall = false;
1539+
return result;
1540+
}
1541+
}
1542+
1543+
return await this.legacyOidOutputFunctionCall(funcOid, datum);
1544+
}
1545+
14711546
private haveSysCatCacheBreakpoint() {
14721547
return !!vscode.debug.breakpoints.find(b => {
14731548
if (!b.enabled) {
@@ -2605,42 +2680,6 @@ class ExprNodeVariable extends NodeVariable {
26052680
return oid;
26062681
};
26072682

2608-
const evalStrWithPtr = async (expr: string) => {
2609-
const result = await this.debug.evaluate(expr, this.frameId);
2610-
const str = this.debug.extractString(result);
2611-
if (str === null) {
2612-
throw new EvaluationError(`failed to get string from expr: ${expr}`);
2613-
}
2614-
2615-
const ptr = this.debug.extractPtrFromString(result);
2616-
if (ptr === null) {
2617-
throw new EvaluationError(`failed to get pointer from expr: ${expr}`);
2618-
}
2619-
return [str, ptr];
2620-
};
2621-
2622-
const legacyOidOutputFunctionCall = async (funcOid: number) => {
2623-
/*
2624-
* Older systems do not have OidOutputFunctionCall(), so use
2625-
* FunctionCall1() instead.
2626-
*/
2627-
/* Call function */
2628-
const result = await this.functionCall(funcOid, '(char *)', `((Const *)${this.getPointer()})->constvalue)`);
2629-
2630-
/* Free allocated string */
2631-
const ptr = this.debug.extractPtrFromString(result);
2632-
if (ptr === null) {
2633-
throw new EvaluationError(`failed to extract pointer from result: "${result.result}"`);
2634-
}
2635-
await this.pfree(ptr);
2636-
2637-
const str = this.debug.extractString(result);
2638-
if (str === null) {
2639-
throw new EvaluationError(`failed to extract string from result: "${result.result}"`);
2640-
}
2641-
return str;
2642-
};
2643-
26442683
if (await this.getMemberValueBool('constisnull')) {
26452684
return 'NULL';
26462685
}
@@ -2744,32 +2783,22 @@ class ExprNodeVariable extends NodeVariable {
27442783
}
27452784

27462785
const funcOid = await evalOid(`*((Oid *)${tupOutput})`);
2747-
let repr;
2748-
2749-
2750-
if (this.context.hasOidOutputFunctionCall) {
2751-
try {
2752-
const [str, ptr] = await evalStrWithPtr(`OidOutputFunctionCall(${funcOid}, ((Const *)${this.getPointer()})->constvalue)`);
2753-
await this.pfree(ptr);
2754-
repr = str;
2755-
} catch (e) {
2756-
if (!isEvaluationError(e)) {
2757-
throw e;
2758-
}
2759-
2760-
repr = await legacyOidOutputFunctionCall(funcOid);
2761-
this.context.hasOidOutputFunctionCall = false;
2762-
}
2763-
} else {
2764-
repr = await legacyOidOutputFunctionCall(funcOid);
2765-
}
2786+
const constvalue = `((Const *)${this.getPointer()})->constvalue`;
2787+
const repr = await this.oidOutputFunctionCall(funcOid, constvalue);
27662788

27672789
await this.pfree(tupOutput);
27682790
await this.pfree(tupIsVarlena);
2769-
if (tupIOParam)
2770-
{await this.pfree(tupIOParam);}
2791+
if (tupIOParam) {
2792+
await this.pfree(tupIOParam);
2793+
}
27712794

2772-
return repr;
2795+
/*
2796+
* We must not get NULL here, because even if string is empty it
2797+
* still be rendered as empty string, not NULL. This should be
2798+
* considered as error, but follow Postel's law - "be liberal
2799+
* in what you accept".
2800+
*/
2801+
return repr ?? 'NULL';
27732802
}
27742803

27752804
private async formatOpExpr() {
@@ -5639,7 +5668,7 @@ class TupleTableSlotAttributesVariable extends Variable {
56395668
const datums = await this.debug.getArrayVariables(expr, nvalid, this.frameId);
56405669
return datums.map(dv => this.debug.extractBool(dv));
56415670
}
5642-
5671+
56435672
async getAttribute(tupdesc: RealVariable, i: number) {
56445673
const expr = `TupleDescAttr((TupleDesc)${tupdesc.getPointer()}, ${i})`;
56455674
try {
@@ -5727,29 +5756,20 @@ class TupleTableSlotAttributesVariable extends Variable {
57275756
let type;
57285757
if (typeInfo) {
57295758
const {typoutput, typname} = typeInfo;
5730-
type = typname ?? '';
57315759

5760+
type = typname;
57325761
if (isnull) {
57335762
value = 'NULL';
5734-
} else {
5735-
const result = await this.evaluate(`OidOutputFunctionCall(${typoutput}, ${datums[i]})`);
5736-
value = this.debug.extractString(result) ?? '???';
5737-
5738-
const ptr = this.debug.extractPtrFromString(result);
5739-
if (ptr === null) {
5740-
logger.warn('OidOutputFunctionCall returned valid string, but failed to extract pointer from', result.result);
5741-
} else {
5742-
await this.pfree(ptr);
5743-
}
5744-
}
5745-
} else {
5746-
type = '';
5747-
if (isnull) {
5748-
value = 'NULL';
5749-
} else {
5750-
value = '???';
5763+
} else if (typoutput) {
5764+
value = await this.oidOutputFunctionCall(typoutput, datums[i]);
57515765
}
5766+
} else if (isnull) {
5767+
value = 'NULL';
57525768
}
5769+
5770+
/* fallback with default values */
5771+
value ??= '???';
5772+
type ??= '';
57535773

57545774
/* value: type */
57555775
attrs.push(new ScalarVariable(value, type, '', this.context, this));

0 commit comments

Comments
 (0)