Compare commits

..

2 Commits
3.3.13 ... dev

Author SHA1 Message Date
Pan Qiancheng c080b15078 fix: 完善DbStore类型,在init时自动创建扩展 2026-01-23 10:53:10 +08:00
Xu Chang ec085ddfd1 3.3.14-dev 2026-01-21 09:55:09 +08:00
5 changed files with 98 additions and 67 deletions

View File

@ -336,50 +336,62 @@ class PostgreSQLStore extends CascadeStore_1.CascadeStore {
await this.connector.disconnect();
}
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({
isolationLevel: 'serializable',
});
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) {
console.log('Initializing Chinese text search configuration...');
const checkChineseConfigSql = `
SELECT COUNT(*) as cnt
FROM pg_catalog.pg_ts_config
WHERE cfgname = 'chinese';
SELECT COUNT(*) as cnt
FROM pg_catalog.pg_ts_config
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);
if (count === 0) {
const createChineseConfigSql = `
CREATE EXTENSION IF NOT EXISTS zhparser;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l WITH simple;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = ${chineseParser || 'zhparser'});
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) {
const sqls = this.translator.translateCreateEntity(entity, option);
for (const sql of sqls) {

View File

@ -16,6 +16,7 @@ export type Plan = {
updatedIndexes: Record<string, Index<any>[]>;
};
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>;
disconnect: () => Promise<void>;
initialize(options: CreateEntityOption): Promise<void>;

View File

@ -1,6 +1,6 @@
{
"name": "oak-db",
"version": "3.3.13",
"version": "3.3.14",
"description": "oak-db",
"main": "lib/index",
"author": {
@ -18,7 +18,7 @@
"lodash": "^4.17.21",
"mysql": "^2.18.1",
"mysql2": "^2.3.3",
"oak-domain": "^5.1.34",
"oak-domain": "file:../oak-domain",
"pg": "^8.16.3",
"uuid": "^8.3.2"
},

View File

@ -452,68 +452,85 @@ export class PostgreSQLStore<
}
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({
isolationLevel: 'serializable',
});
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) {
console.log('Initializing Chinese text search configuration...');
const checkChineseConfigSql = `
SELECT COUNT(*) as cnt
FROM pg_catalog.pg_ts_config
WHERE cfgname = 'chinese';
SELECT COUNT(*) as cnt
FROM pg_catalog.pg_ts_config
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);
if (count === 0) {
const createChineseConfigSql = `
CREATE EXTENSION IF NOT EXISTS zhparser;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l WITH simple;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = ${chineseParser || 'zhparser'});
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) {
const sqls = this.translator.translateCreateEntity(entity, option);
for (const sql of sqls) {
await this.connector.exec(sql, txn);
}
}
await this.connector.commitTransaction(txn);
} catch (error) {
await this.connector.rollbackTransaction(txn);
throw error;
}
}
// 从数据库中读取当前schema
readSchema() {
return this.translator.readSchema((sql) => this.connector.exec(sql));

View File

@ -24,6 +24,7 @@ export type Plan = {
};
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>;
disconnect: () => Promise<void>;
initialize(options: CreateEntityOption): Promise<void>;