@graffy/pg 0.16.20 → 0.16.21-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const common = require("@graffy/common");
4
4
  const debug = require("debug");
5
5
  const pg$1 = require("pg");
6
+ const sqlFormatter = require("sql-formatter");
6
7
  class Sql {
7
8
  constructor(rawStrings, rawValues) {
8
9
  if (rawStrings.length - 1 !== rawValues.length) {
@@ -79,6 +80,20 @@ const empty = raw("");
79
80
  function sql(strings, ...values) {
80
81
  return new Sql(strings, values);
81
82
  }
83
+ function formatSql(sql2) {
84
+ const strings = sql2.strings.slice(0);
85
+ const values = sql2.values.slice(0);
86
+ const output = [];
87
+ while (strings.length) {
88
+ output.push(strings.shift().replace(/\s+/g, " "));
89
+ if (!values.length) break;
90
+ const value = values.shift();
91
+ output.push(
92
+ typeof value === "number" ? value.toString() : typeof value === "object" ? `'${JSON.stringify(value)}'` : `'${value}'`
93
+ );
94
+ }
95
+ return sqlFormatter.format(output.join("").trim(), { language: "postgresql" });
96
+ }
82
97
  const getJsonBuildTrusted = (variadic) => {
83
98
  const args = join(
84
99
  Object.entries(variadic).map(([name, value]) => {
@@ -157,23 +172,23 @@ const getSelectCols = (options, projection = null) => {
157
172
  sqls.push(
158
173
  sql`jsonb_build_object(${join(subSqls, ", ")}) AS "${raw(key)}"`
159
174
  );
175
+ }
176
+ if (key[0] === "$") continue;
177
+ if (typeof projection[key] === "object") {
178
+ const optimisedJsonBuild = getOptimisedJsonBuild(
179
+ projection[key],
180
+ [],
181
+ key,
182
+ options
183
+ );
184
+ sqls.push(
185
+ sql`jsonb_build_object(${join(optimisedJsonBuild, ", ")}) AS "${raw(key)}"`
186
+ );
160
187
  } else {
161
- if (typeof projection[key] === "object") {
162
- const optimisedJsonBuild = getOptimisedJsonBuild(
163
- projection[key],
164
- [],
165
- key,
166
- options
167
- );
168
- sqls.push(
169
- sql`jsonb_build_object(${join(optimisedJsonBuild, ", ")}) AS "${raw(key)}"`
170
- );
171
- } else {
172
- sqls.push(sql`"${raw(key)}"`);
173
- }
188
+ sqls.push(sql`"${raw(key)}"`);
174
189
  }
175
190
  }
176
- return join(sqls, ", ");
191
+ return sqls.length ? join(sqls, ", ") : sql`TRUE AS "$"`;
177
192
  };
178
193
  function vertexSql(array, nullValue) {
179
194
  return sql`array[${join(
@@ -754,7 +769,7 @@ class Db {
754
769
  /*
755
770
  Adds .schema to tableOptions if it doesn't exist yet.
756
771
  It mutates the argument, to "persist" the results and
757
- avoid this query in every operation.
772
+ avoid this query in every operation.
758
773
  */
759
774
  async ensureSchema(tableOptions, typeOids) {
760
775
  if (tableOptions.schema) return;
@@ -805,19 +820,33 @@ class Db {
805
820
  const prefix = common.encodePath(rawPrefix);
806
821
  await this.ensureSchema(tableOptions);
807
822
  const getByArgs = async (args, projection) => {
808
- const result = await this.readSql(
809
- selectByArgs(args, projection, tableOptions),
810
- tableOptions
811
- );
823
+ const sql2 = selectByArgs(args, projection, tableOptions);
824
+ const result = await this.readSql(sql2, tableOptions);
812
825
  const wrappedGraph = common.encodeGraph(common.wrapObject(result, rawPrefix));
813
826
  log("getByArgs", wrappedGraph);
814
827
  common.merge(results, wrappedGraph);
815
828
  };
816
- const getByIds = async () => {
817
- const result = await this.readSql(
818
- selectByIds(Object.keys(idQueries), null, tableOptions),
819
- tableOptions
829
+ const explainArgs = async (args, projection) => {
830
+ const { analyze, $explain: qArgs } = args;
831
+ const qSql = selectByArgs(qArgs, null, tableOptions);
832
+ const sql$1 = sql`EXPLAIN (${analyze ? sql`ANALYZE, BUFFERS, TIMING, ` : sql``}COSTS, VERBOSE, FORMAT JSON) ${qSql}`;
833
+ const result = await this.readSql(sql$1, tableOptions);
834
+ const wrappedGraph = common.encodeGraph(
835
+ common.wrapObject(
836
+ {
837
+ $key: args,
838
+ sql: formatSql(qSql),
839
+ plan: result[0]["QUERY PLAN"][0]
840
+ },
841
+ rawPrefix
842
+ )
820
843
  );
844
+ log("explainArgs", wrappedGraph);
845
+ common.merge(results, wrappedGraph);
846
+ };
847
+ const getByIds = async () => {
848
+ const sql2 = selectByIds(Object.keys(idQueries), null, tableOptions);
849
+ const result = await this.readSql(sql2, tableOptions);
821
850
  for (const object of result) {
822
851
  const wrappedGraph = common.encodeGraph(common.wrapObject(object, rawPrefix));
823
852
  log("getByIds", wrappedGraph);
@@ -836,7 +865,11 @@ class Db {
836
865
  }
837
866
  } else {
838
867
  const projection = node.children ? common.decodeQuery(node.children) : null;
839
- promises.push(getByArgs(args, projection));
868
+ if (args.$explain) {
869
+ promises.push(explainArgs(args));
870
+ } else {
871
+ promises.push(getByArgs(args, projection));
872
+ }
840
873
  }
841
874
  } else {
842
875
  idQueries[args] = node.children;
package/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { isEmpty, isPlainObject, encodePath, unwrap, decodeArgs, decodeQuery, finalize, wrap, isRange, cmp, decodeGraph, mergeObject, wrapObject, merge, encodeGraph, remove } from "@graffy/common";
2
2
  import debug from "debug";
3
3
  import pg$1 from "pg";
4
+ import { format } from "sql-formatter";
4
5
  class Sql {
5
6
  constructor(rawStrings, rawValues) {
6
7
  if (rawStrings.length - 1 !== rawValues.length) {
@@ -77,6 +78,20 @@ const empty = raw("");
77
78
  function sql(strings, ...values) {
78
79
  return new Sql(strings, values);
79
80
  }
81
+ function formatSql(sql2) {
82
+ const strings = sql2.strings.slice(0);
83
+ const values = sql2.values.slice(0);
84
+ const output = [];
85
+ while (strings.length) {
86
+ output.push(strings.shift().replace(/\s+/g, " "));
87
+ if (!values.length) break;
88
+ const value = values.shift();
89
+ output.push(
90
+ typeof value === "number" ? value.toString() : typeof value === "object" ? `'${JSON.stringify(value)}'` : `'${value}'`
91
+ );
92
+ }
93
+ return format(output.join("").trim(), { language: "postgresql" });
94
+ }
80
95
  const getJsonBuildTrusted = (variadic) => {
81
96
  const args = join(
82
97
  Object.entries(variadic).map(([name, value]) => {
@@ -155,23 +170,23 @@ const getSelectCols = (options, projection = null) => {
155
170
  sqls.push(
156
171
  sql`jsonb_build_object(${join(subSqls, ", ")}) AS "${raw(key)}"`
157
172
  );
173
+ }
174
+ if (key[0] === "$") continue;
175
+ if (typeof projection[key] === "object") {
176
+ const optimisedJsonBuild = getOptimisedJsonBuild(
177
+ projection[key],
178
+ [],
179
+ key,
180
+ options
181
+ );
182
+ sqls.push(
183
+ sql`jsonb_build_object(${join(optimisedJsonBuild, ", ")}) AS "${raw(key)}"`
184
+ );
158
185
  } else {
159
- if (typeof projection[key] === "object") {
160
- const optimisedJsonBuild = getOptimisedJsonBuild(
161
- projection[key],
162
- [],
163
- key,
164
- options
165
- );
166
- sqls.push(
167
- sql`jsonb_build_object(${join(optimisedJsonBuild, ", ")}) AS "${raw(key)}"`
168
- );
169
- } else {
170
- sqls.push(sql`"${raw(key)}"`);
171
- }
186
+ sqls.push(sql`"${raw(key)}"`);
172
187
  }
173
188
  }
174
- return join(sqls, ", ");
189
+ return sqls.length ? join(sqls, ", ") : sql`TRUE AS "$"`;
175
190
  };
176
191
  function vertexSql(array, nullValue) {
177
192
  return sql`array[${join(
@@ -708,7 +723,7 @@ class Db {
708
723
  const cubeOid = Number.parseInt(((_b = (_a = tableOptions == null ? void 0 : tableOptions.schema) == null ? void 0 : _a.typeOids) == null ? void 0 : _b.cube) || "0") || null;
709
724
  try {
710
725
  sql2.types = {
711
- getTypeParser: (oid, format) => {
726
+ getTypeParser: (oid, format2) => {
712
727
  if (oid === types.builtins.INT8) {
713
728
  return (value) => Number.parseInt(value, 10);
714
729
  }
@@ -720,7 +735,7 @@ class Db {
720
735
  return array.length > 1 ? array : array[0];
721
736
  };
722
737
  }
723
- return types.getTypeParser(oid, format);
738
+ return types.getTypeParser(oid, format2);
724
739
  }
725
740
  };
726
741
  return await this.client.query(sql2);
@@ -752,7 +767,7 @@ class Db {
752
767
  /*
753
768
  Adds .schema to tableOptions if it doesn't exist yet.
754
769
  It mutates the argument, to "persist" the results and
755
- avoid this query in every operation.
770
+ avoid this query in every operation.
756
771
  */
757
772
  async ensureSchema(tableOptions, typeOids) {
758
773
  if (tableOptions.schema) return;
@@ -803,19 +818,33 @@ class Db {
803
818
  const prefix = encodePath(rawPrefix);
804
819
  await this.ensureSchema(tableOptions);
805
820
  const getByArgs = async (args, projection) => {
806
- const result = await this.readSql(
807
- selectByArgs(args, projection, tableOptions),
808
- tableOptions
809
- );
821
+ const sql2 = selectByArgs(args, projection, tableOptions);
822
+ const result = await this.readSql(sql2, tableOptions);
810
823
  const wrappedGraph = encodeGraph(wrapObject(result, rawPrefix));
811
824
  log("getByArgs", wrappedGraph);
812
825
  merge(results, wrappedGraph);
813
826
  };
814
- const getByIds = async () => {
815
- const result = await this.readSql(
816
- selectByIds(Object.keys(idQueries), null, tableOptions),
817
- tableOptions
827
+ const explainArgs = async (args, projection) => {
828
+ const { analyze, $explain: qArgs } = args;
829
+ const qSql = selectByArgs(qArgs, null, tableOptions);
830
+ const sql$1 = sql`EXPLAIN (${analyze ? sql`ANALYZE, BUFFERS, TIMING, ` : sql``}COSTS, VERBOSE, FORMAT JSON) ${qSql}`;
831
+ const result = await this.readSql(sql$1, tableOptions);
832
+ const wrappedGraph = encodeGraph(
833
+ wrapObject(
834
+ {
835
+ $key: args,
836
+ sql: formatSql(qSql),
837
+ plan: result[0]["QUERY PLAN"][0]
838
+ },
839
+ rawPrefix
840
+ )
818
841
  );
842
+ log("explainArgs", wrappedGraph);
843
+ merge(results, wrappedGraph);
844
+ };
845
+ const getByIds = async () => {
846
+ const sql2 = selectByIds(Object.keys(idQueries), null, tableOptions);
847
+ const result = await this.readSql(sql2, tableOptions);
819
848
  for (const object of result) {
820
849
  const wrappedGraph = encodeGraph(wrapObject(object, rawPrefix));
821
850
  log("getByIds", wrappedGraph);
@@ -834,7 +863,11 @@ class Db {
834
863
  }
835
864
  } else {
836
865
  const projection = node.children ? decodeQuery(node.children) : null;
837
- promises.push(getByArgs(args, projection));
866
+ if (args.$explain) {
867
+ promises.push(explainArgs(args));
868
+ } else {
869
+ promises.push(getByArgs(args, projection));
870
+ }
838
871
  }
839
872
  } else {
840
873
  idQueries[args] = node.children;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graffy/pg",
3
3
  "description": "The standard Postgres module for Graffy. Each instance this module mounts a Postgres table as a Graffy subtree.",
4
4
  "author": "aravind (https://github.com/aravindet)",
5
- "version": "0.16.20",
5
+ "version": "0.16.21-alpha.2",
6
6
  "main": "./index.cjs",
7
7
  "exports": {
8
8
  "import": "./index.mjs",
@@ -16,8 +16,9 @@
16
16
  },
17
17
  "license": "Apache-2.0",
18
18
  "dependencies": {
19
- "@graffy/common": "0.16.20",
20
- "debug": "^4.3.3"
19
+ "@graffy/common": "0.16.21-alpha.2",
20
+ "debug": "^4.3.3",
21
+ "sql-formatter": "^15.6.2"
21
22
  },
22
23
  "peerDependencies": {
23
24
  "pg": "^8.0.0"
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @param {import('sql-template-tag').Sql} sql
3
+ * @returns {string}
4
+ */
5
+ export default function formatSql(sql: import("sql-template-tag").Sql): string;