@memberjunction/sql-dialect 0.0.1 → 5.6.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/README.md CHANGED
@@ -1,45 +1,405 @@
1
1
  # @memberjunction/sql-dialect
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ **Version**: 5.2.0
4
+ **Zero runtime dependencies**
4
5
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
+ ## Overview
6
7
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
+ `@memberjunction/sql-dialect` is an abstract SQL dialect layer that enables database-agnostic SQL generation across MemberJunction. It encapsulates every platform-specific SQL syntax pattern -- identifier quoting, pagination, data types, DDL generation, full-text search, and more -- into a single, testable abstraction with zero database driver dependencies.
8
9
 
9
- ## Purpose
10
+ This package is used by CodeGen, data providers, and SQL converters throughout the MemberJunction monorepo. When code needs to emit SQL that works on both SQL Server and PostgreSQL, it programs against the `SQLDialect` abstract class and lets the concrete dialect handle platform differences.
10
11
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@memberjunction/sql-dialect`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
12
+ ## Architecture
15
13
 
16
- ## What is OIDC Trusted Publishing?
14
+ ### Class Hierarchy
17
15
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
16
+ ```
17
+ SQLDialect (abstract base)
18
+ |-- SQLServerDialect
19
+ |-- PostgreSQLDialect
20
+ ```
19
21
 
20
- ## Setup Instructions
22
+ `SQLDialect` defines approximately 30 abstract methods spanning identifier quoting, pagination, literal expressions, INSERT/UPDATE return patterns, DDL generation, full-text search, data type mapping, and schema introspection. Each concrete dialect implements every method with platform-native SQL.
21
23
 
22
- To properly configure OIDC trusted publishing for this package:
24
+ ### Key Interfaces
23
25
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
26
+ | Interface | Purpose |
27
+ |---|---|
28
+ | `LimitClauseResult` | `{ prefix: string; suffix: string }` -- Flexible pagination fragments. SQL Server uses `prefix` (TOP), PostgreSQL uses `suffix` (LIMIT/OFFSET). |
29
+ | `SchemaIntrospectionSQL` | Catalog query templates for discovering tables, columns, constraints, foreign keys, and indexes. |
30
+ | `TriggerOptions` | Configuration for trigger DDL generation (schema, table, timing, events, body, function name, FOR EACH ROW/STATEMENT). |
31
+ | `IndexOptions` | Configuration for index DDL generation (columns, uniqueness, method, partial WHERE, INCLUDE columns). |
32
+ | `DataTypeMap` | Maps source database types to target platform types. |
33
+ | `MappedType` | Describes a mapped type: `typeName`, `supportsLength`, `supportsPrecisionScale`, `defaultLength`. |
34
+ | `DatabasePlatform` | Union type: `'sqlserver' \| 'postgresql'` |
28
35
 
29
- ## DO NOT USE THIS PACKAGE
36
+ ## Key Methods
30
37
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
38
+ ### Identifier Quoting
36
39
 
37
- ## More Information
40
+ | Method | Description | SQL Server | PostgreSQL |
41
+ |---|---|---|---|
42
+ | `QuoteIdentifier(name)` | Wraps a single identifier | `[name]` | `"name"` |
43
+ | `QuoteSchema(schema, object)` | Schema-qualified reference | `[schema].[object]` | `schema."object"` |
38
44
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
45
+ ### Pagination
42
46
 
43
- ---
47
+ | Method | Description | SQL Server | PostgreSQL |
48
+ |---|---|---|---|
49
+ | `LimitClause(limit, offset?)` | Returns `{ prefix, suffix }` | Without offset: `prefix: 'TOP 10'`. With offset: `suffix: 'OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY'` | `suffix: 'LIMIT 10 OFFSET 20'` |
44
50
 
45
- **Maintained for OIDC setup purposes only**
51
+ ### Literals and Expressions
52
+
53
+ | Method | Description | SQL Server | PostgreSQL |
54
+ |---|---|---|---|
55
+ | `BooleanLiteral(value)` | Platform boolean | `1` / `0` | `true` / `false` |
56
+ | `CurrentTimestampUTC()` | Current UTC time | `GETUTCDATE()` | `(NOW() AT TIME ZONE 'UTC')` |
57
+ | `NewUUID()` | Generate UUID | `NEWID()` | `gen_random_uuid()` |
58
+ | `CastToText(expr)` | Cast to text type | `CAST(expr AS NVARCHAR(MAX))` | `CAST(expr AS TEXT)` |
59
+ | `CastToUUID(expr)` | Cast to UUID type | `CAST(expr AS UNIQUEIDENTIFIER)` | `CAST(expr AS UUID)` |
60
+ | `Coalesce(expr, fallback)` | Null coalescing (concrete) | `COALESCE(expr, fallback)` | `COALESCE(expr, fallback)` |
61
+ | `IsNull(expr, fallback)` | Alias for Coalesce | `COALESCE(expr, fallback)` | `COALESCE(expr, fallback)` |
62
+ | `IIF(condition, trueVal, falseVal)` | Conditional expression | `IIF(cond, t, f)` | `CASE WHEN cond THEN t ELSE f END` |
63
+
64
+ ### INSERT/UPDATE Return Patterns
65
+
66
+ | Method | Description | SQL Server | PostgreSQL |
67
+ |---|---|---|---|
68
+ | `ReturnInsertedClause(columns?)` | Get inserted values back | `OUTPUT INSERTED.*` or `OUTPUT INSERTED.[col]` | `RETURNING *` or `RETURNING "col"` |
69
+ | `AutoIncrementPKExpression()` | Auto-increment DDL | `IDENTITY(1,1)` | `GENERATED ALWAYS AS IDENTITY` |
70
+ | `UUIDPKDefault()` | Default UUID PK expression | `NEWSEQUENTIALID()` | `gen_random_uuid()` |
71
+ | `ScopeIdentityExpression()` | Last inserted identity | `SCOPE_IDENTITY()` | `lastval()` |
72
+ | `RowCountExpression()` | Rows affected | `@@ROWCOUNT` | `ROW_COUNT` (via GET DIAGNOSTICS) |
73
+
74
+ ### DDL Generation
75
+
76
+ | Method | Description |
77
+ |---|---|
78
+ | `TriggerDDL(options: TriggerOptions)` | Full trigger creation DDL. SQL Server emits `CREATE TRIGGER ... AS BEGIN ... END`. PostgreSQL emits a companion `CREATE OR REPLACE FUNCTION` plus `CREATE TRIGGER ... EXECUTE FUNCTION`. |
79
+ | `IndexDDL(options: IndexOptions)` | Index creation DDL. PostgreSQL supports `USING method`, partial `WHERE`, and `IF NOT EXISTS`. SQL Server supports `INCLUDE` columns. |
80
+ | `ExistenceCheckSQL(objectType, schema, name)` | Check if a database object exists. SQL Server uses `OBJECT_ID()`. PostgreSQL uses `pg_catalog` queries. Supports TABLE, VIEW, FUNCTION, PROCEDURE, TRIGGER. |
81
+ | `CreateOrReplaceSupported(objectType)` | Whether `CREATE OR REPLACE` is available. SQL Server: always `false`. PostgreSQL: `true` for FUNCTION, VIEW, PROCEDURE. |
82
+ | `BatchSeparator()` | Statement batch separator. SQL Server: `GO`. PostgreSQL: `""` (empty string). |
83
+
84
+ ### Full-Text Search
85
+
86
+ | Method | Description | SQL Server | PostgreSQL |
87
+ |---|---|---|---|
88
+ | `FullTextSearchPredicate(column, searchTerm)` | Search predicate | `CONTAINS([column], term)` | `column @@ plainto_tsquery('english', term)` |
89
+ | `FullTextIndexDDL(table, columns, catalog?)` | Index creation DDL | Fulltext catalog + fulltext index | tsvector column + GIN index + update trigger |
90
+
91
+ ### String and JSON Functions
92
+
93
+ | Method | Description | SQL Server | PostgreSQL |
94
+ |---|---|---|---|
95
+ | `StringSplitFunction(value, delimiter)` | Split string to rows | `STRING_SPLIT(value, delim)` | `unnest(string_to_array(value, delim))` |
96
+ | `JsonExtract(column, path)` | Extract JSON value | `JSON_VALUE(column, 'path')` | `column->>'path'` |
97
+ | `ConcatOperator()` | String concatenation | `+` | `\|\|` |
98
+
99
+ ### Parameters and Procedure Calls
100
+
101
+ | Method | Description | SQL Server | PostgreSQL |
102
+ |---|---|---|---|
103
+ | `ParameterPlaceholder(index)` | Positional parameter | `@p0`, `@p1`, ... | `$1`, `$2`, ... |
104
+ | `ProcedureCallSyntax(schema, name, params)` | Call stored procedure/function | `EXEC [schema].[name] @p0, @p1` | `SELECT * FROM schema."name"($1, $2)` |
105
+
106
+ ### CTE / Recursion
107
+
108
+ | Method | Description | SQL Server | PostgreSQL |
109
+ |---|---|---|---|
110
+ | `RecursiveCTESyntax()` | Recursive CTE keyword | `WITH` | `WITH RECURSIVE` |
111
+
112
+ ### Permissions and Comments
113
+
114
+ | Method | Description | SQL Server | PostgreSQL |
115
+ |---|---|---|---|
116
+ | `GrantPermission(permission, objectType, schema, object, role)` | Grant access | `GRANT ... ON [s].[o] TO [r]` | `GRANT ... ON s."o" TO "r"` |
117
+ | `CommentOnObject(objectType, schema, name, comment)` | Add description | `EXEC sp_addextendedproperty ...` | `COMMENT ON TYPE s."name" IS '...'` |
118
+
119
+ ### Schema Introspection
120
+
121
+ | Method | Description |
122
+ |---|---|
123
+ | `SchemaIntrospectionQueries()` | Returns a `SchemaIntrospectionSQL` object with platform-specific catalog queries for listing tables, columns, constraints, foreign keys, indexes, and checking object existence. |
124
+
125
+ ### Data Type Mapping
126
+
127
+ | Method | Description |
128
+ |---|---|
129
+ | `get TypeMap(): DataTypeMap` | Returns the dialect-specific type mapper instance. |
130
+ | `MapDataType(sourceType, length?, precision?, scale?)` | Convenience wrapper that calls `TypeMap.MapType()`. Returns a `MappedType`. |
131
+ | `MapDataTypeToString(sourceType, length?, precision?, scale?)` | Convenience wrapper that calls `TypeMap.MapTypeToString()`. Returns a formatted type string like `VARCHAR(255)` or `NUMERIC(10,2)`. |
132
+
133
+ ## DataTypeMap
134
+
135
+ The `DataTypeMap` interface defines how data types are translated between database platforms:
136
+
137
+ ```typescript
138
+ interface MappedType {
139
+ typeName: string; // Target type name (e.g., "UUID", "BOOLEAN")
140
+ supportsLength: boolean; // Whether the type accepts a length parameter
141
+ supportsPrecisionScale: boolean; // Whether the type accepts precision/scale
142
+ defaultLength?: number; // Default length when applicable
143
+ }
144
+
145
+ interface DataTypeMap {
146
+ MapType(sourceType: string, sourceLength?: number,
147
+ sourcePrecision?: number, sourceScale?: number): MappedType;
148
+
149
+ MapTypeToString(sourceType: string, sourceLength?: number,
150
+ sourcePrecision?: number, sourceScale?: number): string;
151
+ }
152
+ ```
153
+
154
+ `SQLServerDataTypeMap` is an identity mapper (SQL Server types map to themselves). `PostgreSQLDataTypeMap` maps SQL Server types to their PostgreSQL equivalents.
155
+
156
+ ### Type Mapping Reference (SQL Server to PostgreSQL)
157
+
158
+ | SQL Server Type | PostgreSQL Type | Notes |
159
+ |---|---|---|
160
+ | `UNIQUEIDENTIFIER` | `UUID` | |
161
+ | `BIT` | `BOOLEAN` | |
162
+ | `NVARCHAR(n)` | `VARCHAR(n)` | |
163
+ | `NVARCHAR(MAX)` | `TEXT` | Length = -1 or unspecified |
164
+ | `VARCHAR(MAX)` | `TEXT` | Length = -1 or unspecified |
165
+ | `NCHAR` / `CHAR` | `CHAR` | Preserves length |
166
+ | `INT` / `INTEGER` | `INTEGER` | |
167
+ | `BIGINT` | `BIGINT` | |
168
+ | `SMALLINT` | `SMALLINT` | |
169
+ | `TINYINT` | `SMALLINT` | No TINYINT in PostgreSQL |
170
+ | `DECIMAL` / `NUMERIC` | `NUMERIC` | Preserves precision/scale |
171
+ | `FLOAT(1-24)` | `REAL` | |
172
+ | `FLOAT(25-53)` | `DOUBLE PRECISION` | |
173
+ | `REAL` | `REAL` | |
174
+ | `MONEY` | `NUMERIC(19,4)` | |
175
+ | `SMALLMONEY` | `NUMERIC(10,4)` | |
176
+ | `DATE` | `DATE` | |
177
+ | `DATETIME` / `DATETIME2` | `TIMESTAMP` | |
178
+ | `DATETIMEOFFSET` | `TIMESTAMPTZ` | |
179
+ | `SMALLDATETIME` | `TIMESTAMP(0)` | |
180
+ | `TIME` | `TIME` | |
181
+ | `TEXT` / `NTEXT` | `TEXT` | |
182
+ | `IMAGE` | `BYTEA` | |
183
+ | `VARBINARY` / `BINARY` | `BYTEA` | |
184
+ | `XML` | `XML` | |
185
+
186
+ PostgreSQL-native types (`UUID`, `BOOLEAN`, `TIMESTAMPTZ`, `JSONB`, `BYTEA`, `SERIAL`, `BIGSERIAL`, `DOUBLE PRECISION`) pass through unchanged.
187
+
188
+ ## Usage Examples
189
+
190
+ ```typescript
191
+ import {
192
+ SQLDialect,
193
+ SQLServerDialect,
194
+ PostgreSQLDialect
195
+ } from '@memberjunction/sql-dialect';
196
+
197
+ // Create a dialect instance
198
+ const dialect: SQLDialect = new PostgreSQLDialect();
199
+
200
+ // Identifier quoting
201
+ dialect.QuoteIdentifier('UserName'); // "UserName"
202
+ dialect.QuoteSchema('__mj', 'User'); // __mj."User"
203
+
204
+ // Pagination
205
+ const { prefix, suffix } = dialect.LimitClause(10, 20);
206
+ // prefix: '', suffix: 'LIMIT 10 OFFSET 20'
207
+
208
+ // Boolean and timestamp literals
209
+ dialect.BooleanLiteral(true); // 'true'
210
+ dialect.CurrentTimestampUTC(); // "(NOW() AT TIME ZONE 'UTC')"
211
+
212
+ // UUID generation
213
+ dialect.NewUUID(); // 'gen_random_uuid()'
214
+
215
+ // Data type mapping
216
+ const mapped = dialect.MapDataType('UNIQUEIDENTIFIER');
217
+ // { typeName: 'UUID', supportsLength: false, supportsPrecisionScale: false }
218
+
219
+ dialect.MapDataTypeToString('NVARCHAR', 255);
220
+ // 'VARCHAR(255)'
221
+
222
+ dialect.MapDataTypeToString('NVARCHAR', -1);
223
+ // 'TEXT'
224
+
225
+ dialect.MapDataTypeToString('DECIMAL', undefined, 10, 2);
226
+ // 'NUMERIC(10,2)'
227
+
228
+ // INSERT return clause
229
+ dialect.ReturnInsertedClause(); // 'RETURNING *'
230
+ dialect.ReturnInsertedClause(['ID', 'Name']);
231
+ // 'RETURNING "ID", "Name"'
232
+
233
+ // Conditional expression
234
+ dialect.IIF('x > 0', "'positive'", "'non-positive'");
235
+ // "CASE WHEN x > 0 THEN 'positive' ELSE 'non-positive' END"
236
+
237
+ // Procedure call
238
+ dialect.ProcedureCallSyntax('__mj', 'spCreateUser', ['$1', '$2']);
239
+ // 'SELECT * FROM __mj."spCreateUser"($1, $2)'
240
+
241
+ // Trigger DDL
242
+ const triggerSQL = dialect.TriggerDDL({
243
+ schema: '__mj',
244
+ tableName: 'User',
245
+ triggerName: 'trgUpdateUser',
246
+ timing: 'BEFORE',
247
+ events: ['UPDATE'],
248
+ body: 'NEW.__mj_UpdatedAt = NOW();',
249
+ functionName: 'fn_update_user_timestamp',
250
+ forEach: 'ROW'
251
+ });
252
+ // Generates:
253
+ // CREATE OR REPLACE FUNCTION __mj."fn_update_user_timestamp"()
254
+ // RETURNS TRIGGER AS $$ BEGIN ... END; $$ LANGUAGE plpgsql;
255
+ // DROP TRIGGER IF EXISTS ... ;
256
+ // CREATE TRIGGER "trgUpdateUser" BEFORE UPDATE ON __mj."User"
257
+ // FOR EACH ROW EXECUTE FUNCTION __mj."fn_update_user_timestamp"();
258
+
259
+ // Index DDL
260
+ const indexSQL = dialect.IndexDDL({
261
+ schema: '__mj',
262
+ tableName: 'User',
263
+ indexName: 'idx_user_email',
264
+ columns: ['Email'],
265
+ unique: true,
266
+ method: 'btree'
267
+ });
268
+ // 'CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_email"
269
+ // ON __mj."User" USING btree("Email")'
270
+ ```
271
+
272
+ ### Polymorphic Usage
273
+
274
+ The key benefit is writing database-agnostic code that works with any dialect:
275
+
276
+ ```typescript
277
+ function buildSelectQuery(dialect: SQLDialect, schema: string, table: string,
278
+ columns: string[], limit: number): string {
279
+ const { prefix, suffix } = dialect.LimitClause(limit);
280
+ const qualifiedTable = dialect.QuoteSchema(schema, table);
281
+ const quotedCols = columns.map(c => dialect.QuoteIdentifier(c)).join(', ');
282
+
283
+ return `SELECT ${prefix} ${quotedCols} FROM ${qualifiedTable} ${suffix}`.trim();
284
+ }
285
+
286
+ // SQL Server output:
287
+ // SELECT TOP 10 [ID], [Name] FROM [__mj].[User]
288
+
289
+ // PostgreSQL output:
290
+ // SELECT "ID", "Name" FROM __mj."User" LIMIT 10
291
+ ```
292
+
293
+ ## Adding a New Dialect
294
+
295
+ To add support for a new database platform (e.g., MySQL):
296
+
297
+ 1. **Create the type map class** implementing `DataTypeMap`:
298
+
299
+ ```typescript
300
+ import { DataTypeMap, MappedType } from '@memberjunction/sql-dialect';
301
+
302
+ class MySQLDataTypeMap implements DataTypeMap {
303
+ MapType(sourceType: string, sourceLength?: number,
304
+ sourcePrecision?: number, sourceScale?: number): MappedType {
305
+ const normalized = sourceType.toUpperCase().trim();
306
+ switch (normalized) {
307
+ case 'UNIQUEIDENTIFIER':
308
+ return { typeName: 'CHAR', supportsLength: true,
309
+ supportsPrecisionScale: false, defaultLength: 36 };
310
+ case 'BIT':
311
+ return { typeName: 'TINYINT(1)', supportsLength: false,
312
+ supportsPrecisionScale: false };
313
+ // ... map remaining types
314
+ default:
315
+ return { typeName: normalized, supportsLength: false,
316
+ supportsPrecisionScale: false };
317
+ }
318
+ }
319
+
320
+ MapTypeToString(sourceType: string, sourceLength?: number,
321
+ sourcePrecision?: number, sourceScale?: number): string {
322
+ const mapped = this.MapType(sourceType, sourceLength, sourcePrecision, sourceScale);
323
+ // Format with length/precision as needed
324
+ return mapped.typeName;
325
+ }
326
+ }
327
+ ```
328
+
329
+ 2. **Create the dialect class** extending `SQLDialect`:
330
+
331
+ ```typescript
332
+ import { SQLDialect, DataTypeMap } from '@memberjunction/sql-dialect';
333
+
334
+ export class MySQLDialect extends SQLDialect {
335
+ get PlatformKey(): DatabasePlatform { return 'mysql' as DatabasePlatform; }
336
+ get TypeMap(): DataTypeMap { return new MySQLDataTypeMap(); }
337
+
338
+ QuoteIdentifier(name: string): string { return `\`${name}\``; }
339
+ QuoteSchema(schema: string, object: string): string {
340
+ return `\`${schema}\`.\`${object}\``;
341
+ }
342
+ LimitClause(limit: number, offset?: number): LimitClauseResult {
343
+ const suffix = offset != null
344
+ ? `LIMIT ${limit} OFFSET ${offset}`
345
+ : `LIMIT ${limit}`;
346
+ return { prefix: '', suffix };
347
+ }
348
+ BooleanLiteral(value: boolean): string { return value ? '1' : '0'; }
349
+ // ... implement all remaining abstract methods (~25+)
350
+ }
351
+ ```
352
+
353
+ 3. **Update the `DatabasePlatform` type** in `sqlDialect.ts` to include the new platform key.
354
+
355
+ 4. **Export from `index.ts`**:
356
+
357
+ ```typescript
358
+ export { MySQLDialect } from './mysqlDialect.js';
359
+ ```
360
+
361
+ 5. **Add tests** in `src/__tests__/mysqlDialect.test.ts` covering every method. The existing `crossDialect.test.ts` provides a pattern for testing multiple dialects against the same assertions.
362
+
363
+ ## Side-by-Side Dialect Comparison
364
+
365
+ | Feature | SQL Server (`SQLServerDialect`) | PostgreSQL (`PostgreSQLDialect`) |
366
+ |---|---|---|
367
+ | **Identifier quoting** | `[name]` | `"name"` |
368
+ | **Schema-qualified** | `[schema].[object]` | `schema."object"` |
369
+ | **Boolean literals** | `1` / `0` | `true` / `false` |
370
+ | **Current UTC time** | `GETUTCDATE()` | `(NOW() AT TIME ZONE 'UTC')` |
371
+ | **New UUID** | `NEWID()` | `gen_random_uuid()` |
372
+ | **UUID PK default** | `NEWSEQUENTIALID()` | `gen_random_uuid()` |
373
+ | **Auto-increment** | `IDENTITY(1,1)` | `GENERATED ALWAYS AS IDENTITY` |
374
+ | **Pagination (no offset)** | `SELECT TOP 10 ...` | `... LIMIT 10` |
375
+ | **Pagination (with offset)** | `OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY` | `LIMIT 10 OFFSET 20` |
376
+ | **Return inserted** | `OUTPUT INSERTED.*` | `RETURNING *` |
377
+ | **Scope identity** | `SCOPE_IDENTITY()` | `lastval()` |
378
+ | **Row count** | `@@ROWCOUNT` | `ROW_COUNT` (GET DIAGNOSTICS) |
379
+ | **Concatenation** | `+` | `\|\|` |
380
+ | **Parameters** | `@p0`, `@p1`, ... | `$1`, `$2`, ... |
381
+ | **Batch separator** | `GO` | _(none)_ |
382
+ | **Conditional** | `IIF(cond, t, f)` | `CASE WHEN cond THEN t ELSE f END` |
383
+ | **Recursive CTE** | `WITH` | `WITH RECURSIVE` |
384
+ | **Procedure call** | `EXEC [s].[name] @p0` | `SELECT * FROM s."name"($1)` |
385
+ | **CREATE OR REPLACE** | Not supported | FUNCTION, VIEW, PROCEDURE |
386
+ | **Full-text search** | `CONTAINS([col], term)` | `col @@ plainto_tsquery(...)` |
387
+ | **JSON extract** | `JSON_VALUE(col, 'path')` | `col->>'path'` |
388
+ | **String split** | `STRING_SPLIT(val, delim)` | `unnest(string_to_array(val, delim))` |
389
+ | **Cast to text** | `CAST(x AS NVARCHAR(MAX))` | `CAST(x AS TEXT)` |
390
+ | **Cast to UUID** | `CAST(x AS UNIQUEIDENTIFIER)` | `CAST(x AS UUID)` |
391
+ | **Object existence** | `IF OBJECT_ID(...) IS NOT NULL` | `SELECT EXISTS (... pg_catalog ...)` |
392
+ | **Comments/descriptions** | `sp_addextendedproperty` | `COMMENT ON ...` |
393
+ | **Grants** | `GRANT ... ON [s].[o] TO [r]` | `GRANT ... ON s."o" TO "r"` |
394
+
395
+ ## Installation
396
+
397
+ ```bash
398
+ npm install @memberjunction/sql-dialect
399
+ ```
400
+
401
+ Or, in a MemberJunction workspace, add the dependency to your package's `package.json` and run `npm install` from the repo root.
402
+
403
+ ## License
404
+
405
+ ISC
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Represents a mapped data type from one database platform to another.
3
+ */
4
+ export interface MappedType {
5
+ /** The target platform data type name (e.g., "VARCHAR", "UUID", "BOOLEAN") */
6
+ typeName: string;
7
+ /** Whether the type supports a length parameter (e.g., VARCHAR(255)) */
8
+ supportsLength: boolean;
9
+ /** Whether the type supports precision/scale (e.g., NUMERIC(10,2)) */
10
+ supportsPrecisionScale: boolean;
11
+ /** Default length if applicable */
12
+ defaultLength?: number;
13
+ }
14
+ /**
15
+ * Maps data types from one database platform to another.
16
+ * Each dialect provides its own DataTypeMap implementation.
17
+ */
18
+ export interface DataTypeMap {
19
+ /**
20
+ * Maps a source database type to the target platform type.
21
+ * @param sourceType - The source database type name (case-insensitive)
22
+ * @param sourceLength - Optional length from the source type definition
23
+ * @param sourcePrecision - Optional precision from the source type definition
24
+ * @param sourceScale - Optional scale from the source type definition
25
+ * @returns The mapped type for the target platform
26
+ */
27
+ MapType(sourceType: string, sourceLength?: number, sourcePrecision?: number, sourceScale?: number): MappedType;
28
+ /**
29
+ * Returns the full type string with length/precision/scale as needed.
30
+ * E.g., "VARCHAR(255)" or "NUMERIC(10,2)" or "UUID"
31
+ */
32
+ MapTypeToString(sourceType: string, sourceLength?: number, sourcePrecision?: number, sourceScale?: number): string;
33
+ }
34
+ //# sourceMappingURL=dataTypeMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataTypeMap.d.ts","sourceRoot":"","sources":["../src/dataTypeMap.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,cAAc,EAAE,OAAO,CAAC;IACxB,sEAAsE;IACtE,sBAAsB,EAAE,OAAO,CAAC;IAChC,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAE/G;;;OAGG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACtH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dataTypeMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataTypeMap.js","sourceRoot":"","sources":["../src/dataTypeMap.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export { DataTypeMap, MappedType } from './dataTypeMap.js';
2
+ export { SQLDialect, DatabasePlatform, LimitClauseResult, SchemaIntrospectionSQL, TriggerOptions, IndexOptions, } from './sqlDialect.js';
3
+ export { SQLServerDialect } from './sqlServerDialect.js';
4
+ export { PostgreSQLDialect } from './postgresqlDialect.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EACH,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,YAAY,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { SQLDialect, } from './sqlDialect.js';
2
+ export { SQLServerDialect } from './sqlServerDialect.js';
3
+ export { PostgreSQLDialect } from './postgresqlDialect.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACH,UAAU,GAMb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { DataTypeMap } from './dataTypeMap.js';
2
+ import { SQLDialect, DatabasePlatform, LimitClauseResult, SchemaIntrospectionSQL, TriggerOptions, IndexOptions } from './sqlDialect.js';
3
+ /**
4
+ * PostgreSQL dialect implementation.
5
+ * Uses "double-quote" identifiers, LIMIT/OFFSET pagination, native BOOLEAN, PL/pgSQL functions.
6
+ */
7
+ export declare class PostgreSQLDialect extends SQLDialect {
8
+ get PlatformKey(): DatabasePlatform;
9
+ QuoteIdentifier(name: string): string;
10
+ QuoteSchema(schema: string, object: string): string;
11
+ LimitClause(limit: number, offset?: number): LimitClauseResult;
12
+ BooleanLiteral(value: boolean): string;
13
+ CurrentTimestampUTC(): string;
14
+ NewUUID(): string;
15
+ CastToText(expr: string): string;
16
+ CastToUUID(expr: string): string;
17
+ ReturnInsertedClause(columns?: string[]): string;
18
+ AutoIncrementPKExpression(): string;
19
+ UUIDPKDefault(): string;
20
+ ScopeIdentityExpression(): string;
21
+ RowCountExpression(): string;
22
+ BatchSeparator(): string;
23
+ ExistenceCheckSQL(objectType: string, schema: string, name: string): string;
24
+ CreateOrReplaceSupported(objectType: string): boolean;
25
+ FullTextSearchPredicate(column: string, searchTerm: string): string;
26
+ FullTextIndexDDL(table: string, columns: string[], _catalog?: string): string;
27
+ RecursiveCTESyntax(): string;
28
+ get TypeMap(): DataTypeMap;
29
+ ParameterPlaceholder(index: number): string;
30
+ ConcatOperator(): string;
31
+ StringSplitFunction(value: string, delimiter: string): string;
32
+ JsonExtract(column: string, path: string): string;
33
+ ProcedureCallSyntax(schema: string, name: string, params: string[]): string;
34
+ TriggerDDL(options: TriggerOptions): string;
35
+ IndexDDL(options: IndexOptions): string;
36
+ GrantPermission(permission: string, _objectType: string, schema: string, object: string, role: string): string;
37
+ CommentOnObject(objectType: string, schema: string, name: string, comment: string): string;
38
+ SchemaIntrospectionQueries(): SchemaIntrospectionSQL;
39
+ IIF(condition: string, trueVal: string, falseVal: string): string;
40
+ private extractSchema;
41
+ private extractName;
42
+ }
43
+ //# sourceMappingURL=postgresqlDialect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgresqlDialect.d.ts","sourceRoot":"","sources":["../src/postgresqlDialect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EACH,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,YAAY,EACf,MAAM,iBAAiB,CAAC;AAwIzB;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC7C,IAAI,WAAW,IAAI,gBAAgB,CAElC;IAID,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIrC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAMnD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB;IAU9D,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAItC,mBAAmB,IAAI,MAAM;IAI7B,OAAO,IAAI,MAAM;IAIjB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIhC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAMhC,oBAAoB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM;IAQhD,yBAAyB,IAAI,MAAM;IAInC,aAAa,IAAI,MAAM;IAIvB,uBAAuB,IAAI,MAAM;IAIjC,kBAAkB,IAAI,MAAM;IAO5B,cAAc,IAAI,MAAM;IAIxB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAkB3E,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAOrD,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;IAInE,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAkC7E,kBAAkB,IAAI,MAAM;IAM5B,IAAI,OAAO,IAAI,WAAW,CAEzB;IAID,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI3C,cAAc,IAAI,MAAM;IAMxB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAI7D,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAOjD,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;IAO3E,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM;IAwB3C,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;IAavC,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAI9G,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAQ1F,0BAA0B,IAAI,sBAAsB;IAgEpD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAMjE,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,WAAW;CAKtB"}