pglite icon indicating copy to clipboard operation
pglite copied to clipboard

pglite parsers "could not be cloned"

Open jay-babu opened this issue 1 month ago • 3 comments

Image Image

The above code throws an error however when doing the same thing except when passing the parsers next to the queries, the error does not occur. This leads me to think there is some error with pglite handling the parsers.

Code that works fine:

        const res = await db.query(itemFiltersOptions.query, itemFiltersOptions.params, {
          parsers: {
            [types.NUMERIC]: (value: string) => {
              console.log("Parsing numeric value:", value);
              return new BigDecimal(value)
            },
          },
        });

jay-babu avatar Oct 29 '25 03:10 jay-babu

I'm not sure I understand which code throws that error?

tdrz avatar Oct 30 '25 15:10 tdrz

@tdrz

Image

this is the code that throws the error.

export class BigDecimal {
  // Configuration: private constants
  private static readonly DECIMALS: number = 18; // Number of decimals on all instances
  private static readonly ROUNDED: boolean = true; // Numbers are truncated (false) or rounded (true)
  private static readonly SHIFT: bigint = 10n ** BigInt(BigDecimal.DECIMALS); // Derived constant
  private static readonly fromBigInt: unique symbol = Symbol(); // Secret to allow construction with given #n value

  public static get ZERO(): BigDecimal {
    return new BigDecimal(0);
  }

  #n: bigint; // the BigInt that holds the BigDecimal's value multiplied by SHIFT

  constructor(
    value: string | number | BigDecimal | bigint,
    convert?: typeof BigDecimal.fromBigInt,
  ) {
    if (value instanceof BigDecimal) {
      this.#n = value.#n;
      return;
    }
    if (convert === BigDecimal.fromBigInt) {
      this.#n = BigInt(value);
      return;
    }

    const str = String(value);
    const [ints, decis = ""] = str.split(".");
    const padded = decis
      .padEnd(BigDecimal.DECIMALS, "0")
      .slice(0, BigDecimal.DECIMALS);
    const base = BigInt(ints + padded);
    const roundIncrement =
      // @ts-expect-error
      BigDecimal.ROUNDED && decis[BigDecimal.DECIMALS] >= "5" ? 1n : 0n;

    this.#n = base + roundIncrement;
  }

  add(num: string | number | BigDecimal): BigDecimal {
    const other = new BigDecimal(num);
    const sum = this.#n + other.#n;
    return new BigDecimal(sum, BigDecimal.fromBigInt);
  }

  subtract(num: string | number | BigDecimal): BigDecimal {
    const other = new BigDecimal(num);
    const diff = this.#n - other.#n;
    return new BigDecimal(diff, BigDecimal.fromBigInt);
  }

  private static divRound(dividend: bigint, divisor: bigint): BigDecimal {
    const quotient = dividend / divisor;
    const remainder = dividend % divisor;
    const shouldRound = BigDecimal.ROUNDED && remainder * 2n >= divisor;
    return new BigDecimal(
      quotient + (shouldRound ? 1n : 0n),
      BigDecimal.fromBigInt,
    );
  }

  multiply(num: string | number | BigDecimal): BigDecimal {
    const other = new BigDecimal(num);
    const product = this.#n * other.#n;
    return BigDecimal.divRound(product, BigDecimal.SHIFT);
  }

  divide(num: string | number | BigDecimal): BigDecimal {
    const other = new BigDecimal(num);
    const scaled = this.#n * BigDecimal.SHIFT;
    return BigDecimal.divRound(scaled, other.#n);
  }

  toString(): string {
    const negative = this.#n < 0n;
    let s = this.#n
      .toString()
      .replace("-", "")
      .padStart(BigDecimal.DECIMALS + 1, "0");
    s = s
      .slice(0, -BigDecimal.DECIMALS)
      .concat(".")
      .concat(s.slice(-BigDecimal.DECIMALS))
      .replace(/(\.0*|0+)$/, "");
    return negative ? "-" + s : s;
  }

  toNumber(): number {
    return Number(this.toString());
  }
}
const pg = await PGliteWorker.create(workerInstance, PGLITE_OPTIONS);

export const PGLITE_OPTIONS = {
  relaxedDurability: true,
  parsers: {
    [types.NUMERIC]: (value: string) => {
      console.log("Parsing numeric value:", value);
      // Parse numeric as float
      return new BigDecimal(value);
    },
    [types.DATE]: (value: string) => {
      return new Date(value);
    },
  },
  fs: indexedDbFS,
  debug: 5 as DebugLevel,
  extensions: {
    live,
    electric: electricSync({}),
  },
};

jay-babu avatar Oct 30 '25 16:10 jay-babu

when calling PGliteWorker.create, then trying to use it, it fails, but using it directly in the query, it works fine

jay-babu avatar Oct 30 '25 16:10 jay-babu