Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c080b15078 | |
|
|
ec085ddfd1 |
|
|
@ -336,50 +336,62 @@ class PostgreSQLStore extends CascadeStore_1.CascadeStore {
|
||||||
await this.connector.disconnect();
|
await this.connector.disconnect();
|
||||||
}
|
}
|
||||||
async initialize(option) {
|
async initialize(option) {
|
||||||
// PG的DDL支持事务,所以这里直接用一个事务包裹所有的初始化操作
|
const schema = this.getSchema();
|
||||||
|
// ===== 第一阶段:事务外创建扩展 =====
|
||||||
|
let hasGeoType = false;
|
||||||
|
let hasChineseTsConfig = false;
|
||||||
|
let chineseParser = null;
|
||||||
|
// 扫描 schema
|
||||||
|
for (const entity in schema) {
|
||||||
|
const { attributes, indexes } = schema[entity];
|
||||||
|
for (const attr in attributes) {
|
||||||
|
if (attributes[attr].type === 'geometry') {
|
||||||
|
hasGeoType = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const index of indexes || []) {
|
||||||
|
if (index.config?.tsConfig === 'chinese' || index.config?.tsConfig?.includes('chinese')) {
|
||||||
|
hasChineseTsConfig = true;
|
||||||
|
}
|
||||||
|
if (index.config?.chineseParser) {
|
||||||
|
(0, assert_1.default)(!chineseParser || chineseParser === index.config.chineseParser, '当前定义了多个中文分词器,请保持一致');
|
||||||
|
chineseParser = index.config.chineseParser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 在事务外创建扩展
|
||||||
|
if (hasGeoType) {
|
||||||
|
console.log('Initializing PostGIS extension for geometry support...');
|
||||||
|
await this.connector.exec('CREATE EXTENSION IF NOT EXISTS postgis;');
|
||||||
|
}
|
||||||
|
if (hasChineseTsConfig) {
|
||||||
|
console.log('Initializing Chinese parser extension...');
|
||||||
|
await this.connector.exec(`CREATE EXTENSION IF NOT EXISTS ${chineseParser || 'zhparser'};`);
|
||||||
|
}
|
||||||
|
// ===== 第二阶段:事务内创建配置和表 =====
|
||||||
const txn = await this.connector.startTransaction({
|
const txn = await this.connector.startTransaction({
|
||||||
isolationLevel: 'serializable',
|
isolationLevel: 'serializable',
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const schema = this.getSchema();
|
// 创建中文文本搜索配置
|
||||||
let hasGeoType = false;
|
|
||||||
let hasChineseTsConfig = false;
|
|
||||||
for (const entity in schema) {
|
|
||||||
const { attributes, indexes } = schema[entity];
|
|
||||||
for (const attr in attributes) {
|
|
||||||
const { type } = attributes[attr];
|
|
||||||
if (type === 'geometry') {
|
|
||||||
hasGeoType = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const index of indexes || []) {
|
|
||||||
if (index.config?.tsConfig === 'chinese' || index.config?.tsConfig?.includes('chinese')) {
|
|
||||||
hasChineseTsConfig = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasGeoType) {
|
|
||||||
console.log('Initializing PostGIS extension for geometry support...');
|
|
||||||
await this.connector.exec('CREATE EXTENSION IF NOT EXISTS postgis;');
|
|
||||||
}
|
|
||||||
if (hasChineseTsConfig) {
|
if (hasChineseTsConfig) {
|
||||||
console.log('Initializing Chinese text search configuration...');
|
console.log('Initializing Chinese text search configuration...');
|
||||||
const checkChineseConfigSql = `
|
const checkChineseConfigSql = `
|
||||||
SELECT COUNT(*) as cnt
|
SELECT COUNT(*) as cnt
|
||||||
FROM pg_catalog.pg_ts_config
|
FROM pg_catalog.pg_ts_config
|
||||||
WHERE cfgname = 'chinese';
|
WHERE cfgname = 'chinese';
|
||||||
`;
|
`;
|
||||||
const result = await this.connector.exec(checkChineseConfigSql);
|
const result = await this.connector.exec(checkChineseConfigSql, txn);
|
||||||
const count = parseInt(result[0][0]?.cnt || '0', 10);
|
const count = parseInt(result[0][0]?.cnt || '0', 10);
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
const createChineseConfigSql = `
|
const createChineseConfigSql = `
|
||||||
CREATE EXTENSION IF NOT EXISTS zhparser;
|
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = ${chineseParser || 'zhparser'});
|
||||||
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
|
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l WITH simple;
|
||||||
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l WITH simple;
|
|
||||||
`;
|
`;
|
||||||
await this.connector.exec(createChineseConfigSql);
|
await this.connector.exec(createChineseConfigSql, txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 创建实体表
|
||||||
for (const entity in schema) {
|
for (const entity in schema) {
|
||||||
const sqls = this.translator.translateCreateEntity(entity, option);
|
const sqls = this.translator.translateCreateEntity(entity, option);
|
||||||
for (const sql of sqls) {
|
for (const sql of sqls) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export type Plan = {
|
||||||
updatedIndexes: Record<string, Index<any>[]>;
|
updatedIndexes: Record<string, Index<any>[]>;
|
||||||
};
|
};
|
||||||
export interface DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends AsyncRowStore<ED, Cxt> {
|
export interface DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends AsyncRowStore<ED, Cxt> {
|
||||||
|
checkRelationAsync<T extends keyof ED, Cxt extends AsyncContext<ED>>(entity: T, operation: Omit<ED[T]['Operation'] | ED[T]['Selection'], 'id'>, context: Cxt): Promise<void>;
|
||||||
connect: () => Promise<void>;
|
connect: () => Promise<void>;
|
||||||
disconnect: () => Promise<void>;
|
disconnect: () => Promise<void>;
|
||||||
initialize(options: CreateEntityOption): Promise<void>;
|
initialize(options: CreateEntityOption): Promise<void>;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "oak-db",
|
"name": "oak-db",
|
||||||
"version": "3.3.13",
|
"version": "3.3.14",
|
||||||
"description": "oak-db",
|
"description": "oak-db",
|
||||||
"main": "lib/index",
|
"main": "lib/index",
|
||||||
"author": {
|
"author": {
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"mysql2": "^2.3.3",
|
"mysql2": "^2.3.3",
|
||||||
"oak-domain": "^5.1.34",
|
"oak-domain": "file:../oak-domain",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -452,68 +452,85 @@ export class PostgreSQLStore<
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(option: CreateEntityOption) {
|
async initialize(option: CreateEntityOption) {
|
||||||
// PG的DDL支持事务,所以这里直接用一个事务包裹所有的初始化操作
|
const schema = this.getSchema();
|
||||||
|
|
||||||
|
// ===== 第一阶段:事务外创建扩展 =====
|
||||||
|
let hasGeoType = false;
|
||||||
|
let hasChineseTsConfig = false;
|
||||||
|
let chineseParser = null;
|
||||||
|
|
||||||
|
// 扫描 schema
|
||||||
|
for (const entity in schema) {
|
||||||
|
const { attributes, indexes } = schema[entity];
|
||||||
|
for (const attr in attributes) {
|
||||||
|
if (attributes[attr].type === 'geometry') {
|
||||||
|
hasGeoType = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const index of indexes || []) {
|
||||||
|
if (index.config?.tsConfig === 'chinese' || index.config?.tsConfig?.includes('chinese')) {
|
||||||
|
hasChineseTsConfig = true;
|
||||||
|
}
|
||||||
|
if (index.config?.chineseParser) {
|
||||||
|
assert(!chineseParser || chineseParser === index.config.chineseParser,
|
||||||
|
'当前定义了多个中文分词器,请保持一致');
|
||||||
|
chineseParser = index.config.chineseParser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在事务外创建扩展
|
||||||
|
if (hasGeoType) {
|
||||||
|
console.log('Initializing PostGIS extension for geometry support...');
|
||||||
|
await this.connector.exec('CREATE EXTENSION IF NOT EXISTS postgis;');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChineseTsConfig) {
|
||||||
|
console.log('Initializing Chinese parser extension...');
|
||||||
|
await this.connector.exec(`CREATE EXTENSION IF NOT EXISTS ${chineseParser || 'zhparser'};`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 第二阶段:事务内创建配置和表 =====
|
||||||
const txn = await this.connector.startTransaction({
|
const txn = await this.connector.startTransaction({
|
||||||
isolationLevel: 'serializable',
|
isolationLevel: 'serializable',
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 创建中文文本搜索配置
|
||||||
const schema = this.getSchema();
|
|
||||||
|
|
||||||
let hasGeoType = false;
|
|
||||||
let hasChineseTsConfig = false;
|
|
||||||
|
|
||||||
for (const entity in schema) {
|
|
||||||
const { attributes, indexes } = schema[entity];
|
|
||||||
for (const attr in attributes) {
|
|
||||||
const { type } = attributes[attr];
|
|
||||||
if (type === 'geometry') {
|
|
||||||
hasGeoType = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const index of indexes || []) {
|
|
||||||
if (index.config?.tsConfig === 'chinese' || index.config?.tsConfig?.includes('chinese')) {
|
|
||||||
hasChineseTsConfig = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasGeoType) {
|
|
||||||
console.log('Initializing PostGIS extension for geometry support...');
|
|
||||||
await this.connector.exec('CREATE EXTENSION IF NOT EXISTS postgis;');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasChineseTsConfig) {
|
if (hasChineseTsConfig) {
|
||||||
console.log('Initializing Chinese text search configuration...');
|
console.log('Initializing Chinese text search configuration...');
|
||||||
const checkChineseConfigSql = `
|
const checkChineseConfigSql = `
|
||||||
SELECT COUNT(*) as cnt
|
SELECT COUNT(*) as cnt
|
||||||
FROM pg_catalog.pg_ts_config
|
FROM pg_catalog.pg_ts_config
|
||||||
WHERE cfgname = 'chinese';
|
WHERE cfgname = 'chinese';
|
||||||
`;
|
`;
|
||||||
const result = await this.connector.exec(checkChineseConfigSql);
|
const result = await this.connector.exec(checkChineseConfigSql, txn);
|
||||||
const count = parseInt(result[0][0]?.cnt || '0', 10);
|
const count = parseInt(result[0][0]?.cnt || '0', 10);
|
||||||
|
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
const createChineseConfigSql = `
|
const createChineseConfigSql = `
|
||||||
CREATE EXTENSION IF NOT EXISTS zhparser;
|
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = ${chineseParser || 'zhparser'});
|
||||||
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
|
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l WITH simple;
|
||||||
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l WITH simple;
|
|
||||||
`;
|
`;
|
||||||
await this.connector.exec(createChineseConfigSql);
|
await this.connector.exec(createChineseConfigSql, txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建实体表
|
||||||
for (const entity in schema) {
|
for (const entity in schema) {
|
||||||
const sqls = this.translator.translateCreateEntity(entity, option);
|
const sqls = this.translator.translateCreateEntity(entity, option);
|
||||||
for (const sql of sqls) {
|
for (const sql of sqls) {
|
||||||
await this.connector.exec(sql, txn);
|
await this.connector.exec(sql, txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.connector.commitTransaction(txn);
|
await this.connector.commitTransaction(txn);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.connector.rollbackTransaction(txn);
|
await this.connector.rollbackTransaction(txn);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从数据库中读取当前schema
|
// 从数据库中读取当前schema
|
||||||
readSchema() {
|
readSchema() {
|
||||||
return this.translator.readSchema((sql) => this.connector.exec(sql));
|
return this.translator.readSchema((sql) => this.connector.exec(sql));
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export type Plan = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends AsyncRowStore<ED, Cxt> {
|
export interface DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends AsyncRowStore<ED, Cxt> {
|
||||||
|
checkRelationAsync<T extends keyof ED, Cxt extends AsyncContext<ED>>(entity: T, operation: Omit<ED[T]['Operation'] | ED[T]['Selection'], 'id'>, context: Cxt): Promise<void>;
|
||||||
connect: () => Promise<void>;
|
connect: () => Promise<void>;
|
||||||
disconnect: () => Promise<void>;
|
disconnect: () => Promise<void>;
|
||||||
initialize(options: CreateEntityOption): Promise<void>;
|
initialize(options: CreateEntityOption): Promise<void>;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue