prostgles-server 2.0.249 → 2.0.250

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/lib/DboBuilder.ts CHANGED
@@ -156,10 +156,13 @@ export type LocalParams = {
156
156
  testRule?: boolean;
157
157
  tableAlias?: string;
158
158
  // subOne?: boolean;
159
- dbTX?: TableHandlers;
159
+
160
+ tx?: {
161
+ dbTX: TableHandlers;
162
+ t: pgPromise.ITask<{}>;
163
+ }
160
164
 
161
165
  // localTX?: pgPromise.ITask<{}>;
162
- localDBTX?: DBHandlerServer;
163
166
 
164
167
  returnQuery?: boolean;
165
168
 
@@ -2431,6 +2434,8 @@ export class TableHandler extends ViewHandler {
2431
2434
 
2432
2435
  }
2433
2436
 
2437
+ let DATA_TYPES: {oid: string, typname: PG_COLUMN_UDT_DATA_TYPE }[] | undefined;
2438
+ let USER_TABLES: { relid: string; relname: string; }[] | undefined;
2434
2439
 
2435
2440
  import { JOIN_TYPES } from "./Prostgles";
2436
2441
  import { BasicSession } from "./AuthHandler";
@@ -2640,6 +2645,107 @@ export class DboBuilder {
2640
2645
  return this.joinPaths;
2641
2646
  }
2642
2647
 
2648
+ runSQL = async (query: string, params: any, options: SQLOptions | undefined, localParams?: LocalParams) => {
2649
+
2650
+ /** Cache types */
2651
+ DATA_TYPES ??= await this.db.any("SELECT oid, typname FROM pg_type") ?? [];
2652
+ USER_TABLES ??= await this.db.any("SELECT relid, relname FROM pg_catalog.pg_statio_user_tables") ?? [];
2653
+
2654
+ const canRunSQL = async (localParams?: LocalParams) => {
2655
+ if(!localParams?.socket || !localParams?.httpReq) return true;
2656
+
2657
+ const { socket } = localParams;
2658
+ const publishParams = await this.prostgles.publishParser!.getPublishParams({ socket });
2659
+ let res = await this.prostgles.opts.publishRawSQL?.(publishParams);
2660
+ return Boolean(res && typeof res === "boolean" || res === "*");
2661
+ }
2662
+
2663
+ if(!(await canRunSQL(localParams))) throw "Not allowed to run SQL";
2664
+
2665
+ const { returnType, allowListen }: SQLOptions = options || ({} as any);
2666
+ const { socket } = localParams || {};
2667
+
2668
+ const db = localParams?.tx?.t || this.db;
2669
+ if(returnType === "noticeSubscription"){
2670
+ if(!socket) throw "Only allowed with client socket"
2671
+ return await this.prostgles.dbEventsManager?.addNotice(socket);
2672
+ } else if(returnType === "statement"){
2673
+ try {
2674
+ return pgp.as.format(query, params);
2675
+ } catch (err){
2676
+ throw (err as any).toString();
2677
+ }
2678
+ } else if(db) {
2679
+
2680
+ let finalQuery = query + "";
2681
+ if(returnType === "arrayMode" && !["listen ", "notify "].find(c => query.toLowerCase().trim().startsWith(c))){
2682
+ finalQuery = new PQ({ text: pgp.as.format(query, params), rowMode: "array" });
2683
+ }
2684
+
2685
+ let _qres = await db.result(finalQuery, params)
2686
+ const { fields, rows, command } = _qres;
2687
+
2688
+ /**
2689
+ * Fallback for watchSchema in case not superuser and cannot add db event listener
2690
+ */
2691
+ const { watchSchema, watchSchemaType } = this.prostgles?.opts || {};
2692
+
2693
+ if(
2694
+ watchSchema &&
2695
+ (!this.prostgles.isSuperUser || watchSchemaType === "prostgles_queries")
2696
+ ){
2697
+ if(["CREATE", "ALTER", "DROP"].includes(command)){
2698
+ this.prostgles.onSchemaChange({ command, query })
2699
+ } else if(query) {
2700
+ const cleanedQuery = query.toLowerCase().replace(/\s\s+/g, ' ');
2701
+ if(PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase()))){
2702
+ this.prostgles.onSchemaChange({ command, query })
2703
+ }
2704
+ }
2705
+ }
2706
+
2707
+ if(command === "LISTEN"){
2708
+ if(!allowListen) throw new Error(`Your query contains a LISTEN command. Set { allowListen: true } to get subscription hooks. Or ignore this message`)
2709
+ if(!socket) throw "Only allowed with client socket"
2710
+ return await this.prostgles.dbEventsManager?.addNotify(query, socket);
2711
+
2712
+ } else if(returnType === "rows") {
2713
+ return rows;
2714
+
2715
+ } else if(returnType === "row") {
2716
+ return rows[0];
2717
+
2718
+ } else if(returnType === "value") {
2719
+ return Object.values(rows?.[0] || {})?.[0];
2720
+
2721
+ } else if(returnType === "values") {
2722
+ return rows.map(r => Object.values(r[0]));
2723
+
2724
+ } else {
2725
+
2726
+ let qres: SQLResult<typeof returnType> = {
2727
+ duration: 0,
2728
+ ..._qres,
2729
+ fields: fields?.map(f => {
2730
+ const dataType = DATA_TYPES!.find(dt => +dt.oid === +f.dataTypeID)?.typname ?? "text",
2731
+ tableName = USER_TABLES!.find(t => +t.relid === +f.tableID),
2732
+ tsDataType = postgresToTsType(dataType);
2733
+
2734
+ return {
2735
+ ...f,
2736
+ tsDataType,
2737
+ dataType,
2738
+ udt_name: dataType,
2739
+ tableName: tableName?.relname
2740
+ }
2741
+ }) ?? []
2742
+ };
2743
+ return qres;
2744
+ }
2745
+
2746
+ } else console.error("db missing");
2747
+ }
2748
+
2643
2749
  async build(): Promise<DBHandlerServer>{
2644
2750
 
2645
2751
  this.tablesOrViews = await getTablesForSchemaPostgresSQL(this.db);
@@ -2707,105 +2813,102 @@ export class DboBuilder {
2707
2813
 
2708
2814
  if(!this.dbo.sql){
2709
2815
 
2710
- let needType = true;
2711
- let DATA_TYPES: {oid: string, typname: PG_COLUMN_UDT_DATA_TYPE }[] = !needType? [] : await this.db.any("SELECT oid, typname FROM pg_type");
2712
- let USER_TABLES: { relid: string; relname: string; }[] = !needType? [] : await this.db.any("SELECT relid, relname FROM pg_catalog.pg_statio_user_tables");
2713
-
2714
- this.dbo.sql = async (query: string, params: any, options: SQLOptions | undefined, localParams?: LocalParams) => {
2816
+ this.dbo.sql = this.runSQL;
2817
+ // this.dbo.sql = async (query: string, params: any, options: SQLOptions | undefined, localParams?: LocalParams) => {
2715
2818
 
2716
- const canRunSQL = async (localParams?: LocalParams) => {
2717
- if(!localParams) return true;
2718
-
2719
- const { socket } = localParams;
2720
- const publishParams = await this.prostgles.publishParser!.getPublishParams({ socket });
2721
- let res = await this.prostgles.opts.publishRawSQL?.(publishParams);
2722
- return Boolean(res && typeof res === "boolean" || res === "*");
2723
- }
2724
-
2725
- if(!(await canRunSQL(localParams))) throw "Not allowed to run SQL";
2726
-
2727
- const { returnType, allowListen }: SQLOptions = options || ({} as any);
2728
- const { socket } = localParams || {};
2729
-
2730
- if(returnType === "noticeSubscription"){
2731
- if(!socket) throw "Only allowed with client socket"
2732
- return await this.prostgles.dbEventsManager?.addNotice(socket);
2733
- } else if(returnType === "statement"){
2734
- try {
2735
- return pgp.as.format(query, params);
2736
- } catch (err){
2737
- throw (err as any).toString();
2738
- }
2739
- } else if(this.db) {
2740
-
2741
- let finalQuery = query + "";
2742
- if(returnType === "arrayMode" && !["listen ", "notify "].find(c => query.toLowerCase().trim().startsWith(c))){
2743
- finalQuery = new PQ({ text: pgp.as.format(query, params), rowMode: "array" });
2744
- }
2745
-
2746
- let _qres = await this.db.result(finalQuery, params)
2747
- const { fields, rows, command } = _qres;
2748
-
2749
- /**
2750
- * Fallback for watchSchema in case not superuser and cannot add db event listener
2751
- */
2752
- const { watchSchema, watchSchemaType } = this.prostgles?.opts || {};
2753
-
2754
- if(
2755
- watchSchema &&
2756
- (!this.prostgles.isSuperUser || watchSchemaType === "prostgles_queries")
2757
- ){
2758
- if(["CREATE", "ALTER", "DROP"].includes(command)){
2759
- this.prostgles.onSchemaChange({ command, query })
2760
- } else if(query) {
2761
- const cleanedQuery = query.toLowerCase().replace(/\s\s+/g, ' ');
2762
- if(PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase()))){
2763
- this.prostgles.onSchemaChange({ command, query })
2764
- }
2765
- }
2766
- }
2767
-
2768
- if(command === "LISTEN"){
2769
- if(!allowListen) throw new Error(`Your query contains a LISTEN command. Set { allowListen: true } to get subscription hooks. Or ignore this message`)
2770
- if(!socket) throw "Only allowed with client socket"
2771
- return await this.prostgles.dbEventsManager?.addNotify(query, socket);
2772
-
2773
- } else if(returnType === "rows") {
2774
- return rows;
2819
+ // const canRunSQL = async (localParams?: LocalParams) => {
2820
+ // if(!localParams?.socket || !localParams?.httpReq) return true;
2821
+
2822
+ // const { socket } = localParams;
2823
+ // const publishParams = await this.prostgles.publishParser!.getPublishParams({ socket });
2824
+ // let res = await this.prostgles.opts.publishRawSQL?.(publishParams);
2825
+ // return Boolean(res && typeof res === "boolean" || res === "*");
2826
+ // }
2827
+
2828
+ // if(!(await canRunSQL(localParams))) throw "Not allowed to run SQL";
2829
+
2830
+ // const { returnType, allowListen }: SQLOptions = options || ({} as any);
2831
+ // const { socket } = localParams || {};
2832
+
2833
+ // if(returnType === "noticeSubscription"){
2834
+ // if(!socket) throw "Only allowed with client socket"
2835
+ // return await this.prostgles.dbEventsManager?.addNotice(socket);
2836
+ // } else if(returnType === "statement"){
2837
+ // try {
2838
+ // return pgp.as.format(query, params);
2839
+ // } catch (err){
2840
+ // throw (err as any).toString();
2841
+ // }
2842
+ // } else if(this.db) {
2843
+
2844
+ // let finalQuery = query + "";
2845
+ // if(returnType === "arrayMode" && !["listen ", "notify "].find(c => query.toLowerCase().trim().startsWith(c))){
2846
+ // finalQuery = new PQ({ text: pgp.as.format(query, params), rowMode: "array" });
2847
+ // }
2848
+
2849
+ // let _qres = await this.db.result(finalQuery, params)
2850
+ // const { fields, rows, command } = _qres;
2851
+
2852
+ // /**
2853
+ // * Fallback for watchSchema in case not superuser and cannot add db event listener
2854
+ // */
2855
+ // const { watchSchema, watchSchemaType } = this.prostgles?.opts || {};
2856
+
2857
+ // if(
2858
+ // watchSchema &&
2859
+ // (!this.prostgles.isSuperUser || watchSchemaType === "prostgles_queries")
2860
+ // ){
2861
+ // if(["CREATE", "ALTER", "DROP"].includes(command)){
2862
+ // this.prostgles.onSchemaChange({ command, query })
2863
+ // } else if(query) {
2864
+ // const cleanedQuery = query.toLowerCase().replace(/\s\s+/g, ' ');
2865
+ // if(PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase()))){
2866
+ // this.prostgles.onSchemaChange({ command, query })
2867
+ // }
2868
+ // }
2869
+ // }
2870
+
2871
+ // if(command === "LISTEN"){
2872
+ // if(!allowListen) throw new Error(`Your query contains a LISTEN command. Set { allowListen: true } to get subscription hooks. Or ignore this message`)
2873
+ // if(!socket) throw "Only allowed with client socket"
2874
+ // return await this.prostgles.dbEventsManager?.addNotify(query, socket);
2875
+
2876
+ // } else if(returnType === "rows") {
2877
+ // return rows;
2775
2878
 
2776
- } else if(returnType === "row") {
2777
- return rows[0];
2879
+ // } else if(returnType === "row") {
2880
+ // return rows[0];
2778
2881
 
2779
- } else if(returnType === "value") {
2780
- return Object.values(rows?.[0] || {})?.[0];
2882
+ // } else if(returnType === "value") {
2883
+ // return Object.values(rows?.[0] || {})?.[0];
2781
2884
 
2782
- } else if(returnType === "values") {
2783
- return rows.map(r => Object.values(r[0]));
2885
+ // } else if(returnType === "values") {
2886
+ // return rows.map(r => Object.values(r[0]));
2784
2887
 
2785
- } else {
2786
-
2787
- let qres: SQLResult<typeof returnType> = {
2788
- duration: 0,
2789
- ..._qres,
2790
- fields: fields?.map(f => {
2791
- const dataType = DATA_TYPES.find(dt => +dt.oid === +f.dataTypeID)?.typname ?? "text",
2792
- tableName = USER_TABLES.find(t => +t.relid === +f.tableID),
2793
- tsDataType = postgresToTsType(dataType);
2794
-
2795
- return {
2796
- ...f,
2797
- tsDataType,
2798
- dataType,
2799
- udt_name: dataType,
2800
- tableName: tableName?.relname
2801
- }
2802
- }) ?? []
2803
- };
2804
- return qres;
2805
- }
2888
+ // } else {
2889
+
2890
+ // let qres: SQLResult<typeof returnType> = {
2891
+ // duration: 0,
2892
+ // ..._qres,
2893
+ // fields: fields?.map(f => {
2894
+ // const dataType = DATA_TYPES.find(dt => +dt.oid === +f.dataTypeID)?.typname ?? "text",
2895
+ // tableName = USER_TABLES.find(t => +t.relid === +f.tableID),
2896
+ // tsDataType = postgresToTsType(dataType);
2897
+
2898
+ // return {
2899
+ // ...f,
2900
+ // tsDataType,
2901
+ // dataType,
2902
+ // udt_name: dataType,
2903
+ // tableName: tableName?.relname
2904
+ // }
2905
+ // }) ?? []
2906
+ // };
2907
+ // return qres;
2908
+ // }
2806
2909
 
2807
- } else console.error("db missing");
2808
- }
2910
+ // } else console.error("db missing");
2911
+ // }
2809
2912
  } else {
2810
2913
  console.warn(`Could not create dbo.sql handler because there is already a table named "sql"`)
2811
2914
  }
