From 683e37f414d7fe2ee0762de0ccec3119b027f140 Mon Sep 17 00:00:00 2001 From: Schmitt Paul Date: Wed, 18 Oct 2023 21:56:13 +0200 Subject: [PATCH 1/2] fix(postgres): add optional schema name parameter for postgres Default to public. Used to let users define table in schema different from public. --- lib/RateLimiterPostgres.js | 30 ++++++++++++++++++++---------- lib/index.d.ts | 16 ++++++++++------ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/RateLimiterPostgres.js b/lib/RateLimiterPostgres.js index 3e83ea3..82b5555 100644 --- a/lib/RateLimiterPostgres.js +++ b/lib/RateLimiterPostgres.js @@ -14,6 +14,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { * storeClient: postgresClient, * storeType: 'knex', // required only for Knex instance * tableName: 'string', + * schemaName: 'string', // optional * } */ constructor(opts, cb = null) { @@ -23,6 +24,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { this.clientType = opts.storeType; this.tableName = opts.tableName; + this.schemaName = opts.schemaName; this.clearExpiredByTimeout = opts.clearExpiredByTimeout; @@ -56,7 +58,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { return new Promise((resolve) => { const q = { name: 'rlflx-clear-expired', - text: `DELETE FROM "${this.tableName}" WHERE expire < $1`, + text: `DELETE FROM "${this.schemaName}"."${this.tableName}" WHERE expire < $1`, values: [expire], }; this._query(q) @@ -150,7 +152,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { } _getCreateTableStmt() { - return `CREATE TABLE IF NOT EXISTS "${this.tableName}" ( + return `CREATE TABLE IF NOT EXISTS "${this.schemaName}"."${this.tableName}" ( key varchar(255) PRIMARY KEY, points integer NOT NULL DEFAULT 0, expire bigint @@ -190,8 +192,16 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { this._tableName = typeof value === 'undefined' ? this.keyPrefix : value; } + get schemaName() { + return this._schemaName; + } + + set schemaName(value) { + this._schemaName = typeof value === 'undefined' ? 'public' : value; + } + get tableCreated() { - return this._tableCreated + return this._tableCreated; } set tableCreated(value) { @@ -252,18 +262,18 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { const expireQ = forceExpire ? ' $3 ' : ` CASE - WHEN "${this.tableName}".expire <= $4 THEN $3 - ELSE "${this.tableName}".expire + WHEN "${this.schemaName}"."${this.tableName}".expire <= $4 THEN $3 + ELSE "${this.schemaName}"."${this.tableName}".expire END `; return this._query({ name: forceExpire ? 'rlflx-upsert-force' : 'rlflx-upsert', text: ` - INSERT INTO "${this.tableName}" VALUES ($1, $2, $3) + INSERT INTO "${this.schemaName}"."${this.tableName}" VALUES ($1, $2, $3) ON CONFLICT(key) DO UPDATE SET points = CASE - WHEN ("${this.tableName}".expire <= $4 OR 1=${forceExpire ? 1 : 0}) THEN $2 - ELSE "${this.tableName}".points + ($2) + WHEN ("${this.schemaName}"."${this.tableName}".expire <= $4 OR 1=${forceExpire ? 1 : 0}) THEN $2 + ELSE "${this.schemaName}"."${this.tableName}".points + ($2) END, expire = ${expireQ} RETURNING points, expire;`, @@ -280,7 +290,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { this._query({ name: 'rlflx-get', text: ` - SELECT points, expire FROM "${this.tableName}" WHERE key = $1 AND (expire > $2 OR expire IS NULL);`, + SELECT points, expire FROM "${this.schemaName}"."${this.tableName}" WHERE key = $1 AND (expire > $2 OR expire IS NULL);`, values: [rlKey, Date.now()], }) .then((res) => { @@ -302,7 +312,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { return this._query({ name: 'rlflx-delete', - text: `DELETE FROM "${this.tableName}" WHERE key = $1`, + text: `DELETE FROM "${this.schemaName}"."${this.tableName}" WHERE key = $1`, values: [rlKey], }) .then(res => res.rowCount > 0); diff --git a/lib/index.d.ts b/lib/index.d.ts index 6b85ce8..eb59884 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -247,10 +247,14 @@ interface IRateLimiterMongoOptions extends IRateLimiterStoreOptions { }; } +interface IRateLimiterPostgresOptions extends IRateLimiterStoreNoAutoExpiryOptions { + schemaName?: string; +} + interface IRateLimiterRedisOptions extends IRateLimiterStoreOptions { - rejectIfRedisNotReady?: boolean; - useRedisPackage?: boolean; - useRedis3AndLowerPackage?: boolean; + rejectIfRedisNotReady?: boolean; + useRedisPackage?: boolean; + useRedis3AndLowerPackage?: boolean; } interface ICallbackReady { @@ -283,7 +287,7 @@ export class RateLimiterClusterMasterPM2 { } export class RateLimiterRedis extends RateLimiterStoreAbstract { - constructor(opts: IRateLimiterRedisOptions); + constructor(opts: IRateLimiterRedisOptions); } export interface IRateLimiterMongoFunctionOptions { @@ -342,10 +346,10 @@ export class RateLimiterMySQL extends RateLimiterStoreAbstract { } export class RateLimiterPostgres extends RateLimiterStoreAbstract { - constructor(opts: IRateLimiterStoreNoAutoExpiryOptions, cb?: ICallbackReady); + constructor(opts: IRateLimiterPostgresOptions, cb?: ICallbackReady); } -export class RateLimiterMemcache extends RateLimiterStoreAbstract {} +export class RateLimiterMemcache extends RateLimiterStoreAbstract { } export class RateLimiterUnion { constructor(...limiters: RateLimiterAbstract[]); From 750cdb448af0fed51d419b9dbfdd7fba0e39024a Mon Sep 17 00:00:00 2001 From: Schmitt Paul Date: Thu, 19 Oct 2023 16:49:23 +0200 Subject: [PATCH 2/2] fix(postgres): use schema name only if provided --- lib/RateLimiterPostgres.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/RateLimiterPostgres.js b/lib/RateLimiterPostgres.js index 82b5555..48cdd74 100644 --- a/lib/RateLimiterPostgres.js +++ b/lib/RateLimiterPostgres.js @@ -54,11 +54,15 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { } } + _getTableIdentifier() { + return this.schemaName ? `"${this.schemaName}"."${this.tableName}"` : `"${this.tableName}"`; + } + clearExpired(expire) { return new Promise((resolve) => { const q = { name: 'rlflx-clear-expired', - text: `DELETE FROM "${this.schemaName}"."${this.tableName}" WHERE expire < $1`, + text: `DELETE FROM ${this._getTableIdentifier()} WHERE expire < $1`, values: [expire], }; this._query(q) @@ -152,7 +156,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { } _getCreateTableStmt() { - return `CREATE TABLE IF NOT EXISTS "${this.schemaName}"."${this.tableName}" ( + return `CREATE TABLE IF NOT EXISTS ${this._getTableIdentifier()} ( key varchar(255) PRIMARY KEY, points integer NOT NULL DEFAULT 0, expire bigint @@ -197,7 +201,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { } set schemaName(value) { - this._schemaName = typeof value === 'undefined' ? 'public' : value; + this._schemaName = value; } get tableCreated() { @@ -262,18 +266,18 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { const expireQ = forceExpire ? ' $3 ' : ` CASE - WHEN "${this.schemaName}"."${this.tableName}".expire <= $4 THEN $3 - ELSE "${this.schemaName}"."${this.tableName}".expire + WHEN ${this._getTableIdentifier()}.expire <= $4 THEN $3 + ELSE ${this._getTableIdentifier()}.expire END `; return this._query({ name: forceExpire ? 'rlflx-upsert-force' : 'rlflx-upsert', text: ` - INSERT INTO "${this.schemaName}"."${this.tableName}" VALUES ($1, $2, $3) + INSERT INTO ${this._getTableIdentifier()} VALUES ($1, $2, $3) ON CONFLICT(key) DO UPDATE SET points = CASE - WHEN ("${this.schemaName}"."${this.tableName}".expire <= $4 OR 1=${forceExpire ? 1 : 0}) THEN $2 - ELSE "${this.schemaName}"."${this.tableName}".points + ($2) + WHEN (${this._getTableIdentifier()}.expire <= $4 OR 1=${forceExpire ? 1 : 0}) THEN $2 + ELSE ${this._getTableIdentifier()}.points + ($2) END, expire = ${expireQ} RETURNING points, expire;`, @@ -290,7 +294,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { this._query({ name: 'rlflx-get', text: ` - SELECT points, expire FROM "${this.schemaName}"."${this.tableName}" WHERE key = $1 AND (expire > $2 OR expire IS NULL);`, + SELECT points, expire FROM ${this._getTableIdentifier()} WHERE key = $1 AND (expire > $2 OR expire IS NULL);`, values: [rlKey, Date.now()], }) .then((res) => { @@ -312,7 +316,7 @@ class RateLimiterPostgres extends RateLimiterStoreAbstract { return this._query({ name: 'rlflx-delete', - text: `DELETE FROM "${this.schemaName}"."${this.tableName}" WHERE key = $1`, + text: `DELETE FROM ${this._getTableIdentifier()} WHERE key = $1`, values: [rlKey], }) .then(res => res.rowCount > 0);