Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/database/postgres/jsonb-utils.ts
Views: 687
import { is_date as isDate } from "@cocalc/util/misc";12/*3jsonbSet: This little piece of very hard to write (and clever?) code4makes it so we can set or **merge in at any nested level** (!)5arbitrary JSON objects. We can also delete any key at any6level by making the value null or undefined! This is amazingly7easy to use in queries -- basically making JSONP with postgres8as expressive as RethinkDB REQL (even better in some ways).9*/1011// The input desc is an object that describes what is being set, e.g.,12// account_id = '... some uuid ';13// desc = {"users": {[account_id]:{group:'collaborator'}}}14// changes the users JSONB field of the table so that users[account_id] has15// the group set to "collaborator".16// If the merge field is set then we merge in the change; otherwise,17// we replace the value.1819// IMPORTANT: this is a dangerous attack vector -- do not call this function20// with unsanitized input from a user!2122export function jsonbSet(23desc: object,24merge: boolean = false25): { set: string; params: any[] } {26const params: any[] = [];27function pushParam(val: any, type: string): number {28if (type.toUpperCase() == "JSONB") {29val = JSON.stringify(val); // this is needed by the driver....}30}31params.push(val);32return params.length;33}3435function set(field: string, data: object, path: string[]): string {36let obj = `COALESCE(${field}#>'{${path.join(",")}}', '{}'::JSONB)`;37for (const key in data) {38const val = data[key];39if (val == null) {40// remove key from object41obj = `(${obj} - '${key}')`;42} else {43// set key in object44if (merge && typeof val === "object" && !isDate(val)) {45const subobj = set(field, val, path.concat([key]));46obj = `JSONB_SET(${obj}, '{${key}}', ${subobj})`;47} else {48// completely replace field[key] with val.49obj = `JSONB_SET(${obj}, '{${key}}', $${pushParam(50val,51"JSONB"52)}::JSONB)`;53}54}55}56return obj;57}5859const v: string[] = [];60for (const field in desc) {61const data = desc[field];62v.push(`${field}=${set(field, data, [])}`);63}64return { set: v.join(","), params };65}666768