import QueryStream from 'pg-query-stream'; import { pipeline } from 'node:stream/promises'; import { Readable } from 'node:stream'; import { query, getPool } from './index.js'; import { batch } from '../lib/streams.js'; const SELECT_NOTES = 'SELECT id, text, x, y, author, color FROM notes ORDER BY id'; // Postgres caps a statement at 65535 bind parameters; six columns per note // leaves 10922 as the hard ceiling, so stay well under it. const INSERT_BATCH_SIZE = 1000; export const getAllNotes = async () => { const { rows } = await query(SELECT_NOTES); return rows; }; /** * Streams every note as an object-mode Readable. The pooled client is checked * out for the life of the stream and released once it ends, errors, or is * destroyed early by a consumer. * * @returns {Promise} */ export const streamAllNotes = async () => { const client = await getPool().connect(); let released = false; const release = () => { if (released) return; released = true; client.release(); }; try { const rows = client.query(new QueryStream(SELECT_NOTES)); rows.once('end', release); rows.once('error', release); rows.once('close', release); return rows; } catch (err) { release(); throw err; } }; export const getNoteById = async (id) => { const { rows } = await query( 'SELECT id, text, x, y, author, color FROM notes WHERE id = $1', [id] ); return rows[0] || null; }; export const createNote = async (note) => { const { rows } = await query( `INSERT INTO notes (id, text, x, y, author, color) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, text, x, y, author, color`, [note.id, note.text, note.x ?? 0, note.y ?? 0, note.author, note.color ?? 'yellow'] ); return rows[0]; }; const insertNoteBatch = async (notes) => { const values = []; const placeholders = []; notes.forEach((note, i) => { const offset = i * 6; placeholders.push( `($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}, $${offset + 6})` ); values.push( note.id, note.text, note.x ?? 0, note.y ?? 0, note.author, note.color ?? 'yellow' ); }); const { rows } = await query( `INSERT INTO notes (id, text, x, y, author, color) VALUES ${placeholders.join(', ')} RETURNING id, text, x, y, author, color`, values ); return rows; }; export const createNotes = async (notes) => { if (!notes || notes.length === 0) { return []; } const inserted = []; await pipeline( Readable.from(notes, { objectMode: true }), batch(INSERT_BATCH_SIZE), async (batches) => { for await (const chunk of batches) { inserted.push(...await insertNoteBatch(chunk)); } } ); return inserted; };