@rebasepro/server-postgres 0.12.1-canary.gf4240e3 → 0.13.0
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/dist/{ensure-collection-policies-BrUVgjz3.js → ensure-collection-policies-ViG8XiPn.js} +2 -2
- package/dist/{ensure-collection-policies-BrUVgjz3.js.map → ensure-collection-policies-ViG8XiPn.js.map} +1 -1
- package/dist/{ensure-collection-tables-Da2oGkX2.js → ensure-collection-tables-CBQdOETu.js} +2 -2
- package/dist/{ensure-collection-tables-Da2oGkX2.js.map → ensure-collection-tables-CBQdOETu.js.map} +1 -1
- package/dist/index.es.js +310 -247
- package/dist/index.es.js.map +1 -1
- package/dist/services/collection-helpers.d.ts +12 -1
- package/dist/{src-CzbghKwf.js → src-DlPBctw_.js} +30 -2
- package/dist/src-DlPBctw_.js.map +1 -0
- package/dist/utils/pg-error-utils.d.ts +16 -0
- package/package.json +6 -6
- package/src/schema/generate-drizzle-schema-logic.ts +19 -0
- package/src/services/BranchService.ts +66 -28
- package/src/services/FetchService.ts +14 -0
- package/src/services/collection-helpers.ts +29 -2
- package/src/utils/pg-error-utils.ts +19 -0
- package/dist/src-CzbghKwf.js.map +0 -1
package/dist/index.es.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from "module";
|
|
|
2
2
|
import process from "process";
|
|
3
3
|
__createRequire(import.meta.url);
|
|
4
4
|
import { a as guardPoolAgainstDirtyRelease, i as createReadReplicaConnection, n as createDirectDatabaseConnection, o as pinSearchPath, r as createPostgresDatabaseConnection } from "./connection-BuZ97wsr.js";
|
|
5
|
-
import {
|
|
5
|
+
import { A as toSnakeCase, C as normalizeToEntityRelation, D as getPolicyNamesForRule, F as Vector, N as hasForeignKeyOnTarget, O as mergeDeep, P as isManyToMany, S as createRelationRefWithData, T as generateForeignKeyName, _ as buildCompositeId, a as getJunctionSecurityRules, b as parseIdValues, c as policyToPostgres, d as findRelation, f as getColumnName, g as resolveCollectionRelations, h as getTableVarName, i as getJunctionCollectionConfig, k as camelCase, l as securityRuleToConditions, m as getTableName$1, n as CollectionRegistry, o as resolveJunctionSpecs, p as getEnumVarName, r as resolveStringColumnLength, s as getEffectiveSecurityRules, t as buildSdkData, u as findAnonymousGrants, v as getDeclaredPrimaryKeys, w as updateDateAutoValues, x as createRelationRef, y as isAddressableId } from "./src-DlPBctw_.js";
|
|
6
6
|
import { n as isPostgresCollectionConfig, r as isRelationalCollectionConfig } from "./src-DoU9yPqq.js";
|
|
7
7
|
import { t as ANONYMOUS_USER_ID } from "./policy-CeA1JcxP.js";
|
|
8
8
|
import { t as createPostgresWebSocket } from "./websocket-B2LsrINK.js";
|
|
@@ -228,6 +228,27 @@ function getColumnMeta(col) {
|
|
|
228
228
|
primary: typeof raw.primary === "boolean" ? raw.primary : void 0
|
|
229
229
|
};
|
|
230
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Whether an address could name a row in this table, judged by the columns.
|
|
233
|
+
*
|
|
234
|
+
* {@link getPrimaryKeys} lets a config's `isId: "uuid"` win over the schema, so
|
|
235
|
+
* its `isUUID` is a claim rather than a fact — right for deriving addresses,
|
|
236
|
+
* wrong for refusing a query. Here the Drizzle column type decides, because it
|
|
237
|
+
* is what Postgres will enforce: a `uuid` column meets `/c/products/new` with
|
|
238
|
+
* `22P02`, which aborts the surrounding transaction and turns every later
|
|
239
|
+
* statement into an unrelated-looking `25P02`.
|
|
240
|
+
*/
|
|
241
|
+
function idCanAddressTable(id, table, idInfoArray) {
|
|
242
|
+
return isAddressableId(id, idInfoArray.map((info) => {
|
|
243
|
+
const col = table[info.fieldName];
|
|
244
|
+
const meta = col ? getColumnMeta(col) : void 0;
|
|
245
|
+
if (!meta?.columnType) return info;
|
|
246
|
+
return {
|
|
247
|
+
...info,
|
|
248
|
+
isUUID: meta.columnType === "PgUUID"
|
|
249
|
+
};
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
231
252
|
function getCollectionByPath(collectionPath, registry) {
|
|
232
253
|
const collection = registry.getCollectionByPath(collectionPath);
|
|
233
254
|
if (!collection) {
|
|
@@ -2772,6 +2793,247 @@ function assertWritableThrough(hop, path) {
|
|
|
2772
2793
|
throw ApiError.badRequest(`"${path}" ends in the to-one relation '${hop.relationKey}', which cannot be written through: the foreign key for a to-one relation lives on '${hop.parentCollection.slug}', not on '${hop.targetCollection.slug}'. Write the target row at "${hop.targetCollection.slug}" and set '${hop.relationKey}' on the parent instead.`, "RELATION_NOT_WRITABLE");
|
|
2773
2794
|
}
|
|
2774
2795
|
//#endregion
|
|
2796
|
+
//#region src/utils/pg-error-utils.ts
|
|
2797
|
+
/**
|
|
2798
|
+
* Shared PostgreSQL error extraction and user-friendly message formatting.
|
|
2799
|
+
*
|
|
2800
|
+
* Drizzle wraps native PG errors in a `.cause` chain. These utilities
|
|
2801
|
+
* unwrap that chain to get the real PostgreSQL error (identified by a
|
|
2802
|
+
* 5-character alphanumeric `code` such as `42P01`) and translate it into
|
|
2803
|
+
* a message that is safe and helpful to show to end-users.
|
|
2804
|
+
*/
|
|
2805
|
+
/**
|
|
2806
|
+
* Return the error when it is a deliberate 4xx, otherwise null.
|
|
2807
|
+
*
|
|
2808
|
+
* A thrown `ApiError` is a decision the server made about the request, not a
|
|
2809
|
+
* database failure: its message and code are already written for the client.
|
|
2810
|
+
*/
|
|
2811
|
+
function asClientFacingError(error) {
|
|
2812
|
+
if (!(error instanceof Error)) return null;
|
|
2813
|
+
const e = error;
|
|
2814
|
+
if (typeof e.statusCode !== "number" || e.statusCode < 400 || e.statusCode >= 500) return null;
|
|
2815
|
+
return e;
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Extract the underlying PostgreSQL error from a Drizzle wrapper.
|
|
2819
|
+
* Drizzle wraps PG errors in a `cause` property — this function
|
|
2820
|
+
* recursively walks the chain until it finds an object with a PG
|
|
2821
|
+
* error code (5-char alphanumeric, e.g. `42P01`).
|
|
2822
|
+
*/
|
|
2823
|
+
function extractPgError(error) {
|
|
2824
|
+
if (!error || typeof error !== "object") return null;
|
|
2825
|
+
if (!(error instanceof Error)) {
|
|
2826
|
+
if ("cause" in error && error.cause && typeof error.cause === "object") return extractPgError(error.cause);
|
|
2827
|
+
return null;
|
|
2828
|
+
}
|
|
2829
|
+
if ("code" in error && typeof error.code === "string" && /^[0-9A-Z]{5}$/.test(error.code)) return error;
|
|
2830
|
+
if (error.cause && typeof error.cause === "object") return extractPgError(error.cause);
|
|
2831
|
+
return null;
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Whether the failure came back from Postgres rather than from building the
|
|
2835
|
+
* query — which decides whether a fallback query is worth issuing.
|
|
2836
|
+
*
|
|
2837
|
+
* Reads here run inside a transaction (that is where `SET LOCAL ROLE` binds
|
|
2838
|
+
* RLS). Once a statement raises, that transaction is aborted, and every later
|
|
2839
|
+
* statement on it returns `25P02` — "current transaction is aborted, commands
|
|
2840
|
+
* ignored until end of transaction block". So a retry after a database error
|
|
2841
|
+
* cannot succeed, and it replaces a precise diagnosis ("invalid input syntax
|
|
2842
|
+
* for type uuid") with a generic one. Rethrow instead.
|
|
2843
|
+
*
|
|
2844
|
+
* A query the driver could not even build — a missing reciprocal relation, say
|
|
2845
|
+
* — never reached Postgres, leaves the transaction usable, and is exactly what
|
|
2846
|
+
* the fallback paths exist for.
|
|
2847
|
+
*/
|
|
2848
|
+
function reachedDatabase(error) {
|
|
2849
|
+
return extractPgError(error) !== null;
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Walk the error cause chain and return the deepest meaningful message.
|
|
2853
|
+
*/
|
|
2854
|
+
function extractCauseMessage(error) {
|
|
2855
|
+
if (!error || typeof error !== "object") return null;
|
|
2856
|
+
if (!(error instanceof Error)) return null;
|
|
2857
|
+
if (error.cause && typeof error.cause === "object") {
|
|
2858
|
+
const deeper = extractCauseMessage(error.cause);
|
|
2859
|
+
if (deeper) return deeper;
|
|
2860
|
+
if (error.cause instanceof Error && error.cause.message) return error.cause.message;
|
|
2861
|
+
}
|
|
2862
|
+
return null;
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Codes that mean "this connection will never work as configured".
|
|
2866
|
+
*
|
|
2867
|
+
* A wrong password or a database that does not exist is a settled fact about
|
|
2868
|
+
* the connection string, not a transient fault — retrying produces the same
|
|
2869
|
+
* answer forever.
|
|
2870
|
+
*/
|
|
2871
|
+
var UNRECOVERABLE_CONNECT_CODES = /* @__PURE__ */ new Set([
|
|
2872
|
+
"28P01",
|
|
2873
|
+
"28000",
|
|
2874
|
+
"3D000",
|
|
2875
|
+
"42501"
|
|
2876
|
+
]);
|
|
2877
|
+
/**
|
|
2878
|
+
* Describe a failed connection attempt in terms a developer can act on.
|
|
2879
|
+
*
|
|
2880
|
+
* The error a caller catches is Drizzle's wrapper: its message is
|
|
2881
|
+
* `Failed query: SELECT 1` and its stack runs through drizzle internals, while
|
|
2882
|
+
* the sentence that says what is actually wrong — "password authentication
|
|
2883
|
+
* failed for user …", "database … does not exist" — sits in `.cause`. Logging
|
|
2884
|
+
* the wrapper, as the bootstrapper used to, tells a developer with a typo in
|
|
2885
|
+
* their `DATABASE_URL` nothing at all.
|
|
2886
|
+
*/
|
|
2887
|
+
function classifyConnectFailure(error) {
|
|
2888
|
+
const pgError = extractPgError(error);
|
|
2889
|
+
const reason = pgError?.message ?? extractCauseMessage(error) ?? (error instanceof Error ? error.message : String(error));
|
|
2890
|
+
return {
|
|
2891
|
+
fatal: Boolean(pgError?.code && UNRECOVERABLE_CONNECT_CODES.has(pgError.code)),
|
|
2892
|
+
reason,
|
|
2893
|
+
code: pgError?.code
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Detect whether an error is specifically a role-switching permission failure
|
|
2898
|
+
* (e.g. "permission denied to set role" or "must be member of role"),
|
|
2899
|
+
* as opposed to a table-level permission denial.
|
|
2900
|
+
*
|
|
2901
|
+
* This is used by the backend driver to auto-disable role switching when the
|
|
2902
|
+
* connection user lacks SET ROLE privileges, rather than surfacing a confusing
|
|
2903
|
+
* error to the Studio SQL Editor user.
|
|
2904
|
+
*/
|
|
2905
|
+
function isRoleSwitchingPermissionError(error) {
|
|
2906
|
+
const pgError = extractPgError(error);
|
|
2907
|
+
if (!pgError || pgError.code !== "42501") return false;
|
|
2908
|
+
const msg = pgError.message.toLowerCase();
|
|
2909
|
+
return msg.includes("set role") || msg.includes("member of role");
|
|
2910
|
+
}
|
|
2911
|
+
/**
|
|
2912
|
+
* Translate a raw PostgreSQL error into a user-friendly message.
|
|
2913
|
+
*
|
|
2914
|
+
* @param pgError - The extracted PostgreSQL error (from {@link extractPgError})
|
|
2915
|
+
* @param context - A human-readable context string (e.g. collection slug or path)
|
|
2916
|
+
* @returns An object with a `message` safe for the client and the PG `code`.
|
|
2917
|
+
*/
|
|
2918
|
+
function pgErrorToFriendlyMessage(pgError, context) {
|
|
2919
|
+
const detail = pgError.detail;
|
|
2920
|
+
const hint = pgError.hint;
|
|
2921
|
+
const constraint = pgError.constraint;
|
|
2922
|
+
const column = pgError.column;
|
|
2923
|
+
const table = pgError.table;
|
|
2924
|
+
const dataType = pgError.dataType;
|
|
2925
|
+
const pgMessage = pgError.message || "Unknown database error";
|
|
2926
|
+
const code = pgError.code || "UNKNOWN";
|
|
2927
|
+
const suffix = hint ? ` Hint: ${hint}` : "";
|
|
2928
|
+
const tableRef = table ?? context;
|
|
2929
|
+
switch (pgError.code) {
|
|
2930
|
+
case "23503": return {
|
|
2931
|
+
message: detail ? `Foreign key constraint violated: ${detail}${suffix}` : `Cannot complete operation: a foreign key constraint${constraint ? ` (${constraint})` : ""} was violated in "${context}".${suffix}`,
|
|
2932
|
+
code
|
|
2933
|
+
};
|
|
2934
|
+
case "23505": return {
|
|
2935
|
+
message: detail ? `Duplicate value: ${detail}${suffix}` : `Cannot complete operation: a unique constraint${constraint ? ` (${constraint})` : ""} was violated in "${context}".${suffix}`,
|
|
2936
|
+
code
|
|
2937
|
+
};
|
|
2938
|
+
case "23502": return {
|
|
2939
|
+
message: `Missing required field: "${column ?? "unknown"}" in "${tableRef}" cannot be empty.${suffix}`,
|
|
2940
|
+
code
|
|
2941
|
+
};
|
|
2942
|
+
case "23514": return {
|
|
2943
|
+
message: `Validation failed: a check constraint${constraint ? ` (${constraint})` : ""} was violated in "${context}".${suffix}`,
|
|
2944
|
+
code
|
|
2945
|
+
};
|
|
2946
|
+
case "22P02": return {
|
|
2947
|
+
message: `Invalid data format in "${context}": ${pgMessage}${suffix}`,
|
|
2948
|
+
code
|
|
2949
|
+
};
|
|
2950
|
+
case "22001": return {
|
|
2951
|
+
message: `Value too long for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`,
|
|
2952
|
+
code
|
|
2953
|
+
};
|
|
2954
|
+
case "22003": return {
|
|
2955
|
+
message: `Numeric value out of range for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`,
|
|
2956
|
+
code
|
|
2957
|
+
};
|
|
2958
|
+
case "42703": return {
|
|
2959
|
+
message: `Unknown column in "${tableRef}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`,
|
|
2960
|
+
code
|
|
2961
|
+
};
|
|
2962
|
+
case "42P01": return {
|
|
2963
|
+
message: `Table not found for "${context}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`,
|
|
2964
|
+
code
|
|
2965
|
+
};
|
|
2966
|
+
case "42501": return {
|
|
2967
|
+
message: `Permission denied on "${tableRef}". Check your database credentials and RLS policies.${suffix}`,
|
|
2968
|
+
code
|
|
2969
|
+
};
|
|
2970
|
+
case "28000": return {
|
|
2971
|
+
message: `Authorization failed for "${context}". Check your database credentials.${suffix}`,
|
|
2972
|
+
code
|
|
2973
|
+
};
|
|
2974
|
+
default: {
|
|
2975
|
+
const parts = [`Database error in "${context}" [${code}]: ${pgMessage}`];
|
|
2976
|
+
if (detail) parts.push(`Detail: ${detail}`);
|
|
2977
|
+
if (column) parts.push(`Column: ${column}`);
|
|
2978
|
+
if (dataType) parts.push(`Data type: ${dataType}`);
|
|
2979
|
+
if (constraint) parts.push(`Constraint: ${constraint}`);
|
|
2980
|
+
if (hint) parts.push(`Hint: ${hint}`);
|
|
2981
|
+
return {
|
|
2982
|
+
message: parts.join(". "),
|
|
2983
|
+
code
|
|
2984
|
+
};
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Sanitize any error into a message safe and helpful for the client.
|
|
2990
|
+
*
|
|
2991
|
+
* A deliberate 4xx (`ApiError`) passes through untouched — the server already
|
|
2992
|
+
* decided what the client should read. Otherwise the PG error is extracted
|
|
2993
|
+
* from the Drizzle cause chain, falling back to a generic message that
|
|
2994
|
+
* doesn't leak SQL.
|
|
2995
|
+
*
|
|
2996
|
+
* @param error - The raw caught error
|
|
2997
|
+
* @param context - A human-readable context string (e.g. collection path)
|
|
2998
|
+
* @returns An object with `message` (user-friendly) and optional `code`
|
|
2999
|
+
* (the `ApiError` code, or the PG SQLSTATE).
|
|
3000
|
+
*/
|
|
3001
|
+
function sanitizeErrorForClient(error, context) {
|
|
3002
|
+
const clientError = asClientFacingError(error);
|
|
3003
|
+
if (clientError) {
|
|
3004
|
+
const line = `[API ${clientError.statusCode} ${clientError.code ?? "BAD_REQUEST"}] in "${context}": ${clientError.message}`;
|
|
3005
|
+
if (clientError.expected) logger.debug(line);
|
|
3006
|
+
else logger.warn(`⚠️ ${line}`);
|
|
3007
|
+
return {
|
|
3008
|
+
message: clientError.message,
|
|
3009
|
+
...clientError.code && { code: clientError.code }
|
|
3010
|
+
};
|
|
3011
|
+
}
|
|
3012
|
+
const pgError = extractPgError(error);
|
|
3013
|
+
if (pgError) {
|
|
3014
|
+
logger.error(`[PG ${pgError.code}] Error in "${context}"`, {
|
|
3015
|
+
code: pgError.code,
|
|
3016
|
+
message: pgError.message,
|
|
3017
|
+
detail: pgError.detail,
|
|
3018
|
+
hint: pgError.hint,
|
|
3019
|
+
column: pgError.column,
|
|
3020
|
+
table: pgError.table,
|
|
3021
|
+
constraint: pgError.constraint,
|
|
3022
|
+
dataType: pgError.dataType,
|
|
3023
|
+
drizzleMessage: error instanceof Error ? error.message : String(error)
|
|
3024
|
+
});
|
|
3025
|
+
return pgErrorToFriendlyMessage(pgError, context);
|
|
3026
|
+
}
|
|
3027
|
+
logger.error(`Database error in "${context}" (no PG error extracted)`, {
|
|
3028
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3029
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
3030
|
+
cause: error instanceof Error && error.cause ? error.cause instanceof Error ? error.cause.message : String(error.cause) : void 0
|
|
3031
|
+
});
|
|
3032
|
+
const causeMessage = extractCauseMessage(error);
|
|
3033
|
+
if (causeMessage) return { message: `Database error in "${context}": ${causeMessage}` };
|
|
3034
|
+
return { message: `Could not load data for "${context}". Check server logs for details.` };
|
|
3035
|
+
}
|
|
3036
|
+
//#endregion
|
|
2775
3037
|
//#region src/services/FetchService.ts
|
|
2776
3038
|
/**
|
|
2777
3039
|
* Service for handling all row read operations.
|
|
@@ -3086,6 +3348,7 @@ var FetchService = class {
|
|
|
3086
3348
|
const idInfo = idInfoArray[0];
|
|
3087
3349
|
const idField = table[idInfo.fieldName];
|
|
3088
3350
|
if (!idField) throw new Error(`ID field '${idInfo.fieldName}' not found in table for collection '${collectionPath}'`);
|
|
3351
|
+
if (!idCanAddressTable(id, table, idInfoArray)) return void 0;
|
|
3089
3352
|
const parsedId = parseIdValues(id, idInfoArray)[idInfo.fieldName];
|
|
3090
3353
|
const tableName = getTableName(table);
|
|
3091
3354
|
const qb = this.getQueryBuilder(tableName);
|
|
@@ -3104,6 +3367,7 @@ var FetchService = class {
|
|
|
3104
3367
|
logger.error(`[FetchService] ResolvedRelation inference error for collection '${collectionPath}': ${e.message}`);
|
|
3105
3368
|
logger.error("Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.");
|
|
3106
3369
|
}
|
|
3370
|
+
if (reachedDatabase(e)) throw e;
|
|
3107
3371
|
logger.warn(`[FetchService] db.query.findFirst failed for ${collectionPath}, falling back to db.select`, { error: e });
|
|
3108
3372
|
}
|
|
3109
3373
|
const result = await this.db.select().from(table).where(eq(idField, parsedId)).limit(1);
|
|
@@ -3157,6 +3421,7 @@ var FetchService = class {
|
|
|
3157
3421
|
logger.error(`[FetchService] ResolvedRelation inference error for collection '${collectionPath}': ${e.message}`);
|
|
3158
3422
|
logger.error("Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.");
|
|
3159
3423
|
}
|
|
3424
|
+
if (reachedDatabase(e)) throw e;
|
|
3160
3425
|
logger.warn(`[FetchService] db.query.findMany failed for ${collectionPath}, falling back to db.select`, { error: e });
|
|
3161
3426
|
}
|
|
3162
3427
|
let vectorMeta;
|
|
@@ -3376,6 +3641,7 @@ var FetchService = class {
|
|
|
3376
3641
|
logger.error(`[FetchService] ResolvedRelation inference error for collection '${collectionPath}': ${e.message}`);
|
|
3377
3642
|
logger.error("Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.");
|
|
3378
3643
|
}
|
|
3644
|
+
if (reachedDatabase(e)) throw e;
|
|
3379
3645
|
logger.warn(`[fetchCollectionForRest] db.query.findMany failed for ${collectionPath}, falling back`, { error: e });
|
|
3380
3646
|
}
|
|
3381
3647
|
const rows = await this.fetchRowsWithConditionsRaw(collectionPath, options);
|
|
@@ -3421,6 +3687,7 @@ var FetchService = class {
|
|
|
3421
3687
|
const idInfoArray = requirePrimaryKeys(collection, this.registry);
|
|
3422
3688
|
const idInfo = idInfoArray[0];
|
|
3423
3689
|
const idField = table[idInfo.fieldName];
|
|
3690
|
+
if (!idCanAddressTable(id, table, idInfoArray)) return null;
|
|
3424
3691
|
const parsedId = parseIdValues(id, idInfoArray)[idInfo.fieldName];
|
|
3425
3692
|
const tableName = getTableName(table);
|
|
3426
3693
|
const qb = this.getQueryBuilder(tableName);
|
|
@@ -3439,6 +3706,7 @@ var FetchService = class {
|
|
|
3439
3706
|
logger.error(`[FetchService] ResolvedRelation inference error for collection '${collectionPath}': ${e.message}`);
|
|
3440
3707
|
logger.error("Hint: This usually means a relation in your drizzle schema is missing a reciprocal 'one()' or 'many()' definition. Run 'rebase schema generate' to fix this.");
|
|
3441
3708
|
}
|
|
3709
|
+
if (reachedDatabase(e)) throw e;
|
|
3442
3710
|
logger.warn(`[fetchOneForRest] db.query.findFirst failed for ${collectionPath}, falling back`, { error: e });
|
|
3443
3711
|
}
|
|
3444
3712
|
const result = await this.db.select().from(table).where(eq(idField, parsedId)).limit(1);
|
|
@@ -3597,229 +3865,6 @@ var FetchService = class {
|
|
|
3597
3865
|
}
|
|
3598
3866
|
};
|
|
3599
3867
|
//#endregion
|
|
3600
|
-
//#region src/utils/pg-error-utils.ts
|
|
3601
|
-
/**
|
|
3602
|
-
* Shared PostgreSQL error extraction and user-friendly message formatting.
|
|
3603
|
-
*
|
|
3604
|
-
* Drizzle wraps native PG errors in a `.cause` chain. These utilities
|
|
3605
|
-
* unwrap that chain to get the real PostgreSQL error (identified by a
|
|
3606
|
-
* 5-character alphanumeric `code` such as `42P01`) and translate it into
|
|
3607
|
-
* a message that is safe and helpful to show to end-users.
|
|
3608
|
-
*/
|
|
3609
|
-
/**
|
|
3610
|
-
* Return the error when it is a deliberate 4xx, otherwise null.
|
|
3611
|
-
*
|
|
3612
|
-
* A thrown `ApiError` is a decision the server made about the request, not a
|
|
3613
|
-
* database failure: its message and code are already written for the client.
|
|
3614
|
-
*/
|
|
3615
|
-
function asClientFacingError(error) {
|
|
3616
|
-
if (!(error instanceof Error)) return null;
|
|
3617
|
-
const e = error;
|
|
3618
|
-
if (typeof e.statusCode !== "number" || e.statusCode < 400 || e.statusCode >= 500) return null;
|
|
3619
|
-
return e;
|
|
3620
|
-
}
|
|
3621
|
-
/**
|
|
3622
|
-
* Extract the underlying PostgreSQL error from a Drizzle wrapper.
|
|
3623
|
-
* Drizzle wraps PG errors in a `cause` property — this function
|
|
3624
|
-
* recursively walks the chain until it finds an object with a PG
|
|
3625
|
-
* error code (5-char alphanumeric, e.g. `42P01`).
|
|
3626
|
-
*/
|
|
3627
|
-
function extractPgError(error) {
|
|
3628
|
-
if (!error || typeof error !== "object") return null;
|
|
3629
|
-
if (!(error instanceof Error)) {
|
|
3630
|
-
if ("cause" in error && error.cause && typeof error.cause === "object") return extractPgError(error.cause);
|
|
3631
|
-
return null;
|
|
3632
|
-
}
|
|
3633
|
-
if ("code" in error && typeof error.code === "string" && /^[0-9A-Z]{5}$/.test(error.code)) return error;
|
|
3634
|
-
if (error.cause && typeof error.cause === "object") return extractPgError(error.cause);
|
|
3635
|
-
return null;
|
|
3636
|
-
}
|
|
3637
|
-
/**
|
|
3638
|
-
* Walk the error cause chain and return the deepest meaningful message.
|
|
3639
|
-
*/
|
|
3640
|
-
function extractCauseMessage(error) {
|
|
3641
|
-
if (!error || typeof error !== "object") return null;
|
|
3642
|
-
if (!(error instanceof Error)) return null;
|
|
3643
|
-
if (error.cause && typeof error.cause === "object") {
|
|
3644
|
-
const deeper = extractCauseMessage(error.cause);
|
|
3645
|
-
if (deeper) return deeper;
|
|
3646
|
-
if (error.cause instanceof Error && error.cause.message) return error.cause.message;
|
|
3647
|
-
}
|
|
3648
|
-
return null;
|
|
3649
|
-
}
|
|
3650
|
-
/**
|
|
3651
|
-
* Codes that mean "this connection will never work as configured".
|
|
3652
|
-
*
|
|
3653
|
-
* A wrong password or a database that does not exist is a settled fact about
|
|
3654
|
-
* the connection string, not a transient fault — retrying produces the same
|
|
3655
|
-
* answer forever.
|
|
3656
|
-
*/
|
|
3657
|
-
var UNRECOVERABLE_CONNECT_CODES = /* @__PURE__ */ new Set([
|
|
3658
|
-
"28P01",
|
|
3659
|
-
"28000",
|
|
3660
|
-
"3D000",
|
|
3661
|
-
"42501"
|
|
3662
|
-
]);
|
|
3663
|
-
/**
|
|
3664
|
-
* Describe a failed connection attempt in terms a developer can act on.
|
|
3665
|
-
*
|
|
3666
|
-
* The error a caller catches is Drizzle's wrapper: its message is
|
|
3667
|
-
* `Failed query: SELECT 1` and its stack runs through drizzle internals, while
|
|
3668
|
-
* the sentence that says what is actually wrong — "password authentication
|
|
3669
|
-
* failed for user …", "database … does not exist" — sits in `.cause`. Logging
|
|
3670
|
-
* the wrapper, as the bootstrapper used to, tells a developer with a typo in
|
|
3671
|
-
* their `DATABASE_URL` nothing at all.
|
|
3672
|
-
*/
|
|
3673
|
-
function classifyConnectFailure(error) {
|
|
3674
|
-
const pgError = extractPgError(error);
|
|
3675
|
-
const reason = pgError?.message ?? extractCauseMessage(error) ?? (error instanceof Error ? error.message : String(error));
|
|
3676
|
-
return {
|
|
3677
|
-
fatal: Boolean(pgError?.code && UNRECOVERABLE_CONNECT_CODES.has(pgError.code)),
|
|
3678
|
-
reason,
|
|
3679
|
-
code: pgError?.code
|
|
3680
|
-
};
|
|
3681
|
-
}
|
|
3682
|
-
/**
|
|
3683
|
-
* Detect whether an error is specifically a role-switching permission failure
|
|
3684
|
-
* (e.g. "permission denied to set role" or "must be member of role"),
|
|
3685
|
-
* as opposed to a table-level permission denial.
|
|
3686
|
-
*
|
|
3687
|
-
* This is used by the backend driver to auto-disable role switching when the
|
|
3688
|
-
* connection user lacks SET ROLE privileges, rather than surfacing a confusing
|
|
3689
|
-
* error to the Studio SQL Editor user.
|
|
3690
|
-
*/
|
|
3691
|
-
function isRoleSwitchingPermissionError(error) {
|
|
3692
|
-
const pgError = extractPgError(error);
|
|
3693
|
-
if (!pgError || pgError.code !== "42501") return false;
|
|
3694
|
-
const msg = pgError.message.toLowerCase();
|
|
3695
|
-
return msg.includes("set role") || msg.includes("member of role");
|
|
3696
|
-
}
|
|
3697
|
-
/**
|
|
3698
|
-
* Translate a raw PostgreSQL error into a user-friendly message.
|
|
3699
|
-
*
|
|
3700
|
-
* @param pgError - The extracted PostgreSQL error (from {@link extractPgError})
|
|
3701
|
-
* @param context - A human-readable context string (e.g. collection slug or path)
|
|
3702
|
-
* @returns An object with a `message` safe for the client and the PG `code`.
|
|
3703
|
-
*/
|
|
3704
|
-
function pgErrorToFriendlyMessage(pgError, context) {
|
|
3705
|
-
const detail = pgError.detail;
|
|
3706
|
-
const hint = pgError.hint;
|
|
3707
|
-
const constraint = pgError.constraint;
|
|
3708
|
-
const column = pgError.column;
|
|
3709
|
-
const table = pgError.table;
|
|
3710
|
-
const dataType = pgError.dataType;
|
|
3711
|
-
const pgMessage = pgError.message || "Unknown database error";
|
|
3712
|
-
const code = pgError.code || "UNKNOWN";
|
|
3713
|
-
const suffix = hint ? ` Hint: ${hint}` : "";
|
|
3714
|
-
const tableRef = table ?? context;
|
|
3715
|
-
switch (pgError.code) {
|
|
3716
|
-
case "23503": return {
|
|
3717
|
-
message: detail ? `Foreign key constraint violated: ${detail}${suffix}` : `Cannot complete operation: a foreign key constraint${constraint ? ` (${constraint})` : ""} was violated in "${context}".${suffix}`,
|
|
3718
|
-
code
|
|
3719
|
-
};
|
|
3720
|
-
case "23505": return {
|
|
3721
|
-
message: detail ? `Duplicate value: ${detail}${suffix}` : `Cannot complete operation: a unique constraint${constraint ? ` (${constraint})` : ""} was violated in "${context}".${suffix}`,
|
|
3722
|
-
code
|
|
3723
|
-
};
|
|
3724
|
-
case "23502": return {
|
|
3725
|
-
message: `Missing required field: "${column ?? "unknown"}" in "${tableRef}" cannot be empty.${suffix}`,
|
|
3726
|
-
code
|
|
3727
|
-
};
|
|
3728
|
-
case "23514": return {
|
|
3729
|
-
message: `Validation failed: a check constraint${constraint ? ` (${constraint})` : ""} was violated in "${context}".${suffix}`,
|
|
3730
|
-
code
|
|
3731
|
-
};
|
|
3732
|
-
case "22P02": return {
|
|
3733
|
-
message: `Invalid data format in "${context}": ${pgMessage}${suffix}`,
|
|
3734
|
-
code
|
|
3735
|
-
};
|
|
3736
|
-
case "22001": return {
|
|
3737
|
-
message: `Value too long for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`,
|
|
3738
|
-
code
|
|
3739
|
-
};
|
|
3740
|
-
case "22003": return {
|
|
3741
|
-
message: `Numeric value out of range for column "${column ?? "unknown"}" in "${tableRef}": ${pgMessage}${suffix}`,
|
|
3742
|
-
code
|
|
3743
|
-
};
|
|
3744
|
-
case "42703": return {
|
|
3745
|
-
message: `Unknown column in "${tableRef}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`,
|
|
3746
|
-
code
|
|
3747
|
-
};
|
|
3748
|
-
case "42P01": return {
|
|
3749
|
-
message: `Table not found for "${context}": ${pgMessage}. Check if your schema is up to date (run migrations).${suffix}`,
|
|
3750
|
-
code
|
|
3751
|
-
};
|
|
3752
|
-
case "42501": return {
|
|
3753
|
-
message: `Permission denied on "${tableRef}". Check your database credentials and RLS policies.${suffix}`,
|
|
3754
|
-
code
|
|
3755
|
-
};
|
|
3756
|
-
case "28000": return {
|
|
3757
|
-
message: `Authorization failed for "${context}". Check your database credentials.${suffix}`,
|
|
3758
|
-
code
|
|
3759
|
-
};
|
|
3760
|
-
default: {
|
|
3761
|
-
const parts = [`Database error in "${context}" [${code}]: ${pgMessage}`];
|
|
3762
|
-
if (detail) parts.push(`Detail: ${detail}`);
|
|
3763
|
-
if (column) parts.push(`Column: ${column}`);
|
|
3764
|
-
if (dataType) parts.push(`Data type: ${dataType}`);
|
|
3765
|
-
if (constraint) parts.push(`Constraint: ${constraint}`);
|
|
3766
|
-
if (hint) parts.push(`Hint: ${hint}`);
|
|
3767
|
-
return {
|
|
3768
|
-
message: parts.join(". "),
|
|
3769
|
-
code
|
|
3770
|
-
};
|
|
3771
|
-
}
|
|
3772
|
-
}
|
|
3773
|
-
}
|
|
3774
|
-
/**
|
|
3775
|
-
* Sanitize any error into a message safe and helpful for the client.
|
|
3776
|
-
*
|
|
3777
|
-
* A deliberate 4xx (`ApiError`) passes through untouched — the server already
|
|
3778
|
-
* decided what the client should read. Otherwise the PG error is extracted
|
|
3779
|
-
* from the Drizzle cause chain, falling back to a generic message that
|
|
3780
|
-
* doesn't leak SQL.
|
|
3781
|
-
*
|
|
3782
|
-
* @param error - The raw caught error
|
|
3783
|
-
* @param context - A human-readable context string (e.g. collection path)
|
|
3784
|
-
* @returns An object with `message` (user-friendly) and optional `code`
|
|
3785
|
-
* (the `ApiError` code, or the PG SQLSTATE).
|
|
3786
|
-
*/
|
|
3787
|
-
function sanitizeErrorForClient(error, context) {
|
|
3788
|
-
const clientError = asClientFacingError(error);
|
|
3789
|
-
if (clientError) {
|
|
3790
|
-
const line = `[API ${clientError.statusCode} ${clientError.code ?? "BAD_REQUEST"}] in "${context}": ${clientError.message}`;
|
|
3791
|
-
if (clientError.expected) logger.debug(line);
|
|
3792
|
-
else logger.warn(`⚠️ ${line}`);
|
|
3793
|
-
return {
|
|
3794
|
-
message: clientError.message,
|
|
3795
|
-
...clientError.code && { code: clientError.code }
|
|
3796
|
-
};
|
|
3797
|
-
}
|
|
3798
|
-
const pgError = extractPgError(error);
|
|
3799
|
-
if (pgError) {
|
|
3800
|
-
logger.error(`[PG ${pgError.code}] Error in "${context}"`, {
|
|
3801
|
-
code: pgError.code,
|
|
3802
|
-
message: pgError.message,
|
|
3803
|
-
detail: pgError.detail,
|
|
3804
|
-
hint: pgError.hint,
|
|
3805
|
-
column: pgError.column,
|
|
3806
|
-
table: pgError.table,
|
|
3807
|
-
constraint: pgError.constraint,
|
|
3808
|
-
dataType: pgError.dataType,
|
|
3809
|
-
drizzleMessage: error instanceof Error ? error.message : String(error)
|
|
3810
|
-
});
|
|
3811
|
-
return pgErrorToFriendlyMessage(pgError, context);
|
|
3812
|
-
}
|
|
3813
|
-
logger.error(`Database error in "${context}" (no PG error extracted)`, {
|
|
3814
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3815
|
-
stack: error instanceof Error ? error.stack : void 0,
|
|
3816
|
-
cause: error instanceof Error && error.cause ? error.cause instanceof Error ? error.cause.message : String(error.cause) : void 0
|
|
3817
|
-
});
|
|
3818
|
-
const causeMessage = extractCauseMessage(error);
|
|
3819
|
-
if (causeMessage) return { message: `Database error in "${context}": ${causeMessage}` };
|
|
3820
|
-
return { message: `Could not load data for "${context}". Check server logs for details.` };
|
|
3821
|
-
}
|
|
3822
|
-
//#endregion
|
|
3823
3868
|
//#region src/services/PersistService.ts
|
|
3824
3869
|
/**
|
|
3825
3870
|
* Service for handling all row write operations.
|
|
@@ -4220,19 +4265,34 @@ function validateIdentifier(value, label) {
|
|
|
4220
4265
|
if (!/^[a-zA-Z0-9_-]+$/.test(value)) throw new Error(`Invalid ${label}: only letters, digits, underscores, and hyphens are allowed.`);
|
|
4221
4266
|
}
|
|
4222
4267
|
/**
|
|
4223
|
-
*
|
|
4224
|
-
*
|
|
4268
|
+
* Postgres truncates identifiers at NAMEDATALEN-1 = 63 bytes, and does it
|
|
4269
|
+
* silently. The prefix comes out of the same budget.
|
|
4225
4270
|
*/
|
|
4226
|
-
|
|
4227
|
-
|
|
4271
|
+
var MAX_BRANCH_NAME_LENGTH = 60;
|
|
4272
|
+
/**
|
|
4273
|
+
* Check a user-provided branch name, and otherwise leave it exactly as given.
|
|
4274
|
+
*
|
|
4275
|
+
* This used to strip everything outside [a-zA-Z0-9_], so `my-feature` was
|
|
4276
|
+
* quietly created as `myfeature`: the name you typed was not the name `list`
|
|
4277
|
+
* gave back. Nothing ever needed that. Every identifier this service builds is
|
|
4278
|
+
* double-quoted (see `CREATE DATABASE` below), which is what makes a hyphen
|
|
4279
|
+
* safe, and `validateIdentifier` has always accepted hyphens for the `--from`
|
|
4280
|
+
* source database — the two disagreed about the same character class.
|
|
4281
|
+
*
|
|
4282
|
+
* Refusing a name we cannot represent is better than representing a different
|
|
4283
|
+
* one, which is also why the length is checked here rather than left to
|
|
4284
|
+
* Postgres, whose answer to an over-long identifier is a silent rename.
|
|
4285
|
+
*/
|
|
4286
|
+
function assertValidBranchName(name) {
|
|
4287
|
+
validateIdentifier(name, "branch name");
|
|
4288
|
+
if (name.length > MAX_BRANCH_NAME_LENGTH) throw new Error(`Branch name "${name}" is too long: ${name.length} characters, maximum ${MAX_BRANCH_NAME_LENGTH}. Postgres truncates identifiers past 63 bytes, which would give the branch a name you did not choose.`);
|
|
4228
4289
|
}
|
|
4229
4290
|
/**
|
|
4230
4291
|
* Convert a user-facing branch name to the actual PostgreSQL database name.
|
|
4231
4292
|
*/
|
|
4232
4293
|
function toBranchDbName(name) {
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
return `${BRANCH_DB_PREFIX}${sanitized}`;
|
|
4294
|
+
assertValidBranchName(name);
|
|
4295
|
+
return `${BRANCH_DB_PREFIX}${name}`;
|
|
4236
4296
|
}
|
|
4237
4297
|
var BranchService = class {
|
|
4238
4298
|
db;
|
|
@@ -4269,9 +4329,8 @@ var BranchService = class {
|
|
|
4269
4329
|
async createBranch(name, options) {
|
|
4270
4330
|
if (options?.source) validateIdentifier(options.source, "source database name");
|
|
4271
4331
|
const dbName = toBranchDbName(name);
|
|
4272
|
-
const sanitizedName = sanitizeBranchName(name);
|
|
4273
4332
|
const sourceDb = options?.source || this.poolManager.defaultDatabaseName;
|
|
4274
|
-
if ((await this.db.execute(sql`SELECT name FROM rebase.branches WHERE name = ${
|
|
4333
|
+
if ((await this.db.execute(sql`SELECT name FROM rebase.branches WHERE name = ${name} OR db_name = ${dbName}`)).rows.length > 0) throw new Error(`Branch "${name}" already exists.`);
|
|
4275
4334
|
await this.poolManager.disconnectDatabase(sourceDb);
|
|
4276
4335
|
const safeDbName = dbName.replace(/"/g, "\"\"");
|
|
4277
4336
|
const safeSourceDb = sourceDb.replace(/"/g, "\"\"");
|
|
@@ -4282,10 +4341,10 @@ var BranchService = class {
|
|
|
4282
4341
|
throw describeBranchDdlError(err, dbName);
|
|
4283
4342
|
}
|
|
4284
4343
|
const now = /* @__PURE__ */ new Date();
|
|
4285
|
-
await this.db.execute(sql`INSERT INTO rebase.branches (name, db_name, parent_db, created_at)
|
|
4286
|
-
VALUES (${
|
|
4344
|
+
await this.db.execute(sql`INSERT INTO rebase.branches (name, db_name, parent_db, created_at)
|
|
4345
|
+
VALUES (${name}, ${dbName}, ${sourceDb}, ${now.toISOString()})`);
|
|
4287
4346
|
return {
|
|
4288
|
-
name
|
|
4347
|
+
name,
|
|
4289
4348
|
parentDatabase: sourceDb,
|
|
4290
4349
|
createdAt: now
|
|
4291
4350
|
};
|
|
@@ -4295,19 +4354,21 @@ var BranchService = class {
|
|
|
4295
4354
|
* Cannot delete the main/default database.
|
|
4296
4355
|
*/
|
|
4297
4356
|
async deleteBranch(name) {
|
|
4298
|
-
|
|
4299
|
-
|
|
4357
|
+
assertValidBranchName(name);
|
|
4358
|
+
if (toBranchDbName(name) === this.poolManager.defaultDatabaseName) throw new Error("Cannot delete the main database.");
|
|
4359
|
+
const existingRows = (await this.db.execute(sql`SELECT db_name FROM rebase.branches WHERE name = ${name}`)).rows;
|
|
4360
|
+
if (existingRows.length === 0) throw new Error(`Branch "${name}" not found.`);
|
|
4361
|
+
const dbName = existingRows[0].db_name;
|
|
4300
4362
|
if (dbName === this.poolManager.defaultDatabaseName) throw new Error("Cannot delete the main database.");
|
|
4301
|
-
if ((await this.db.execute(sql`SELECT db_name FROM rebase.branches WHERE name = ${sanitizedName}`)).rows.length === 0) throw new Error(`Branch "${sanitizedName}" not found.`);
|
|
4302
4363
|
await this.poolManager.disconnectDatabase(dbName);
|
|
4303
4364
|
const safeDbName = dbName.replace(/"/g, "\"\"");
|
|
4304
4365
|
try {
|
|
4305
4366
|
await this.db.execute(sql.raw(`DROP DATABASE "${safeDbName}"`));
|
|
4306
4367
|
} catch (err) {
|
|
4307
|
-
if (extractPgError(err)?.code === PG_OBJECT_IN_USE) throw new Error(`Cannot delete branch "${
|
|
4368
|
+
if (extractPgError(err)?.code === PG_OBJECT_IN_USE) throw new Error(`Cannot delete branch "${name}": the database has active connections. Close other clients and try again.`);
|
|
4308
4369
|
throw describeBranchDdlError(err, dbName);
|
|
4309
4370
|
}
|
|
4310
|
-
await this.db.execute(sql`DELETE FROM rebase.branches WHERE name = ${
|
|
4371
|
+
await this.db.execute(sql`DELETE FROM rebase.branches WHERE name = ${name}`);
|
|
4311
4372
|
}
|
|
4312
4373
|
/**
|
|
4313
4374
|
* List all branches recorded in the metadata table.
|
|
@@ -4334,20 +4395,21 @@ var BranchService = class {
|
|
|
4334
4395
|
* Get info about a specific branch.
|
|
4335
4396
|
*/
|
|
4336
4397
|
async getBranchInfo(name) {
|
|
4337
|
-
|
|
4398
|
+
assertValidBranchName(name);
|
|
4338
4399
|
const rows = (await this.db.execute(sql`
|
|
4339
|
-
SELECT
|
|
4400
|
+
SELECT
|
|
4340
4401
|
b.name,
|
|
4402
|
+
b.db_name,
|
|
4341
4403
|
b.parent_db,
|
|
4342
4404
|
b.created_at
|
|
4343
4405
|
FROM rebase.branches b
|
|
4344
|
-
WHERE b.name = ${
|
|
4406
|
+
WHERE b.name = ${name}
|
|
4345
4407
|
`)).rows;
|
|
4346
4408
|
if (rows.length === 0) return void 0;
|
|
4347
4409
|
const row = rows[0];
|
|
4348
4410
|
let sizeBytes;
|
|
4349
4411
|
try {
|
|
4350
|
-
const dbName =
|
|
4412
|
+
const dbName = row.db_name;
|
|
4351
4413
|
const sizeRows = (await this.db.execute(sql`SELECT pg_database_size(${dbName}) as size_bytes`)).rows;
|
|
4352
4414
|
if (sizeRows.length > 0 && sizeRows[0].size_bytes != null) sizeBytes = Number(sizeRows[0].size_bytes);
|
|
4353
4415
|
} catch {}
|
|
@@ -5946,6 +6008,7 @@ var getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
|
5946
6008
|
const isId = isIdProperty(propName, prop, collection);
|
|
5947
6009
|
let baseType = numProp.validation?.integer || isId ? `integer("${colName}")` : `numeric("${colName}")`;
|
|
5948
6010
|
if (numProp.columnType) if (numProp.columnType === "double precision") baseType = `doublePrecision("${colName}")`;
|
|
6011
|
+
else if (numProp.columnType === "bigint" || numProp.columnType === "bigserial") baseType = `${numProp.columnType}("${colName}", { mode: "number" })`;
|
|
5949
6012
|
else baseType = `${numProp.columnType}("${colName}")`;
|
|
5950
6013
|
if ("isId" in numProp && numProp.isId === "increment") columnDefinition = `${baseType}.generatedByDefaultAsIdentity()`;
|
|
5951
6014
|
else if ("isId" in numProp && typeof numProp.isId === "string" && numProp.isId !== "manual") {
|
|
@@ -12225,7 +12288,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
12225
12288
|
*/
|
|
12226
12289
|
async ensureCollectionSchema(collections, driverResult, log) {
|
|
12227
12290
|
const internals = driverResult.internals;
|
|
12228
|
-
const { ensureCollectionTables } = await import("./ensure-collection-tables-
|
|
12291
|
+
const { ensureCollectionTables } = await import("./ensure-collection-tables-CBQdOETu.js");
|
|
12229
12292
|
const plan = await ensureCollectionTables({ async query(text) {
|
|
12230
12293
|
const result = await internals.db.execute(sql.raw(text));
|
|
12231
12294
|
return { rows: result.rows ?? (Array.isArray(result) ? result : []) };
|
|
@@ -12250,7 +12313,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
12250
12313
|
*/
|
|
12251
12314
|
async ensureCollectionPolicies(collections, driverResult, log) {
|
|
12252
12315
|
const internals = driverResult.internals;
|
|
12253
|
-
const { ensureCollectionPolicies } = await import("./ensure-collection-policies-
|
|
12316
|
+
const { ensureCollectionPolicies } = await import("./ensure-collection-policies-ViG8XiPn.js");
|
|
12254
12317
|
const outcome = await ensureCollectionPolicies({ async query(text) {
|
|
12255
12318
|
const result = await internals.db.execute(sql.raw(text));
|
|
12256
12319
|
return { rows: result.rows ?? (Array.isArray(result) ? result : []) };
|