@@ -2821,13 +2924,19 @@ export class DboBuilder {
2821
2924
 
2822
2925
  getTX = (cb: TxCB) => {
2823
2926
  return this.db.tx((t) => {
2824
- let dbTX: TableHandlers = {};
2927
+ let dbTX: TableHandlers & Pick<DBHandlerServer, "sql"> = {};
2825
2928
  this.tablesOrViews?.map(tov => {
2826
2929
  dbTX[tov.name] = new (tov.is_view? ViewHandler: TableHandler)(this.db, tov, this, t, dbTX, this.joinPaths);
2827
2930
  });
2931
+ if(!dbTX.sql){
2932
+ dbTX.sql = this.runSQL;
2933
+ }
2828
2934
  getKeys(dbTX).map(k => {
2829
2935
  dbTX[k].dbTX = dbTX;
2830
- })
2936
+ });
2937
+
2938
+ dbTX.sql = (q, args, opts, localP) => this.runSQL(q, args, opts, { tx: {dbTX, t }, ...(localP ?? {}) })
2939
+
2831
2940
  return cb(dbTX, t);
2832
2941
  });
2833
2942
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "2.0.249",
3
+ "version": "2.0.250",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1 +1 @@
1
- 18549
1
+ 29151
@@ -640,7 +640,7 @@ export default async function isomorphic(db: Partial<DBHandlerServer> | Partial<
640
640
  }
