Loading IVOA.js +1 −1 Original line number Diff line number Diff line Loading @@ -71,7 +71,6 @@ async function IVOA_tablesEndpoint(req, IO) { * @returns {Promise<void>} - Starts the server and binds all routes */ async function IVOA_main(IO) { await putStr("IVOA server started")(IO); const port = await readRESTPort(IO); startRESTServer(port, [ { path: "/tap", endpoint: IVOA_tapEndpoint }, Loading @@ -79,6 +78,7 @@ async function IVOA_main(IO) { { path: "/tap/capabilities", endpoint: IVOA_capabilitiesEndpoint }, { path: "/tap/tables", endpoint: IVOA_tablesEndpoint }, ])(IO); await putStr("IVOA server started")(IO); } /** Loading IVOA.py +64 −47 Original line number Diff line number Diff line # IVOA # IVOA implementation from Lib.IO import InternalError, IO, performIO from Lib.IO import putStr, readFile import asyncio from Lib.IO import InternalError, IO, perform_io from Lib.IO import put_str, read_file from Lib.PostgreSQL import connPostgreSQL, fetchRows, quitPostgreSQL from Lib.PostgreSQL import conn_postgresql, fetch_rows, quit_postgresql from Lib.REST import startRESTServer, readRESTPort from Lib.REST import start_rest_server, read_rest_port from Lib.VOTable import buildVOTablesFromRows from Lib.VOTable import build_votables_from_rows async def IVOA_tapEndpoint(req, IO): async def ivoa_tap_endpoint(req, io): """ Serves the static /tap HTML landing page (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - HTML content Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: HTML content. """ return await readFile("Stat/tap.html")(IO) return await read_file("Stat/tap.html")(io) async def IVOA_availabilityEndpoint(req, IO): async def ivoa_availability_endpoint(req, io): """ Serves the static /availability XML response (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - XML content Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: XML content. """ return await readFile("Stat/availability.xml")(IO) return await read_file("Stat/availability.xml")(io) async def IVOA_capabilitiesEndpoint(req, IO): async def ivoa_capabilities_endpoint(req, io): """ Serves the static /capabilities XML response (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - XML content Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: XML content. """ return await readFile("Stat/capabilities.xml")(IO) return await read_file("Stat/capabilities.xml")(io) async def IVOA_tablesEndpoint(req, IO): async def ivoa_tables_endpoint(req, io): """ Executes an SQL query from file and returns a VOTable representation (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - XML content in VOTable format or error message Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: XML content in VOTable format or error message. """ query = await readFile("Query/tables.sql")(IO) conn = await connPostgreSQL()(IO) result = await fetchRows(conn, query, [])(IO) await quitPostgreSQL(conn)(IO) query = await read_file("Query/tables.sql")(io) conn = await conn_postgresql()(io) result = await fetch_rows(conn, query, [])(io) await quit_postgresql(conn)(io) if result["tag"] == "nothing": return '<?xml version="1.0" encoding="UTF-8"?><error>No tables found</error>' return buildVOTablesFromRows(result["value"]) return build_votables_from_rows(result["value"]) async def IVOA_main(IO): async def ivoa_main(io): """ Main entry point for the IVOA TAP and DataLink server (functional signature: IO -> IO ()). @param IO: IO token @returns: Coroutine[None] - Starts the server and binds all routes Args: io: IO token. Returns: Awaitable[None]: Starts the server and binds all routes. """ await putStr("IVOA server started")(IO) port = readRESTPort(IO) await startRESTServer( port = read_rest_port(io) await start_rest_server( port, [ {"path": "/tap", "endpoint": IVOA_tapEndpoint}, {"path": "/tap/availability", "endpoint": IVOA_availabilityEndpoint}, {"path": "/tap/capabilities", "endpoint": IVOA_capabilitiesEndpoint}, {"path": "/tap/tables", "endpoint": IVOA_tablesEndpoint}, {"path": "/tap", "endpoint": ivoa_tap_endpoint}, {"path": "/tap/availability", "endpoint": ivoa_availability_endpoint}, {"path": "/tap/capabilities", "endpoint": ivoa_capabilities_endpoint}, {"path": "/tap/tables", "endpoint": ivoa_tables_endpoint}, ], )(IO) )(io) await put_str("IVOA server started")(io) await asyncio.Event().wait() def mainExpression(): def main_expression(): """ Wraps the program execution in a safe IO environment (functional signature: () -> IO ()). @returns: IO () - The IO token after the execution Returns: IO: The IO token after the execution. """ return performIO(lambda IO: IVOA_main(IO)) return perform_io(lambda io: ivoa_main(io)) if __name__ == "__main__": import asyncio try: asyncio.run(mainExpression()) asyncio.run(main_expression()) except InternalError as e: asyncio.run(putStr("ERROR: " + str(e))(IO)) asyncio.run(put_str("ERROR: " + str(e))(IO)) except Exception as e: raise Lib/IO.js +2 −2 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ export function performIO(f) { * Prints a string to the console (functional signature: String -> IO -> IO). * * @param {string} x - Text to print * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that prints a string to the console */ export const putStr = (x) => async (IO) => { console.log(x); Loading @@ -53,7 +53,7 @@ export const putStr = (x) => async (IO) => { * Reads the contents of a file as UTF-8 (functional signature: String -> IO -> IO String). * * @param {string} path - Path to the file * @returns {function(Symbol): Promise<string>} - A CPS IO action * @returns {function(Symbol): Promise<string>} - A CPS IO action that reads the contents of a file as a UTF-8 string */ export const readFile = (path) => async (IO) => { return await fs.readFile(path, "utf-8"); Loading Lib/IO.py +42 −30 Original line number Diff line number Diff line Loading @@ -5,87 +5,99 @@ # - pip install aiofiles import aiofiles from typing import Callable, Awaitable class InternalError(Exception): """ Custom error type to represent internal application exceptions (functional signature: String -> InternalError). @param message: The error message Args: message (str): The error message. """ def __init__(self, message): def __init__(self, message: str): super().__init__(message) # Token representing the 'IO world' in an implicit continuation-passing style (functional signature: Symbol). IO = object() """ Token representing the 'IO world' in an implicit continuation-passing style (functional signature: Symbol). """ def createIO(f): def create_io(f: Callable[[object], any]) -> any: """ Applies an implicit CPS IO continuation to the IO token (functional signature: ((Symbol) -> a) -> a). @param f: A CPS function expecting an IO token @returns: The result of applying the function to the "IO world" Args: f (Callable[[Symbol], Any]): A CPS function expecting an IO token. Returns: Any: The result of applying the function to the IO world. """ return f(IO) def performIO(f): def perform_io(f: Callable[[object], Awaitable[any]]) -> Awaitable[any]: """ Alias for createIO which semantically marks a point where an effect is executed (functional signature: ((Symbol) -> a) -> a). Alias for create_io which semantically marks a point where an effect is executed (functional signature: ((Symbol) -> a) -> a). @param f: A CPS function @returns: The result of the function performing IO operations Args: f (Callable[[Symbol], Awaitable[Any]]): A CPS function. Returns: Awaitable[Any]: The result of the function performing IO operations. """ return createIO(lambda w: f(w)) return create_io(lambda w: f(w)) def putStr(x): def put_str(x: str) -> Callable[[object], Awaitable[object]]: """ Prints a string to the console (functional signature: String -> IO -> IO). @param x: Text to print @returns: A CPS IO action Args: x (str): Text to print. Returns: Callable[[Symbol], Awaitable[Symbol]]: A CPS IO action that prints the string. """ async def inner(IO): async def inner(io: object) -> object: print(x) return IO return io return inner def readFile(path): def read_file(path: str) -> Callable[[object], Awaitable[str]]: """ Reads the contents of a file as UTF-8 (functional signature: String -> IO -> IO String). @param path: Path to the file @returns: A CPS IO action Args: path (str): Path to the file. Returns: Callable[[Symbol], Awaitable[str]]: A CPS IO action that reads the contents. """ async def inner(IO): async def inner(io: object) -> str: async with aiofiles.open(path, mode="r", encoding="utf-8") as f: content = await f.read() return content return await f.read() return inner # Represents the absence of a value (functional signature: Maybe a). nothing = {"tag": "nothing"} """ Represents the absence of a value (functional signature: Maybe a). """ def just(value): def just(value: any) -> dict: """ Wraps a value into a 'just' tagged union (functional signature: a -> Maybe a). @param value: The value to wrap @returns: A tagged value {tag: 'just', value: *} Args: value (Any): The value to wrap. Returns: dict: A tagged value {'tag': 'just', 'value': *}. """ return {"tag": "just", "value": value} Lib/PostgreSQL.js +25 −14 Original line number Diff line number Diff line Loading @@ -15,19 +15,24 @@ import config from "../Conf/PostgreSQL.json" with { type: "json" }; const { Client } = pkg; /** * @typedef {import("pg").Client} PGConn - A PostgreSQL connection in the CPS model */ /** * Connects to the PostgreSQL server using parameters from the config file (functional signature: () -> IO PGConn). * * @returns {function(Symbol): Promise<Client>} - A CPS IO action * @returns {function(Symbol): Promise<PGConn>} - A CPS IO action that returns a connected PostgreSQL connection * @throws {InternalError} - If the connection fails */ export function connPostgreSQL() { return async (IO) => { const client = new Client(config); const conn = new Client(config); try { await client.connect(); putStr("Connected to PostgreSQL")(IO); return client; await conn.connect(); await putStr("Connected to PostgreSQL")(IO); return conn; } catch (err) { throw new InternalError("Connection failed: " + err.message); } Loading @@ -37,17 +42,17 @@ export function connPostgreSQL() { /** * Executes an SQL query with parameters, returning no result (functional signature: PGConn -> String -> List String -> IO ()). * * @param {Client} conn - A connected PostgreSQL client * @param {PGConn} conn - A connected PostgreSQL client * @param {string} sql - The SQL statement * @param {Array<string>} params - A list of parameters to bind * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that executes an SQL query without returning any result * @throws {InternalError} - If the query execution fails */ export function execQuery(conn, sql, params) { return async (IO) => { try { await conn.query(sql, params); putStr("Query executed")(IO); await putStr("Query executed")(IO); return IO; } catch (err) { throw new InternalError("Execution failed: " + err.message); Loading @@ -58,17 +63,22 @@ export function execQuery(conn, sql, params) { /** * Executes a SELECT query and returns the resulting rows (functional signature: PGConn -> String -> List String -> IO (Maybe (List (List String)))). * * @param {Client} conn - A connected PostgreSQL client * @param {PGConn} conn - A connected PostgreSQL client * @param {string} sql - The SQL query * @param {Array<string>} params - A list of parameters to bind * @returns {function(Symbol): Promise<{ tag: "just", value: any[] }>} - A CPS IO action * @returns {function(Symbol): Promise<{ tag: "just", value: any[] }>} - A CPS IO action that returns query result rows wrapped in a Maybe * @throws {InternalError} - If the query fails */ export function fetchRows(conn, sql, params) { return async (IO) => { try { const res = await conn.query(sql, params); putStr(`${res.rows.length} rows retrieved`)(IO); await putStr(`${res.rows.length} rows retrieved`)(IO); if (res.rows.length === 0) { return nothing; } const rowsAsArrays = res.rows.map((row) => [ row.schema_name, row.table_name, Loading @@ -77,6 +87,7 @@ export function fetchRows(conn, sql, params) { row.datatype, row.description, ]); return just(rowsAsArrays); } catch (err) { throw new InternalError("Select failed: " + err.message); Loading @@ -87,13 +98,13 @@ export function fetchRows(conn, sql, params) { /** * Closes the PostgreSQL connection (functional signature: PGConn -> IO ()). * * @param {Client} conn - A connected PostgreSQL client * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action * @param {PGConn} conn - A connected PostgreSQL client * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that closes the PostgreSQL connection */ export function quitPostgreSQL(conn) { return async (IO) => { await conn.end(); putStr("PostgreSQL connection closed")(IO); await putStr("PostgreSQL connection closed")(IO); return IO; }; } Loading
IVOA.js +1 −1 Original line number Diff line number Diff line Loading @@ -71,7 +71,6 @@ async function IVOA_tablesEndpoint(req, IO) { * @returns {Promise<void>} - Starts the server and binds all routes */ async function IVOA_main(IO) { await putStr("IVOA server started")(IO); const port = await readRESTPort(IO); startRESTServer(port, [ { path: "/tap", endpoint: IVOA_tapEndpoint }, Loading @@ -79,6 +78,7 @@ async function IVOA_main(IO) { { path: "/tap/capabilities", endpoint: IVOA_capabilitiesEndpoint }, { path: "/tap/tables", endpoint: IVOA_tablesEndpoint }, ])(IO); await putStr("IVOA server started")(IO); } /** Loading
IVOA.py +64 −47 Original line number Diff line number Diff line # IVOA # IVOA implementation from Lib.IO import InternalError, IO, performIO from Lib.IO import putStr, readFile import asyncio from Lib.IO import InternalError, IO, perform_io from Lib.IO import put_str, read_file from Lib.PostgreSQL import connPostgreSQL, fetchRows, quitPostgreSQL from Lib.PostgreSQL import conn_postgresql, fetch_rows, quit_postgresql from Lib.REST import startRESTServer, readRESTPort from Lib.REST import start_rest_server, read_rest_port from Lib.VOTable import buildVOTablesFromRows from Lib.VOTable import build_votables_from_rows async def IVOA_tapEndpoint(req, IO): async def ivoa_tap_endpoint(req, io): """ Serves the static /tap HTML landing page (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - HTML content Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: HTML content. """ return await readFile("Stat/tap.html")(IO) return await read_file("Stat/tap.html")(io) async def IVOA_availabilityEndpoint(req, IO): async def ivoa_availability_endpoint(req, io): """ Serves the static /availability XML response (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - XML content Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: XML content. """ return await readFile("Stat/availability.xml")(IO) return await read_file("Stat/availability.xml")(io) async def IVOA_capabilitiesEndpoint(req, IO): async def ivoa_capabilities_endpoint(req, io): """ Serves the static /capabilities XML response (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - XML content Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: XML content. """ return await readFile("Stat/capabilities.xml")(IO) return await read_file("Stat/capabilities.xml")(io) async def IVOA_tablesEndpoint(req, IO): async def ivoa_tables_endpoint(req, io): """ Executes an SQL query from file and returns a VOTable representation (functional signature: Request -> IO -> IO String). @param req: Incoming HTTP request @param IO: IO token @returns: Coroutine[str] - XML content in VOTable format or error message Args: req: Incoming HTTP request. io: IO token. Returns: Awaitable[str]: XML content in VOTable format or error message. """ query = await readFile("Query/tables.sql")(IO) conn = await connPostgreSQL()(IO) result = await fetchRows(conn, query, [])(IO) await quitPostgreSQL(conn)(IO) query = await read_file("Query/tables.sql")(io) conn = await conn_postgresql()(io) result = await fetch_rows(conn, query, [])(io) await quit_postgresql(conn)(io) if result["tag"] == "nothing": return '<?xml version="1.0" encoding="UTF-8"?><error>No tables found</error>' return buildVOTablesFromRows(result["value"]) return build_votables_from_rows(result["value"]) async def IVOA_main(IO): async def ivoa_main(io): """ Main entry point for the IVOA TAP and DataLink server (functional signature: IO -> IO ()). @param IO: IO token @returns: Coroutine[None] - Starts the server and binds all routes Args: io: IO token. Returns: Awaitable[None]: Starts the server and binds all routes. """ await putStr("IVOA server started")(IO) port = readRESTPort(IO) await startRESTServer( port = read_rest_port(io) await start_rest_server( port, [ {"path": "/tap", "endpoint": IVOA_tapEndpoint}, {"path": "/tap/availability", "endpoint": IVOA_availabilityEndpoint}, {"path": "/tap/capabilities", "endpoint": IVOA_capabilitiesEndpoint}, {"path": "/tap/tables", "endpoint": IVOA_tablesEndpoint}, {"path": "/tap", "endpoint": ivoa_tap_endpoint}, {"path": "/tap/availability", "endpoint": ivoa_availability_endpoint}, {"path": "/tap/capabilities", "endpoint": ivoa_capabilities_endpoint}, {"path": "/tap/tables", "endpoint": ivoa_tables_endpoint}, ], )(IO) )(io) await put_str("IVOA server started")(io) await asyncio.Event().wait() def mainExpression(): def main_expression(): """ Wraps the program execution in a safe IO environment (functional signature: () -> IO ()). @returns: IO () - The IO token after the execution Returns: IO: The IO token after the execution. """ return performIO(lambda IO: IVOA_main(IO)) return perform_io(lambda io: ivoa_main(io)) if __name__ == "__main__": import asyncio try: asyncio.run(mainExpression()) asyncio.run(main_expression()) except InternalError as e: asyncio.run(putStr("ERROR: " + str(e))(IO)) asyncio.run(put_str("ERROR: " + str(e))(IO)) except Exception as e: raise
Lib/IO.js +2 −2 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ export function performIO(f) { * Prints a string to the console (functional signature: String -> IO -> IO). * * @param {string} x - Text to print * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that prints a string to the console */ export const putStr = (x) => async (IO) => { console.log(x); Loading @@ -53,7 +53,7 @@ export const putStr = (x) => async (IO) => { * Reads the contents of a file as UTF-8 (functional signature: String -> IO -> IO String). * * @param {string} path - Path to the file * @returns {function(Symbol): Promise<string>} - A CPS IO action * @returns {function(Symbol): Promise<string>} - A CPS IO action that reads the contents of a file as a UTF-8 string */ export const readFile = (path) => async (IO) => { return await fs.readFile(path, "utf-8"); Loading
Lib/IO.py +42 −30 Original line number Diff line number Diff line Loading @@ -5,87 +5,99 @@ # - pip install aiofiles import aiofiles from typing import Callable, Awaitable class InternalError(Exception): """ Custom error type to represent internal application exceptions (functional signature: String -> InternalError). @param message: The error message Args: message (str): The error message. """ def __init__(self, message): def __init__(self, message: str): super().__init__(message) # Token representing the 'IO world' in an implicit continuation-passing style (functional signature: Symbol). IO = object() """ Token representing the 'IO world' in an implicit continuation-passing style (functional signature: Symbol). """ def createIO(f): def create_io(f: Callable[[object], any]) -> any: """ Applies an implicit CPS IO continuation to the IO token (functional signature: ((Symbol) -> a) -> a). @param f: A CPS function expecting an IO token @returns: The result of applying the function to the "IO world" Args: f (Callable[[Symbol], Any]): A CPS function expecting an IO token. Returns: Any: The result of applying the function to the IO world. """ return f(IO) def performIO(f): def perform_io(f: Callable[[object], Awaitable[any]]) -> Awaitable[any]: """ Alias for createIO which semantically marks a point where an effect is executed (functional signature: ((Symbol) -> a) -> a). Alias for create_io which semantically marks a point where an effect is executed (functional signature: ((Symbol) -> a) -> a). @param f: A CPS function @returns: The result of the function performing IO operations Args: f (Callable[[Symbol], Awaitable[Any]]): A CPS function. Returns: Awaitable[Any]: The result of the function performing IO operations. """ return createIO(lambda w: f(w)) return create_io(lambda w: f(w)) def putStr(x): def put_str(x: str) -> Callable[[object], Awaitable[object]]: """ Prints a string to the console (functional signature: String -> IO -> IO). @param x: Text to print @returns: A CPS IO action Args: x (str): Text to print. Returns: Callable[[Symbol], Awaitable[Symbol]]: A CPS IO action that prints the string. """ async def inner(IO): async def inner(io: object) -> object: print(x) return IO return io return inner def readFile(path): def read_file(path: str) -> Callable[[object], Awaitable[str]]: """ Reads the contents of a file as UTF-8 (functional signature: String -> IO -> IO String). @param path: Path to the file @returns: A CPS IO action Args: path (str): Path to the file. Returns: Callable[[Symbol], Awaitable[str]]: A CPS IO action that reads the contents. """ async def inner(IO): async def inner(io: object) -> str: async with aiofiles.open(path, mode="r", encoding="utf-8") as f: content = await f.read() return content return await f.read() return inner # Represents the absence of a value (functional signature: Maybe a). nothing = {"tag": "nothing"} """ Represents the absence of a value (functional signature: Maybe a). """ def just(value): def just(value: any) -> dict: """ Wraps a value into a 'just' tagged union (functional signature: a -> Maybe a). @param value: The value to wrap @returns: A tagged value {tag: 'just', value: *} Args: value (Any): The value to wrap. Returns: dict: A tagged value {'tag': 'just', 'value': *}. """ return {"tag": "just", "value": value}
Lib/PostgreSQL.js +25 −14 Original line number Diff line number Diff line Loading @@ -15,19 +15,24 @@ import config from "../Conf/PostgreSQL.json" with { type: "json" }; const { Client } = pkg; /** * @typedef {import("pg").Client} PGConn - A PostgreSQL connection in the CPS model */ /** * Connects to the PostgreSQL server using parameters from the config file (functional signature: () -> IO PGConn). * * @returns {function(Symbol): Promise<Client>} - A CPS IO action * @returns {function(Symbol): Promise<PGConn>} - A CPS IO action that returns a connected PostgreSQL connection * @throws {InternalError} - If the connection fails */ export function connPostgreSQL() { return async (IO) => { const client = new Client(config); const conn = new Client(config); try { await client.connect(); putStr("Connected to PostgreSQL")(IO); return client; await conn.connect(); await putStr("Connected to PostgreSQL")(IO); return conn; } catch (err) { throw new InternalError("Connection failed: " + err.message); } Loading @@ -37,17 +42,17 @@ export function connPostgreSQL() { /** * Executes an SQL query with parameters, returning no result (functional signature: PGConn -> String -> List String -> IO ()). * * @param {Client} conn - A connected PostgreSQL client * @param {PGConn} conn - A connected PostgreSQL client * @param {string} sql - The SQL statement * @param {Array<string>} params - A list of parameters to bind * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that executes an SQL query without returning any result * @throws {InternalError} - If the query execution fails */ export function execQuery(conn, sql, params) { return async (IO) => { try { await conn.query(sql, params); putStr("Query executed")(IO); await putStr("Query executed")(IO); return IO; } catch (err) { throw new InternalError("Execution failed: " + err.message); Loading @@ -58,17 +63,22 @@ export function execQuery(conn, sql, params) { /** * Executes a SELECT query and returns the resulting rows (functional signature: PGConn -> String -> List String -> IO (Maybe (List (List String)))). * * @param {Client} conn - A connected PostgreSQL client * @param {PGConn} conn - A connected PostgreSQL client * @param {string} sql - The SQL query * @param {Array<string>} params - A list of parameters to bind * @returns {function(Symbol): Promise<{ tag: "just", value: any[] }>} - A CPS IO action * @returns {function(Symbol): Promise<{ tag: "just", value: any[] }>} - A CPS IO action that returns query result rows wrapped in a Maybe * @throws {InternalError} - If the query fails */ export function fetchRows(conn, sql, params) { return async (IO) => { try { const res = await conn.query(sql, params); putStr(`${res.rows.length} rows retrieved`)(IO); await putStr(`${res.rows.length} rows retrieved`)(IO); if (res.rows.length === 0) { return nothing; } const rowsAsArrays = res.rows.map((row) => [ row.schema_name, row.table_name, Loading @@ -77,6 +87,7 @@ export function fetchRows(conn, sql, params) { row.datatype, row.description, ]); return just(rowsAsArrays); } catch (err) { throw new InternalError("Select failed: " + err.message); Loading @@ -87,13 +98,13 @@ export function fetchRows(conn, sql, params) { /** * Closes the PostgreSQL connection (functional signature: PGConn -> IO ()). * * @param {Client} conn - A connected PostgreSQL client * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action * @param {PGConn} conn - A connected PostgreSQL client * @returns {function(Symbol): Promise<Symbol>} - A CPS IO action that closes the PostgreSQL connection */ export function quitPostgreSQL(conn) { return async (IO) => { await conn.end(); putStr("PostgreSQL connection closed")(IO); await putStr("PostgreSQL connection closed")(IO); return IO; }; }