@mostajs/orm 1.0.0 → 1.2.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/bridge/MostaJdbcBridge.java +345 -0
- package/dist/bridge/BridgeManager.d.ts +74 -0
- package/dist/bridge/BridgeManager.js +361 -0
- package/dist/bridge/JdbcNormalizer.d.ts +63 -0
- package/dist/bridge/JdbcNormalizer.js +253 -0
- package/dist/bridge/jdbc-registry.d.ts +31 -0
- package/dist/bridge/jdbc-registry.js +67 -0
- package/dist/dialects/abstract-sql.dialect.d.ts +26 -4
- package/dist/dialects/abstract-sql.dialect.js +77 -3
- package/dist/dialects/db2.dialect.js +2 -2
- package/dist/dialects/hana.dialect.js +2 -2
- package/dist/dialects/hsqldb.dialect.js +22 -56
- package/dist/dialects/mariadb.dialect.js +2 -2
- package/dist/dialects/mssql.dialect.d.ts +2 -2
- package/dist/dialects/mssql.dialect.js +2 -2
- package/dist/dialects/mysql.dialect.d.ts +2 -2
- package/dist/dialects/mysql.dialect.js +2 -2
- package/dist/dialects/oracle.dialect.js +2 -2
- package/dist/dialects/postgres.dialect.d.ts +2 -2
- package/dist/dialects/postgres.dialect.js +2 -2
- package/dist/dialects/spanner.dialect.js +2 -2
- package/dist/dialects/sybase.dialect.js +2 -2
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/docs/audit-dialects-vs-hibernate.md +642 -0
- package/docs/jdbc-normalizer-study.md +843 -0
- package/docs/plan-session-factory-multi-bridge.md +1233 -0
- package/jar_files/hsqldb-2.7.2.jar +0 -0
- package/package.json +3 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DialectType } from '../core/types.js';
|
|
2
|
+
export interface JdbcDriverInfo {
|
|
3
|
+
/** Glob prefix to find the JAR in jar_files/ (e.g. 'hsqldb' matches hsqldb*.jar) */
|
|
4
|
+
jarPrefix: string;
|
|
5
|
+
/** JDBC URL template — {host}, {port}, {db} are replaced at runtime */
|
|
6
|
+
jdbcUrlTemplate: string;
|
|
7
|
+
/** Default SGBD port */
|
|
8
|
+
defaultPort: number;
|
|
9
|
+
/** Default JDBC user */
|
|
10
|
+
defaultUser: string;
|
|
11
|
+
/** Default JDBC password */
|
|
12
|
+
defaultPassword: string;
|
|
13
|
+
/** JDBC driver class name (for logging) */
|
|
14
|
+
driverClass: string;
|
|
15
|
+
/** Human-readable label */
|
|
16
|
+
label: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Registry of JDBC-bridge-eligible dialects.
|
|
20
|
+
* Only dialects that benefit from the JDBC bridge are listed here.
|
|
21
|
+
* Dialects with good npm drivers (pg, mysql2, mongoose...) are NOT listed.
|
|
22
|
+
*/
|
|
23
|
+
export declare const JDBC_REGISTRY: Partial<Record<DialectType, JdbcDriverInfo>>;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a dialect has a JDBC bridge entry.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hasJdbcDriver(dialect: DialectType): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Get JDBC driver info for a dialect. Returns undefined if not bridge-eligible.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getJdbcDriverInfo(dialect: DialectType): JdbcDriverInfo | undefined;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// JDBC Driver Registry — maps dialect → JAR pattern → JDBC URL template
|
|
2
|
+
// Used by JdbcNormalizer to auto-detect JARs and compose JDBC URLs
|
|
3
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
4
|
+
/**
|
|
5
|
+
* Registry of JDBC-bridge-eligible dialects.
|
|
6
|
+
* Only dialects that benefit from the JDBC bridge are listed here.
|
|
7
|
+
* Dialects with good npm drivers (pg, mysql2, mongoose...) are NOT listed.
|
|
8
|
+
*/
|
|
9
|
+
export const JDBC_REGISTRY = {
|
|
10
|
+
hsqldb: {
|
|
11
|
+
jarPrefix: 'hsqldb',
|
|
12
|
+
jdbcUrlTemplate: 'jdbc:hsqldb:hsql://{host}:{port}/{db}',
|
|
13
|
+
defaultPort: 9001,
|
|
14
|
+
defaultUser: 'SA',
|
|
15
|
+
defaultPassword: '',
|
|
16
|
+
driverClass: 'org.hsqldb.jdbc.JDBCDriver',
|
|
17
|
+
label: 'HyperSQL (HSQLDB)',
|
|
18
|
+
},
|
|
19
|
+
oracle: {
|
|
20
|
+
jarPrefix: 'ojdbc',
|
|
21
|
+
jdbcUrlTemplate: 'jdbc:oracle:thin:@//{host}:{port}/{db}',
|
|
22
|
+
defaultPort: 1521,
|
|
23
|
+
defaultUser: 'system',
|
|
24
|
+
defaultPassword: 'oracle',
|
|
25
|
+
driverClass: 'oracle.jdbc.OracleDriver',
|
|
26
|
+
label: 'Oracle Database',
|
|
27
|
+
},
|
|
28
|
+
db2: {
|
|
29
|
+
jarPrefix: 'db2jcc',
|
|
30
|
+
jdbcUrlTemplate: 'jdbc:db2://{host}:{port}/{db}',
|
|
31
|
+
defaultPort: 50000,
|
|
32
|
+
defaultUser: 'db2inst1',
|
|
33
|
+
defaultPassword: 'db2inst1',
|
|
34
|
+
driverClass: 'com.ibm.db2.jcc.DB2Driver',
|
|
35
|
+
label: 'IBM DB2',
|
|
36
|
+
},
|
|
37
|
+
sybase: {
|
|
38
|
+
jarPrefix: 'jconn',
|
|
39
|
+
jdbcUrlTemplate: 'jdbc:sybase:Tds:{host}:{port}/{db}',
|
|
40
|
+
defaultPort: 5000,
|
|
41
|
+
defaultUser: 'sa',
|
|
42
|
+
defaultPassword: '',
|
|
43
|
+
driverClass: 'com.sybase.jdbc4.jdbc.SybDriver',
|
|
44
|
+
label: 'Sybase ASE',
|
|
45
|
+
},
|
|
46
|
+
hana: {
|
|
47
|
+
jarPrefix: 'ngdbc',
|
|
48
|
+
jdbcUrlTemplate: 'jdbc:sap://{host}:{port}',
|
|
49
|
+
defaultPort: 30015,
|
|
50
|
+
defaultUser: 'SYSTEM',
|
|
51
|
+
defaultPassword: 'manager',
|
|
52
|
+
driverClass: 'com.sap.db.jdbc.Driver',
|
|
53
|
+
label: 'SAP HANA',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Check if a dialect has a JDBC bridge entry.
|
|
58
|
+
*/
|
|
59
|
+
export function hasJdbcDriver(dialect) {
|
|
60
|
+
return dialect in JDBC_REGISTRY;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get JDBC driver info for a dialect. Returns undefined if not bridge-eligible.
|
|
64
|
+
*/
|
|
65
|
+
export function getJdbcDriverInfo(dialect) {
|
|
66
|
+
return JDBC_REGISTRY[dialect];
|
|
67
|
+
}
|
|
@@ -13,10 +13,16 @@ export declare abstract class AbstractSqlDialect implements IDialect {
|
|
|
13
13
|
abstract fieldToSqlType(field: FieldDef): string;
|
|
14
14
|
/** Get the SQL column type for the primary key (id) column */
|
|
15
15
|
abstract getIdColumnType(): string;
|
|
16
|
-
/** Execute a SELECT query
|
|
17
|
-
abstract
|
|
18
|
-
/** Execute a non-SELECT statement
|
|
19
|
-
abstract
|
|
16
|
+
/** Execute a SELECT query via the dialect's native driver (npm) */
|
|
17
|
+
abstract doExecuteQuery<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
18
|
+
/** Execute a non-SELECT statement via the dialect's native driver (npm) */
|
|
19
|
+
abstract doExecuteRun(sql: string, params: unknown[]): Promise<{
|
|
20
|
+
changes: number;
|
|
21
|
+
}>;
|
|
22
|
+
/** Execute a SELECT query — routes to JDBC bridge or native driver */
|
|
23
|
+
executeQuery<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
24
|
+
/** Execute a non-SELECT statement — routes to JDBC bridge or native driver */
|
|
25
|
+
executeRun(sql: string, params: unknown[]): Promise<{
|
|
20
26
|
changes: number;
|
|
21
27
|
}>;
|
|
22
28
|
/** Establish the actual database connection */
|
|
@@ -32,6 +38,8 @@ export declare abstract class AbstractSqlDialect implements IDialect {
|
|
|
32
38
|
protected showSql: boolean;
|
|
33
39
|
protected formatSql: boolean;
|
|
34
40
|
private paramCounter;
|
|
41
|
+
private bridgeInstance;
|
|
42
|
+
private jdbcBridgeActive;
|
|
35
43
|
/** Whether this dialect supports CREATE TABLE IF NOT EXISTS */
|
|
36
44
|
protected supportsIfNotExists(): boolean;
|
|
37
45
|
/** Whether this dialect supports RETURNING clause on INSERT */
|
|
@@ -85,6 +93,20 @@ export declare abstract class AbstractSqlDialect implements IDialect {
|
|
|
85
93
|
connect(config: ConnectionConfig): Promise<void>;
|
|
86
94
|
disconnect(): Promise<void>;
|
|
87
95
|
testConnection(): Promise<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Execute a SELECT query via the JDBC bridge.
|
|
98
|
+
* Called transparently when jdbcBridgeActive is true.
|
|
99
|
+
*/
|
|
100
|
+
protected bridgeExecuteQuery<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
101
|
+
/**
|
|
102
|
+
* Execute a non-SELECT statement via the JDBC bridge.
|
|
103
|
+
* Called transparently when jdbcBridgeActive is true.
|
|
104
|
+
*/
|
|
105
|
+
protected bridgeExecuteRun(sql: string, params: unknown[]): Promise<{
|
|
106
|
+
changes: number;
|
|
107
|
+
}>;
|
|
108
|
+
/** Whether the JDBC bridge is active for this dialect instance */
|
|
109
|
+
protected get isJdbcBridgeActive(): boolean;
|
|
88
110
|
initSchema(schemas: EntitySchema[]): Promise<void>;
|
|
89
111
|
find<T>(schema: EntitySchema, filter: DALFilter, options?: QueryOptions): Promise<T[]>;
|
|
90
112
|
findOne<T>(schema: EntitySchema, filter: DALFilter, options?: QueryOptions): Promise<T | null>;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// Abstract SQL Dialect — base class for all SQL dialects
|
|
2
2
|
// Inspired by org.hibernate.dialect.Dialect (Hibernate ORM 6.4)
|
|
3
3
|
// Extracts ~80% of shared SQL logic from sqlite.dialect.ts
|
|
4
|
+
// Includes JDBC bridge support via JdbcNormalizer (transparent interception)
|
|
4
5
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
5
6
|
import { randomUUID } from 'crypto';
|
|
7
|
+
import { JdbcNormalizer } from '../bridge/JdbcNormalizer.js';
|
|
8
|
+
import { hasJdbcDriver } from '../bridge/jdbc-registry.js';
|
|
9
|
+
import { BridgeManager } from '../bridge/BridgeManager.js';
|
|
6
10
|
// ============================================================
|
|
7
11
|
// SQL Logging — inspired by hibernate.show_sql / hibernate.format_sql
|
|
8
12
|
// ============================================================
|
|
@@ -57,12 +61,28 @@ function regexToLike(regex) {
|
|
|
57
61
|
// AbstractSqlDialect — base for all SQL dialects
|
|
58
62
|
// ============================================================
|
|
59
63
|
export class AbstractSqlDialect {
|
|
64
|
+
// --- Concrete query methods with JDBC bridge interception ---
|
|
65
|
+
/** Execute a SELECT query — routes to JDBC bridge or native driver */
|
|
66
|
+
async executeQuery(sql, params) {
|
|
67
|
+
if (this.jdbcBridgeActive)
|
|
68
|
+
return this.bridgeExecuteQuery(sql, params);
|
|
69
|
+
return this.doExecuteQuery(sql, params);
|
|
70
|
+
}
|
|
71
|
+
/** Execute a non-SELECT statement — routes to JDBC bridge or native driver */
|
|
72
|
+
async executeRun(sql, params) {
|
|
73
|
+
if (this.jdbcBridgeActive)
|
|
74
|
+
return this.bridgeExecuteRun(sql, params);
|
|
75
|
+
return this.doExecuteRun(sql, params);
|
|
76
|
+
}
|
|
60
77
|
// --- Protected state ---
|
|
61
78
|
config = null;
|
|
62
79
|
schemas = [];
|
|
63
80
|
showSql = false;
|
|
64
81
|
formatSql = false;
|
|
65
82
|
paramCounter = 0;
|
|
83
|
+
// --- JDBC Bridge state (transparent interception) ---
|
|
84
|
+
bridgeInstance = null;
|
|
85
|
+
jdbcBridgeActive = false;
|
|
66
86
|
// --- Hooks (overridable by subclasses) ---
|
|
67
87
|
/** Whether this dialect supports CREATE TABLE IF NOT EXISTS */
|
|
68
88
|
supportsIfNotExists() { return true; }
|
|
@@ -523,8 +543,25 @@ export class AbstractSqlDialect {
|
|
|
523
543
|
this.config = config;
|
|
524
544
|
this.showSql = config.showSql ?? false;
|
|
525
545
|
this.formatSql = config.formatSql ?? false;
|
|
526
|
-
|
|
527
|
-
this
|
|
546
|
+
// --- JDBC Bridge interception via BridgeManager ---
|
|
547
|
+
// If a JDBC JAR is available for this dialect, use the bridge
|
|
548
|
+
// instead of calling the dialect's doConnect() (npm driver).
|
|
549
|
+
// BridgeManager handles multi-bridge, port management, PID files, autostart.
|
|
550
|
+
const jarDir = config.options?.jarDir;
|
|
551
|
+
if (hasJdbcDriver(this.dialectType) && JdbcNormalizer.isAvailable(this.dialectType, jarDir)) {
|
|
552
|
+
const manager = BridgeManager.getInstance();
|
|
553
|
+
this.bridgeInstance = await manager.getOrCreate(this.dialectType, config.uri, {
|
|
554
|
+
jarDir,
|
|
555
|
+
bridgeJavaFile: config.options?.bridgeJavaFile,
|
|
556
|
+
});
|
|
557
|
+
this.jdbcBridgeActive = true;
|
|
558
|
+
this.log('CONNECT', `${config.uri} [via JDBC bridge on port ${this.bridgeInstance.port}]`);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
// No JAR found — use the dialect's native npm driver
|
|
562
|
+
await this.doConnect(config);
|
|
563
|
+
this.log('CONNECT', config.uri);
|
|
564
|
+
}
|
|
528
565
|
if (config.schemaStrategy === 'create') {
|
|
529
566
|
this.log('SCHEMA', 'create — dropping existing tables');
|
|
530
567
|
await this.dropAllTables();
|
|
@@ -535,19 +572,56 @@ export class AbstractSqlDialect {
|
|
|
535
572
|
this.log('SCHEMA', 'create-drop — dropping all tables on shutdown');
|
|
536
573
|
await this.dropAllTables();
|
|
537
574
|
}
|
|
538
|
-
|
|
575
|
+
if (this.jdbcBridgeActive && this.bridgeInstance) {
|
|
576
|
+
// Do NOT stop the bridge — BridgeManager manages its lifecycle.
|
|
577
|
+
// Other dialect instances may reuse the same bridge.
|
|
578
|
+
// Bridges are stopped by BridgeManager.stopAll() on app exit.
|
|
579
|
+
this.bridgeInstance = null;
|
|
580
|
+
this.jdbcBridgeActive = false;
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
await this.doDisconnect();
|
|
584
|
+
}
|
|
539
585
|
this.config = null;
|
|
540
586
|
this.schemas = [];
|
|
541
587
|
this.log('DISCONNECT', '');
|
|
542
588
|
}
|
|
543
589
|
async testConnection() {
|
|
544
590
|
try {
|
|
591
|
+
if (this.jdbcBridgeActive && this.bridgeInstance) {
|
|
592
|
+
const result = await this.bridgeInstance.normalizer.query('SELECT 1', []);
|
|
593
|
+
return Array.isArray(result);
|
|
594
|
+
}
|
|
545
595
|
return await this.doTestConnection();
|
|
546
596
|
}
|
|
547
597
|
catch {
|
|
548
598
|
return false;
|
|
549
599
|
}
|
|
550
600
|
}
|
|
601
|
+
// --- JDBC Bridge query methods (used by executeQuery/executeRun interception) ---
|
|
602
|
+
/**
|
|
603
|
+
* Execute a SELECT query via the JDBC bridge.
|
|
604
|
+
* Called transparently when jdbcBridgeActive is true.
|
|
605
|
+
*/
|
|
606
|
+
async bridgeExecuteQuery(sql, params) {
|
|
607
|
+
if (!this.bridgeInstance)
|
|
608
|
+
throw new Error('JDBC bridge not initialized');
|
|
609
|
+
return this.bridgeInstance.normalizer.query(sql, params);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Execute a non-SELECT statement via the JDBC bridge.
|
|
613
|
+
* Called transparently when jdbcBridgeActive is true.
|
|
614
|
+
*/
|
|
615
|
+
async bridgeExecuteRun(sql, params) {
|
|
616
|
+
if (!this.bridgeInstance)
|
|
617
|
+
throw new Error('JDBC bridge not initialized');
|
|
618
|
+
const result = await this.bridgeInstance.normalizer.query(sql, params);
|
|
619
|
+
return { changes: result?.changes ?? 0 };
|
|
620
|
+
}
|
|
621
|
+
/** Whether the JDBC bridge is active for this dialect instance */
|
|
622
|
+
get isJdbcBridgeActive() {
|
|
623
|
+
return this.jdbcBridgeActive;
|
|
624
|
+
}
|
|
551
625
|
// --- Schema management (hibernate.hbm2ddl.auto) ---
|
|
552
626
|
async initSchema(schemas) {
|
|
553
627
|
this.schemas = schemas;
|
|
@@ -97,7 +97,7 @@ class DB2Dialect extends AbstractSqlDialect {
|
|
|
97
97
|
return Array.isArray(rows);
|
|
98
98
|
}
|
|
99
99
|
// --- Query execution ---
|
|
100
|
-
async
|
|
100
|
+
async doExecuteQuery(sql, params) {
|
|
101
101
|
if (!this.conn)
|
|
102
102
|
throw new Error('DB2 not connected. Call connect() first.');
|
|
103
103
|
return new Promise((resolve, reject) => {
|
|
@@ -109,7 +109,7 @@ class DB2Dialect extends AbstractSqlDialect {
|
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
|
-
async
|
|
112
|
+
async doExecuteRun(sql, params) {
|
|
113
113
|
if (!this.conn)
|
|
114
114
|
throw new Error('DB2 not connected. Call connect() first.');
|
|
115
115
|
return new Promise((resolve, reject) => {
|
|
@@ -92,7 +92,7 @@ class HANADialect extends AbstractSqlDialect {
|
|
|
92
92
|
return Array.isArray(rows);
|
|
93
93
|
}
|
|
94
94
|
// --- Query execution ---
|
|
95
|
-
async
|
|
95
|
+
async doExecuteQuery(sql, params) {
|
|
96
96
|
if (!this.conn)
|
|
97
97
|
throw new Error('HANA not connected. Call connect() first.');
|
|
98
98
|
return new Promise((resolve, reject) => {
|
|
@@ -104,7 +104,7 @@ class HANADialect extends AbstractSqlDialect {
|
|
|
104
104
|
});
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
|
-
async
|
|
107
|
+
async doExecuteRun(sql, params) {
|
|
108
108
|
if (!this.conn)
|
|
109
109
|
throw new Error('HANA not connected. Call connect() first.');
|
|
110
110
|
return new Promise((resolve, reject) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// HyperSQL (HSQLDB) Dialect — extends AbstractSqlDialect
|
|
2
2
|
// Equivalent to org.hibernate.dialect.HSQLDialect (Hibernate ORM 6.4)
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// Driver: JDBC bridge (no npm driver — Java database)
|
|
4
|
+
// Connection handled transparently by AbstractSqlDialect JDBC bridge interception
|
|
5
5
|
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
6
6
|
import { AbstractSqlDialect } from './abstract-sql.dialect.js';
|
|
7
7
|
// ============================================================
|
|
@@ -16,13 +16,12 @@ const HSQL_TYPE_MAP = {
|
|
|
16
16
|
array: 'CLOB',
|
|
17
17
|
};
|
|
18
18
|
// ============================================================
|
|
19
|
-
// HSQLDialect
|
|
19
|
+
// HSQLDialect — SQL definition only (like Hibernate HSQLDialect)
|
|
20
|
+
// Connection is handled by AbstractSqlDialect via JDBC bridge
|
|
20
21
|
// ============================================================
|
|
21
22
|
class HSQLDialect extends AbstractSqlDialect {
|
|
22
23
|
dialectType = 'hsqldb';
|
|
23
|
-
|
|
24
|
-
connected = false;
|
|
25
|
-
// --- Abstract implementations ---
|
|
24
|
+
// --- Abstract implementations (SQL definition) ---
|
|
26
25
|
quoteIdentifier(name) {
|
|
27
26
|
return `"${name}"`;
|
|
28
27
|
}
|
|
@@ -47,62 +46,29 @@ class HSQLDialect extends AbstractSqlDialect {
|
|
|
47
46
|
}
|
|
48
47
|
// HSQLDB supports LIMIT/OFFSET natively
|
|
49
48
|
// (default buildLimitOffset from AbstractSqlDialect works)
|
|
50
|
-
// --- Connection (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
await this.httpPost('SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS', []);
|
|
59
|
-
this.connected = true;
|
|
60
|
-
}
|
|
61
|
-
catch (e) {
|
|
62
|
-
throw new Error(`HSQLDB HTTP bridge not reachable at ${this.baseUrl}.\n` +
|
|
63
|
-
`Ensure the HSQLDB server is running with HTTP API enabled.\n` +
|
|
64
|
-
`Original error: ${e instanceof Error ? e.message : String(e)}`);
|
|
65
|
-
}
|
|
49
|
+
// --- Connection (native driver fallback) ---
|
|
50
|
+
// HSQLDB is a Java database — no npm driver exists.
|
|
51
|
+
// Normal path: AbstractSqlDialect detects hsqldb*.jar and uses JDBC bridge.
|
|
52
|
+
// These methods are only called if no JAR is found (fallback impossible).
|
|
53
|
+
async doConnect(_config) {
|
|
54
|
+
throw new Error('HSQLDB requires a JDBC bridge.\n' +
|
|
55
|
+
'Place hsqldb*.jar in the jar_files/ directory.\n' +
|
|
56
|
+
'No npm driver exists for HSQLDB.');
|
|
66
57
|
}
|
|
67
58
|
async doDisconnect() {
|
|
68
|
-
|
|
69
|
-
this.baseUrl = '';
|
|
59
|
+
// Nothing to clean up — bridge is managed by AbstractSqlDialect
|
|
70
60
|
}
|
|
71
61
|
async doTestConnection() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
await this.httpPost('SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS', []);
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
62
|
+
// Bridge test is handled by AbstractSqlDialect.testConnection()
|
|
63
|
+
return false;
|
|
81
64
|
}
|
|
82
|
-
// --- Query execution
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return this.httpPost(sql, params);
|
|
65
|
+
// --- Query execution (native driver fallback) ---
|
|
66
|
+
// Only called when JDBC bridge is NOT active (impossible for HSQLDB).
|
|
67
|
+
async doExecuteQuery(_sql, _params) {
|
|
68
|
+
throw new Error('HSQLDB requires a JDBC bridge. Place hsqldb*.jar in jar_files/.');
|
|
87
69
|
}
|
|
88
|
-
async
|
|
89
|
-
|
|
90
|
-
throw new Error('HSQLDB not connected. Call connect() first.');
|
|
91
|
-
const result = await this.httpPost(sql, params);
|
|
92
|
-
return { changes: result?.changes ?? 0 };
|
|
93
|
-
}
|
|
94
|
-
/** Send SQL to HSQLDB HTTP bridge */
|
|
95
|
-
async httpPost(sql, params) {
|
|
96
|
-
const response = await fetch(`${this.baseUrl}/query`, {
|
|
97
|
-
method: 'POST',
|
|
98
|
-
headers: { 'Content-Type': 'application/json' },
|
|
99
|
-
body: JSON.stringify({ sql, params }),
|
|
100
|
-
});
|
|
101
|
-
if (!response.ok) {
|
|
102
|
-
const text = await response.text();
|
|
103
|
-
throw new Error(`HSQLDB query failed (${response.status}): ${text}`);
|
|
104
|
-
}
|
|
105
|
-
return response.json();
|
|
70
|
+
async doExecuteRun(_sql, _params) {
|
|
71
|
+
throw new Error('HSQLDB requires a JDBC bridge. Place hsqldb*.jar in jar_files/.');
|
|
106
72
|
}
|
|
107
73
|
getDialectLabel() { return 'HSQLDB'; }
|
|
108
74
|
}
|
|
@@ -34,7 +34,7 @@ class MariaDBDialect extends MySQLDialect {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
// Override executeQuery to handle mariadb driver's different API
|
|
37
|
-
async
|
|
37
|
+
async doExecuteQuery(sql, params) {
|
|
38
38
|
if (!this.pool)
|
|
39
39
|
throw new Error('MariaDB not connected. Call connect() first.');
|
|
40
40
|
try {
|
|
@@ -50,7 +50,7 @@ class MariaDBDialect extends MySQLDialect {
|
|
|
50
50
|
return super.executeQuery(sql, params);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
async
|
|
53
|
+
async doExecuteRun(sql, params) {
|
|
54
54
|
if (!this.pool)
|
|
55
55
|
throw new Error('MariaDB not connected. Call connect() first.');
|
|
56
56
|
try {
|
|
@@ -18,8 +18,8 @@ export declare class MSSQLDialect extends AbstractSqlDialect {
|
|
|
18
18
|
doConnect(config: ConnectionConfig): Promise<void>;
|
|
19
19
|
doDisconnect(): Promise<void>;
|
|
20
20
|
doTestConnection(): Promise<boolean>;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
doExecuteQuery<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
22
|
+
doExecuteRun(sql: string, params: unknown[]): Promise<{
|
|
23
23
|
changes: number;
|
|
24
24
|
}>;
|
|
25
25
|
protected getDialectLabel(): string;
|
|
@@ -96,7 +96,7 @@ export class MSSQLDialect extends AbstractSqlDialect {
|
|
|
96
96
|
return true;
|
|
97
97
|
}
|
|
98
98
|
// --- Query execution ---
|
|
99
|
-
async
|
|
99
|
+
async doExecuteQuery(sql, params) {
|
|
100
100
|
if (!this.pool)
|
|
101
101
|
throw new Error('SQL Server not connected. Call connect() first.');
|
|
102
102
|
const request = this.pool.request();
|
|
@@ -107,7 +107,7 @@ export class MSSQLDialect extends AbstractSqlDialect {
|
|
|
107
107
|
const result = await request.query(sql);
|
|
108
108
|
return result.recordset;
|
|
109
109
|
}
|
|
110
|
-
async
|
|
110
|
+
async doExecuteRun(sql, params) {
|
|
111
111
|
if (!this.pool)
|
|
112
112
|
throw new Error('SQL Server not connected. Call connect() first.');
|
|
113
113
|
const request = this.pool.request();
|
|
@@ -15,8 +15,8 @@ export declare class MySQLDialect extends AbstractSqlDialect {
|
|
|
15
15
|
doConnect(config: ConnectionConfig): Promise<void>;
|
|
16
16
|
doDisconnect(): Promise<void>;
|
|
17
17
|
doTestConnection(): Promise<boolean>;
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
doExecuteQuery<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
19
|
+
doExecuteRun(sql: string, params: unknown[]): Promise<{
|
|
20
20
|
changes: number;
|
|
21
21
|
}>;
|
|
22
22
|
protected getDialectLabel(): string;
|
|
@@ -79,13 +79,13 @@ export class MySQLDialect extends AbstractSqlDialect {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
// --- Query execution ---
|
|
82
|
-
async
|
|
82
|
+
async doExecuteQuery(sql, params) {
|
|
83
83
|
if (!this.pool)
|
|
84
84
|
throw new Error('MySQL not connected. Call connect() first.');
|
|
85
85
|
const [rows] = await this.pool.execute(sql, params);
|
|
86
86
|
return rows;
|
|
87
87
|
}
|
|
88
|
-
async
|
|
88
|
+
async doExecuteRun(sql, params) {
|
|
89
89
|
if (!this.pool)
|
|
90
90
|
throw new Error('MySQL not connected. Call connect() first.');
|
|
91
91
|
const [result] = await this.pool.execute(sql, params);
|
|
@@ -111,7 +111,7 @@ class OracleDialect extends AbstractSqlDialect {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
// --- Query execution ---
|
|
114
|
-
async
|
|
114
|
+
async doExecuteQuery(sql, params) {
|
|
115
115
|
if (!this.pool)
|
|
116
116
|
throw new Error('Oracle not connected. Call connect() first.');
|
|
117
117
|
const conn = await this.pool.getConnection();
|
|
@@ -123,7 +123,7 @@ class OracleDialect extends AbstractSqlDialect {
|
|
|
123
123
|
await conn.close();
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
async
|
|
126
|
+
async doExecuteRun(sql, params) {
|
|
127
127
|
if (!this.pool)
|
|
128
128
|
throw new Error('Oracle not connected. Call connect() first.');
|
|
129
129
|
const conn = await this.pool.getConnection();
|
|
@@ -17,8 +17,8 @@ export declare class PostgresDialect extends AbstractSqlDialect {
|
|
|
17
17
|
doConnect(config: ConnectionConfig): Promise<void>;
|
|
18
18
|
doDisconnect(): Promise<void>;
|
|
19
19
|
doTestConnection(): Promise<boolean>;
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
doExecuteQuery<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
21
|
+
doExecuteRun(sql: string, params: unknown[]): Promise<{
|
|
22
22
|
changes: number;
|
|
23
23
|
}>;
|
|
24
24
|
protected getDialectLabel(): string;
|
|
@@ -83,13 +83,13 @@ export class PostgresDialect extends AbstractSqlDialect {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
// --- Query execution ---
|
|
86
|
-
async
|
|
86
|
+
async doExecuteQuery(sql, params) {
|
|
87
87
|
if (!this.pool)
|
|
88
88
|
throw new Error('PostgreSQL not connected. Call connect() first.');
|
|
89
89
|
const result = await this.pool.query(sql, params);
|
|
90
90
|
return result.rows;
|
|
91
91
|
}
|
|
92
|
-
async
|
|
92
|
+
async doExecuteRun(sql, params) {
|
|
93
93
|
if (!this.pool)
|
|
94
94
|
throw new Error('PostgreSQL not connected. Call connect() first.');
|
|
95
95
|
const result = await this.pool.query(sql, params);
|
|
@@ -135,7 +135,7 @@ class SpannerDialect extends AbstractSqlDialect {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
// --- Query execution ---
|
|
138
|
-
async
|
|
138
|
+
async doExecuteQuery(sql, params) {
|
|
139
139
|
if (!this.database)
|
|
140
140
|
throw new Error('Spanner not connected. Call connect() first.');
|
|
141
141
|
// Build named params object: { p1: val1, p2: val2, ... }
|
|
@@ -152,7 +152,7 @@ class SpannerDialect extends AbstractSqlDialect {
|
|
|
152
152
|
return row;
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
|
-
async
|
|
155
|
+
async doExecuteRun(sql, params) {
|
|
156
156
|
if (!this.database)
|
|
157
157
|
throw new Error('Spanner not connected. Call connect() first.');
|
|
158
158
|
// For DML operations, Spanner requires using transactions
|
|
@@ -67,7 +67,7 @@ class SybaseDialect extends MSSQLDialect {
|
|
|
67
67
|
await this.pool.query('SELECT 1');
|
|
68
68
|
return true;
|
|
69
69
|
}
|
|
70
|
-
async
|
|
70
|
+
async doExecuteQuery(sql, params) {
|
|
71
71
|
if (!this.pool)
|
|
72
72
|
throw new Error('Sybase not connected. Call connect() first.');
|
|
73
73
|
// Sybase driver doesn't support named params — replace @pN with values
|
|
@@ -75,7 +75,7 @@ class SybaseDialect extends MSSQLDialect {
|
|
|
75
75
|
const result = await this.pool.query(resolvedSql);
|
|
76
76
|
return Array.isArray(result) ? result : [];
|
|
77
77
|
}
|
|
78
|
-
async
|
|
78
|
+
async doExecuteRun(sql, params) {
|
|
79
79
|
if (!this.pool)
|
|
80
80
|
throw new Error('Sybase not connected. Call connect() first.');
|
|
81
81
|
const resolvedSql = this.resolveParams(sql, params);
|
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,10 @@ export { registerSchema, registerSchemas, getSchema, getSchemaByCollection, getA
|
|
|
5
5
|
export { getDialect, getConfigFromEnv, getCurrentDialectType, disconnectDialect, testConnection, createConnection, } from './core/factory.js';
|
|
6
6
|
export { BaseRepository } from './core/base-repository.js';
|
|
7
7
|
export { normalizeDoc, normalizeDocs } from './core/normalizer.js';
|
|
8
|
+
export { JdbcNormalizer, parseUri } from './bridge/JdbcNormalizer.js';
|
|
9
|
+
export { JDBC_REGISTRY, hasJdbcDriver, getJdbcDriverInfo } from './bridge/jdbc-registry.js';
|
|
10
|
+
export { BridgeManager } from './bridge/BridgeManager.js';
|
|
11
|
+
export type { BridgeInstance } from './bridge/BridgeManager.js';
|
|
12
|
+
export type { JdbcDriverInfo } from './bridge/jdbc-registry.js';
|
|
13
|
+
export type { JdbcBridgeConfig } from './bridge/JdbcNormalizer.js';
|
|
8
14
|
export { MostaORMError, EntityNotFoundError, ConnectionError, ValidationError, DialectNotFoundError, } from './core/errors.js';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,12 @@ export { BaseRepository } from './core/base-repository.js';
|
|
|
21
21
|
// ============================================================
|
|
22
22
|
export { normalizeDoc, normalizeDocs } from './core/normalizer.js';
|
|
23
23
|
// ============================================================
|
|
24
|
+
// JDBC Bridge
|
|
25
|
+
// ============================================================
|
|
26
|
+
export { JdbcNormalizer, parseUri } from './bridge/JdbcNormalizer.js';
|
|
27
|
+
export { JDBC_REGISTRY, hasJdbcDriver, getJdbcDriverInfo } from './bridge/jdbc-registry.js';
|
|
28
|
+
export { BridgeManager } from './bridge/BridgeManager.js';
|
|
29
|
+
// ============================================================
|
|
24
30
|
// Errors
|
|
25
31
|
// ============================================================
|
|
26
32
|
export { MostaORMError, EntityNotFoundError, ConnectionError, ValidationError, DialectNotFoundError, } from './core/errors.js';
|