641
641
  },
642
642
  */
643
-
643
+
644
644
  const json = { a: true, arr: "2", arr1: 3, arr2: [1], arrStr: ["1123.string"] }
645
645
  const fo = await db.tjson.insert({ colOneOf: "a", json }, { returning: "*"});
646
646
  // assert.deepStrictEqual(fo.json, json);
@@ -313,7 +313,15 @@ function dd() {
313
313
  row.name = "b";
314
314
  return row;
315
315
  },
316
- postValidate: async (row, dbo) => {
316
+ postValidate: async (row, dboTx) => {
317
+ /** Records must exist in this transaction */
318
+ log(JSON.stringify(row));
319
+ const exists = await dboTx.sql("SELECT * FROM insert_rules WHERE id = ${id}", row, { returnType: "row" });
320
+ const existsd = await dboTx.insert_rules.findOne({ id: row.id });
321
+ if (row.id !== exists.id || row.id !== existsd.id) {
322
+ console.error("postValidate failed");
323
+ // process.exit(1)
324
+ }
317
325
  if (row.name === "fail")
318
326
  throw "Failed";
319
327
  return undefined;
@@ -341,7 +341,15 @@ function dd(){
341
341
  if(row.name === "a") row.name = "b"
342
342
  return row
343
343
  },
344
- postValidate: async (row, dbo) => {
344
+ postValidate: async (row, dboTx) => {
345
+ /** Records must exist in this transaction */
346
+ log(JSON.stringify(row))
347
+ const exists = await dboTx.sql("SELECT * FROM insert_rules WHERE id = ${id}", row, { returnType: "row" });
348
+ const existsd = await dboTx.insert_rules.findOne({ id: row.id });
349
+ if(row.id !== exists.id || row.id !== existsd.id){
350
+ console.error("postValidate failed");
351
+ // process.exit(1)
352
+ }
345
353
  if(row.name === "fail") throw "Failed";
346
354
  return undefined
347
355
  }
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "../..": {
23
23
  "name": "prostgles-server",
24
- "version": "2.0.248",
24
+ "version": "2.0.249",
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@aws-sdk/client-s3": "^3.121.0",