对SelectOption和OperateOption的优化

This commit is contained in:
Xu Chang 2022-08-10 11:21:06 +08:00
parent b9b9145705
commit 316d3b6458
15 changed files with 1311 additions and 829 deletions

15
lib/MySQL/connector.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
import mysql from 'mysql2';
import { TxnOption } from 'oak-domain/lib/types';
import { MySQLConfiguration } from './types/Configuration';
export declare class MySqlConnector {
pool?: mysql.Pool;
configuration: MySQLConfiguration;
txnDict: Record<string, mysql.PoolConnection>;
constructor(configuration: MySQLConfiguration);
connect(): void;
disconnect(): void;
startTransaction(option?: TxnOption): Promise<string>;
exec(sql: string, txn?: string): Promise<any>;
commitTransaction(txn: string): Promise<void>;
rollbackTransaction(txn: string): Promise<void>;
}

View File

@ -1,49 +1,84 @@
"use strict"; "use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) { var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.MySqlConnector = void 0; exports.MySqlConnector = void 0;
const mysql2_1 = __importDefault(require("mysql2")); var mysql2_1 = __importDefault(require("mysql2"));
const uuid_1 = require("uuid"); var uuid_1 = require("uuid");
const assert_1 = __importDefault(require("assert")); var assert_1 = __importDefault(require("assert"));
class MySqlConnector { var MySqlConnector = /** @class */ (function () {
pool; function MySqlConnector(configuration) {
configuration;
txnDict;
constructor(configuration) {
this.configuration = configuration; this.configuration = configuration;
this.txnDict = {}; this.txnDict = {};
} }
connect() { MySqlConnector.prototype.connect = function () {
this.pool = mysql2_1.default.createPool(this.configuration); this.pool = mysql2_1.default.createPool(this.configuration);
} };
disconnect() { MySqlConnector.prototype.disconnect = function () {
this.pool.end(); this.pool.end();
} };
startTransaction(option) { MySqlConnector.prototype.startTransaction = function (option) {
return new Promise((resolve, reject) => { var _this = this;
this.pool.getConnection((err, connection) => { return new Promise(function (resolve, reject) {
_this.pool.getConnection(function (err, connection) {
if (err) { if (err) {
return reject(err); return reject(err);
} }
const { isolationLevel } = option || {}; var isolationLevel = (option || {}).isolationLevel;
const startTxn = () => { var startTxn = function () {
let sql = 'START TRANSACTION;'; var sql = 'START TRANSACTION;';
connection.query(sql, (err2) => { connection.query(sql, function (err2) {
var _a;
if (err2) { if (err2) {
connection.release(); connection.release();
return reject(err2); return reject(err2);
} }
const id = (0, uuid_1.v4)(); var id = (0, uuid_1.v4)();
Object.assign(this.txnDict, { Object.assign(_this.txnDict, (_a = {},
[id]: connection, _a[id] = connection,
}); _a));
resolve(id); resolve(id);
}); });
}; };
if (isolationLevel) { if (isolationLevel) {
connection.query(`SET TRANSACTION ISOLATION LEVEL ${isolationLevel};`, (err2) => { connection.query("SET TRANSACTION ISOLATION LEVEL ".concat(isolationLevel, ";"), function (err2) {
if (err2) { if (err2) {
connection.release(); connection.release();
return reject(err2); return reject(err2);
@ -56,44 +91,51 @@ class MySqlConnector {
} }
}); });
}); });
} };
async exec(sql, txn) { MySqlConnector.prototype.exec = function (sql, txn) {
if (process.env.NODE_ENV === 'development') { return __awaiter(this, void 0, void 0, function () {
console.log(sql); var connection_1;
} var _this = this;
if (txn) { return __generator(this, function (_a) {
const connection = this.txnDict[txn]; if (process.env.NODE_ENV === 'development') {
(0, assert_1.default)(connection); console.log(sql);
return new Promise((resolve, reject) => { }
connection.query(sql, (err, result) => { if (txn) {
if (err) { connection_1 = this.txnDict[txn];
console.error(`sql exec err: ${sql}`, err); (0, assert_1.default)(connection_1);
return reject(err); return [2 /*return*/, new Promise(function (resolve, reject) {
} connection_1.query(sql, function (err, result) {
resolve(result); if (err) {
}); console.error("sql exec err: ".concat(sql), err);
return reject(err);
}
resolve(result);
});
})];
}
else {
return [2 /*return*/, new Promise(function (resolve, reject) {
// if (process.env.DEBUG) {
// console.log(sql);
//}
_this.pool.query(sql, function (err, result) {
if (err) {
console.error("sql exec err: ".concat(sql), err);
return reject(err);
}
resolve(result);
});
})];
}
return [2 /*return*/];
}); });
} });
else { };
return new Promise((resolve, reject) => { MySqlConnector.prototype.commitTransaction = function (txn) {
// if (process.env.DEBUG) { var connection = this.txnDict[txn];
// console.log(sql);
//}
this.pool.query(sql, (err, result) => {
if (err) {
console.error(`sql exec err: ${sql}`, err);
return reject(err);
}
resolve(result);
});
});
}
}
commitTransaction(txn) {
const connection = this.txnDict[txn];
(0, assert_1.default)(connection); (0, assert_1.default)(connection);
return new Promise((resolve, reject) => { return new Promise(function (resolve, reject) {
connection.query('COMMIT;', (err) => { connection.query('COMMIT;', function (err) {
if (err) { if (err) {
return reject(err); return reject(err);
} }
@ -101,12 +143,12 @@ class MySqlConnector {
resolve(); resolve();
}); });
}); });
} };
rollbackTransaction(txn) { MySqlConnector.prototype.rollbackTransaction = function (txn) {
const connection = this.txnDict[txn]; var connection = this.txnDict[txn];
(0, assert_1.default)(connection); (0, assert_1.default)(connection);
return new Promise((resolve, reject) => { return new Promise(function (resolve, reject) {
connection.query('ROLLBACK;', (err) => { connection.query('ROLLBACK;', function (err) {
if (err) { if (err) {
return reject(err); return reject(err);
} }
@ -114,6 +156,7 @@ class MySqlConnector {
resolve(); resolve();
}); });
}); });
} };
} return MySqlConnector;
}());
exports.MySqlConnector = MySqlConnector; exports.MySqlConnector = MySqlConnector;

24
lib/MySQL/store.d.ts vendored Normal file
View File

@ -0,0 +1,24 @@
import { EntityDict, Context, DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperateOption, OperationResult, SelectionResult, TxnOption, SelectRowShape, StorageSchema, DeduceCreateMultipleOperation, SelectOption } from 'oak-domain/lib/types';
import { CascadeStore } from 'oak-domain/lib/store/CascadeStore';
import { MySQLConfiguration } from './types/Configuration';
import { MySqlConnector } from './connector';
import { MySqlTranslator, MySqlSelectOption, MysqlOperateOption } from './translator';
export declare class MysqlStore<ED extends EntityDict, Cxt extends Context<ED>> extends CascadeStore<ED, Cxt> {
connector: MySqlConnector;
translator: MySqlTranslator<ED>;
constructor(storageSchema: StorageSchema<ED>, configuration: MySQLConfiguration);
protected supportManyToOneJoin(): boolean;
protected supportMultipleCreate(): boolean;
private formResult;
protected selectAbjointRow<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt, option?: MySqlSelectOption): Promise<SelectRowShape<ED[T]['Schema'], S['data']>[]>;
protected updateAbjointRow<T extends keyof ED>(entity: T, operation: DeduceCreateMultipleOperation<ED[T]['Schema']> | DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: MysqlOperateOption): Promise<number>;
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, params?: OperateOption): Promise<OperationResult<ED>>;
select<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt, option?: SelectOption): Promise<SelectionResult<ED[T]['Schema'], S['data']>>;
count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option?: SelectOption): Promise<number>;
begin(option?: TxnOption): Promise<string>;
commit(txnId: string): Promise<void>;
rollback(txnId: string): Promise<void>;
connect(): void;
disconnect(): void;
initialize(dropIfExists?: boolean): Promise<void>;
}

View File

@ -1,59 +1,111 @@
"use strict"; "use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) { var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.MysqlStore = void 0; exports.MysqlStore = void 0;
const CascadeStore_1 = require("oak-domain/lib/store/CascadeStore"); var CascadeStore_1 = require("oak-domain/lib/store/CascadeStore");
const connector_1 = require("./connector"); var connector_1 = require("./connector");
const translator_1 = require("./translator"); var translator_1 = require("./translator");
const lodash_1 = require("lodash"); var lodash_1 = require("lodash");
const assert_1 = __importDefault(require("assert")); var assert_1 = __importDefault(require("assert"));
const relation_1 = require("oak-domain/lib/store/relation"); var relation_1 = require("oak-domain/lib/store/relation");
function convertGeoTextToObject(geoText) { function convertGeoTextToObject(geoText) {
if (geoText.startsWith('POINT')) { if (geoText.startsWith('POINT')) {
const coord = geoText.match((/(\d|\.)+(?=\)|\s)/g)); var coord = geoText.match((/(\d|\.)+(?=\)|\s)/g));
return { return {
type: 'Point', type: 'Point',
coordinates: coord.map(ele => parseFloat(ele)), coordinates: coord.map(function (ele) { return parseFloat(ele); }),
}; };
} }
else { else {
throw new Error('only support Point now'); throw new Error('only support Point now');
} }
} }
class MysqlStore extends CascadeStore_1.CascadeStore { var MysqlStore = /** @class */ (function (_super) {
connector; __extends(MysqlStore, _super);
translator; function MysqlStore(storageSchema, configuration) {
constructor(storageSchema, configuration) { var _this = _super.call(this, storageSchema) || this;
super(storageSchema); _this.connector = new connector_1.MySqlConnector(configuration);
this.connector = new connector_1.MySqlConnector(configuration); _this.translator = new translator_1.MySqlTranslator(storageSchema);
this.translator = new translator_1.MySqlTranslator(storageSchema); return _this;
} }
supportManyToOneJoin() { MysqlStore.prototype.supportManyToOneJoin = function () {
return true; return true;
} };
supportMultipleCreate() { MysqlStore.prototype.supportMultipleCreate = function () {
return true; return true;
} };
formResult(entity, result) { MysqlStore.prototype.formResult = function (entity, result) {
const schema = this.getSchema(); var schema = this.getSchema();
function resolveAttribute(entity2, r, attr, value) { function resolveAttribute(entity2, r, attr, value) {
const { attributes, view } = schema[entity2]; var _a;
var _b = schema[entity2], attributes = _b.attributes, view = _b.view;
if (!view) { if (!view) {
const i = attr.indexOf("."); var i = attr.indexOf(".");
if (i !== -1) { if (i !== -1) {
const attrHead = attr.slice(0, i); var attrHead = attr.slice(0, i);
const attrTail = attr.slice(i + 1); var attrTail = attr.slice(i + 1);
if (!r[attrHead]) { if (!r[attrHead]) {
r[attrHead] = {}; r[attrHead] = {};
} }
const rel = (0, relation_1.judgeRelation)(schema, entity2, attrHead); var rel = (0, relation_1.judgeRelation)(schema, entity2, attrHead);
(0, assert_1.default)(rel === 2 || typeof rel === 'string'); (0, assert_1.default)(rel === 2 || typeof rel === 'string');
resolveAttribute(typeof rel === 'string' ? rel : attrHead, r[attrHead], attrTail, value); resolveAttribute(typeof rel === 'string' ? rel : attrHead, r[attrHead], attrTail, value);
} }
else if (attributes[attr]) { else if (attributes[attr]) {
const { type } = attributes[attr]; var type = attributes[attr].type;
switch (type) { switch (type) {
case 'date': case 'date':
case 'time': { case 'time': {
@ -87,7 +139,7 @@ class MysqlStore extends CascadeStore_1.CascadeStore {
case 'function': { case 'function': {
if (typeof value === 'string') { if (typeof value === 'string') {
// 函数的执行环境需要的参数只有创建函数者知悉只能由上层再创建Function // 函数的执行环境需要的参数只有创建函数者知悉只能由上层再创建Function
r[attr] = `return ${Buffer.from(value, 'base64').toString()}`; r[attr] = "return ".concat(Buffer.from(value, 'base64').toString());
} }
else { else {
r[attr] = value; r[attr] = value;
@ -117,15 +169,15 @@ class MysqlStore extends CascadeStore_1.CascadeStore {
} }
} }
else { else {
(0, lodash_1.assign)(r, { (0, lodash_1.assign)(r, (_a = {},
[attr]: value, _a[attr] = value,
}); _a));
} }
} }
function formalizeNullObject(r, e) { function formalizeNullObject(r, e) {
const { attributes: a2 } = schema[e]; var a2 = schema[e].attributes;
let allowFormalize = true; var allowFormalize = true;
for (let attr in r) { for (var attr in r) {
if (typeof r[attr] === 'object' && a2[attr] && a2[attr].type === 'ref') { if (typeof r[attr] === 'object' && a2[attr] && a2[attr].type === 'ref') {
if (formalizeNullObject(r[attr], a2[attr].ref)) { if (formalizeNullObject(r[attr], a2[attr].ref)) {
r[attr] = null; r[attr] = null;
@ -141,106 +193,220 @@ class MysqlStore extends CascadeStore_1.CascadeStore {
return allowFormalize; return allowFormalize;
} }
function formSingleRow(r) { function formSingleRow(r) {
let result2 = {}; var result2 = {};
for (let attr in r) { for (var attr in r) {
const value = r[attr]; var value = r[attr];
resolveAttribute(entity, result2, attr, value); resolveAttribute(entity, result2, attr, value);
} }
formalizeNullObject(result2, entity); formalizeNullObject(result2, entity);
return result2; return result2;
} }
if (result instanceof Array) { if (result instanceof Array) {
return result.map(r => formSingleRow(r)); return result.map(function (r) { return formSingleRow(r); });
} }
return formSingleRow(result); return formSingleRow(result);
} };
async selectAbjointRow(entity, selection, context, params) { MysqlStore.prototype.selectAbjointRow = function (entity, selection, context, option) {
const sql = this.translator.translateSelect(entity, selection, params); return __awaiter(this, void 0, void 0, function () {
const result = await this.connector.exec(sql, context.getCurrentTxnId()); var sql, result;
return this.formResult(entity, result); return __generator(this, function (_a) {
} switch (_a.label) {
async updateAbjointRow(entity, operation, context, params) { case 0:
const { translator, connector } = this; sql = this.translator.translateSelect(entity, selection, option);
const { action } = operation; return [4 /*yield*/, this.connector.exec(sql, context.getCurrentTxnId())];
const txn = context.getCurrentTxnId(); case 1:
switch (action) { result = _a.sent();
case 'create': { return [2 /*return*/, this.formResult(entity, result)];
const { data } = operation; }
const sql = translator.translateInsert(entity, data instanceof Array ? data : [data]); });
await connector.exec(sql, txn); });
context.opRecords.push({ };
a: 'c', MysqlStore.prototype.updateAbjointRow = function (entity, operation, context, option) {
d: data, return __awaiter(this, void 0, void 0, function () {
e: entity, var _a, translator, connector, action, txn, _b, data, sql, sql, sql;
}); return __generator(this, function (_c) {
return data instanceof Array ? data.length : 1; switch (_c.label) {
} case 0:
case 'remove': { _a = this, translator = _a.translator, connector = _a.connector;
const sql = translator.translateRemove(entity, operation, params); action = operation.action;
await connector.exec(sql, txn); txn = context.getCurrentTxnId();
// todo 这里对sorter和indexfrom/count的支持不完整 _b = action;
context.opRecords.push({ switch (_b) {
a: 'r', case 'create': return [3 /*break*/, 1];
e: entity, case 'remove': return [3 /*break*/, 3];
f: operation.filter, }
}); return [3 /*break*/, 5];
return 1; case 1:
} data = operation.data;
default: { sql = translator.translateInsert(entity, data instanceof Array ? data : [data]);
(0, assert_1.default)(!['select', 'download', 'stat'].includes(action)); return [4 /*yield*/, connector.exec(sql, txn)];
const sql = translator.translateUpdate(entity, operation, params); case 2:
await connector.exec(sql, txn); _c.sent();
// todo 这里对sorter和indexfrom/count的支持不完整 if (!(option === null || option === void 0 ? void 0 : option.notCollect)) {
context.opRecords.push({ context.opRecords.push({
a: 'u', a: 'c',
e: entity, d: data,
d: operation.data, e: entity,
f: operation.filter, });
}); }
return 1; return [2 /*return*/, data instanceof Array ? data.length : 1];
} case 3:
} sql = translator.translateRemove(entity, operation, option);
} return [4 /*yield*/, connector.exec(sql, txn)];
async operate(entity, operation, context, params) { case 4:
const { action } = operation; _c.sent();
(0, assert_1.default)(!['select', 'download', 'stat'].includes(action), '现在不支持使用select operation'); // todo 这里对sorter和indexfrom/count的支持不完整
return await this.cascadeUpdate(entity, operation, context, params); if (!(option === null || option === void 0 ? void 0 : option.notCollect)) {
} context.opRecords.push({
async select(entity, selection, context, params) { a: 'r',
const result = await this.cascadeSelect(entity, selection, context, params); e: entity,
return { f: operation.filter,
result, });
}; }
} return [2 /*return*/, 1];
async count(entity, selection, context, params) { case 5:
const sql = this.translator.translateCount(entity, selection, params); (0, assert_1.default)(!['select', 'download', 'stat'].includes(action));
const result = await this.connector.exec(sql, context.getCurrentTxnId()); sql = translator.translateUpdate(entity, operation, option);
return result.count; return [4 /*yield*/, connector.exec(sql, txn)];
} case 6:
async begin(option) { _c.sent();
const txn = await this.connector.startTransaction(option); // todo 这里对sorter和indexfrom/count的支持不完整
return txn; if (!(option === null || option === void 0 ? void 0 : option.notCollect)) {
} context.opRecords.push({
async commit(txnId) { a: 'u',
await this.connector.commitTransaction(txnId); e: entity,
} d: operation.data,
async rollback(txnId) { f: operation.filter,
await this.connector.rollbackTransaction(txnId); });
} }
connect() { return [2 /*return*/, 1];
}
});
});
};
MysqlStore.prototype.operate = function (entity, operation, context, params) {
return __awaiter(this, void 0, void 0, function () {
var action;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
action = operation.action;
(0, assert_1.default)(!['select', 'download', 'stat'].includes(action), '现在不支持使用select operation');
return [4 /*yield*/, this.cascadeUpdate(entity, operation, context, params)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
MysqlStore.prototype.select = function (entity, selection, context, option) {
return __awaiter(this, void 0, void 0, function () {
var result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.cascadeSelect(entity, selection, context, option)];
case 1:
result = _a.sent();
return [2 /*return*/, {
result: result,
}];
}
});
});
};
MysqlStore.prototype.count = function (entity, selection, context, option) {
return __awaiter(this, void 0, void 0, function () {
var sql, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
sql = this.translator.translateCount(entity, selection, option);
return [4 /*yield*/, this.connector.exec(sql, context.getCurrentTxnId())];
case 1:
result = _a.sent();
return [2 /*return*/, result.count];
}
});
});
};
MysqlStore.prototype.begin = function (option) {
return __awaiter(this, void 0, void 0, function () {
var txn;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.connector.startTransaction(option)];
case 1:
txn = _a.sent();
return [2 /*return*/, txn];
}
});
});
};
MysqlStore.prototype.commit = function (txnId) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.connector.commitTransaction(txnId)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
MysqlStore.prototype.rollback = function (txnId) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.connector.rollbackTransaction(txnId)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
MysqlStore.prototype.connect = function () {
this.connector.connect(); this.connector.connect();
} };
disconnect() { MysqlStore.prototype.disconnect = function () {
this.connector.disconnect(); this.connector.disconnect();
} };
async initialize(dropIfExists) { MysqlStore.prototype.initialize = function (dropIfExists) {
const schema = this.getSchema(); return __awaiter(this, void 0, void 0, function () {
for (const entity in schema) { var schema, _a, _b, _i, entity, sqls, _c, sqls_1, sql;
const sqls = this.translator.translateCreateEntity(entity, { replace: dropIfExists }); return __generator(this, function (_d) {
for (const sql of sqls) { switch (_d.label) {
await this.connector.exec(sql); case 0:
} schema = this.getSchema();
} _a = [];
} for (_b in schema)
} _a.push(_b);
_i = 0;
_d.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 6];
entity = _a[_i];
sqls = this.translator.translateCreateEntity(entity, { replace: dropIfExists });
_c = 0, sqls_1 = sqls;
_d.label = 2;
case 2:
if (!(_c < sqls_1.length)) return [3 /*break*/, 5];
sql = sqls_1[_c];
return [4 /*yield*/, this.connector.exec(sql)];
case 3:
_d.sent();
_d.label = 4;
case 4:
_c++;
return [3 /*break*/, 2];
case 5:
_i++;
return [3 /*break*/, 1];
case 6: return [2 /*return*/];
}
});
});
};
return MysqlStore;
}(CascadeStore_1.CascadeStore));
exports.MysqlStore = MysqlStore; exports.MysqlStore = MysqlStore;

104
lib/MySQL/translator.d.ts vendored Normal file
View File

@ -0,0 +1,104 @@
import { EntityDict, Q_FullTextValue, RefOrExpression, Ref, StorageSchema } from "oak-domain/lib/types";
import { DataType } from "oak-domain/lib/types/schema/DataTypes";
import { SqlOperateOption, SqlSelectOption, SqlTranslator } from "../sqlTranslator";
export interface MySqlSelectOption extends SqlSelectOption {
}
export interface MysqlOperateOption extends SqlOperateOption {
}
export declare class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> {
protected getDefaultSelectFilter(alias: string, option?: MySqlSelectOption): string;
private makeUpSchema;
constructor(schema: StorageSchema<ED>);
static supportedDataTypes: DataType[];
static spatialTypes: DataType[];
static withLengthDataTypes: DataType[];
static withPrecisionDataTypes: DataType[];
static withScaleDataTypes: DataType[];
static unsignedAndZerofillTypes: DataType[];
static withWidthDataTypes: DataType[];
static dataTypeDefaults: {
varchar: {
length: number;
};
nvarchar: {
length: number;
};
"national varchar": {
length: number;
};
char: {
length: number;
};
binary: {
length: number;
};
varbinary: {
length: number;
};
decimal: {
precision: number;
scale: number;
};
dec: {
precision: number;
scale: number;
};
numeric: {
precision: number;
scale: number;
};
fixed: {
precision: number;
scale: number;
};
float: {
precision: number;
};
double: {
precision: number;
};
time: {
precision: number;
};
datetime: {
precision: number;
};
timestamp: {
precision: number;
};
bit: {
width: number;
};
int: {
width: number;
};
integer: {
width: number;
};
tinyint: {
width: number;
};
smallint: {
width: number;
};
mediumint: {
width: number;
};
bigint: {
width: number;
};
};
maxAliasLength: number;
private populateDataTypeDef;
protected translateAttrProjection(dataType: DataType, alias: string, attr: string): string;
protected translateAttrValue(dataType: DataType | Ref, value: any): string;
protected translateFullTextSearch<T extends keyof ED>(value: Q_FullTextValue, entity: T, alias: string): string;
translateCreateEntity<T extends keyof ED>(entity: T, options?: {
replace?: boolean;
}): string[];
private translateFnName;
protected translateExpression<T extends keyof ED>(alias: string, expression: RefOrExpression<keyof ED[T]["OpSchema"]>, refDict: Record<string, string>): string;
protected populateSelectStmt<T extends keyof ED>(projectionText: string, fromText: string, selection: ED[T]['Selection'], aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: MySqlSelectOption): string;
protected populateUpdateStmt(updateText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: MysqlOperateOption): string;
protected populateRemoveStmt(removeText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: MysqlOperateOption): string;
}

View File

@ -1,15 +1,30 @@
"use strict"; "use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) { var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.MySqlTranslator = void 0; exports.MySqlTranslator = void 0;
const assert_1 = __importDefault(require("assert")); var assert_1 = __importDefault(require("assert"));
const util_1 = require("util"); var util_1 = require("util");
const lodash_1 = require("lodash"); var lodash_1 = require("lodash");
const luxon_1 = require("luxon"); var luxon_1 = require("luxon");
const sqlTranslator_1 = require("../sqlTranslator"); var sqlTranslator_1 = require("../sqlTranslator");
const GeoTypes = [ var GeoTypes = [
{ {
type: 'point', type: 'point',
name: "Point" name: "Point"
@ -42,54 +57,65 @@ const GeoTypes = [
]; ];
function transformGeoData(data) { function transformGeoData(data) {
if (data instanceof Array) { if (data instanceof Array) {
const element = data[0]; var element_1 = data[0];
if (element instanceof Array) { if (element_1 instanceof Array) {
return ` GeometryCollection(${data.map(ele => transformGeoData(ele)).join(',')})`; return " GeometryCollection(".concat(data.map(function (ele) { return transformGeoData(ele); }).join(','), ")");
} }
else { else {
const geoType = GeoTypes.find(ele => ele.type === element.type); var geoType_1 = GeoTypes.find(function (ele) { return ele.type === element_1.type; });
if (!geoType) { if (!geoType_1) {
throw new Error(`${element.type} is not supported in MySQL`); throw new Error("".concat(element_1.type, " is not supported in MySQL"));
} }
const multiGeoType = GeoTypes.find(ele => ele.element === geoType.type && ele.multiple); var multiGeoType = GeoTypes.find(function (ele) { return ele.element === geoType_1.type && ele.multiple; });
return ` ${multiGeoType.name}(${data.map(ele => transformGeoData(ele)).join(',')})`; return " ".concat(multiGeoType.name, "(").concat(data.map(function (ele) { return transformGeoData(ele); }).join(','), ")");
} }
} }
else { else {
const { type, coordinate } = data; var type_1 = data.type, coordinate = data.coordinate;
const geoType = GeoTypes.find(ele => ele.type === type); var geoType = GeoTypes.find(function (ele) { return ele.type === type_1; });
if (!geoType) { if (!geoType) {
throw new Error(`${data.type} is not supported in MySQL`); throw new Error("".concat(data.type, " is not supported in MySQL"));
} }
const { element, name } = geoType; var element_2 = geoType.element, name_1 = geoType.name;
if (!element) { if (!element_2) {
// Point // Point
return ` ${name}(${coordinate.join(',')})`; return " ".concat(name_1, "(").concat(coordinate.join(','), ")");
} }
// Polygon or Linestring // Polygon or Linestring
return ` ${name}(${coordinate.map((ele) => transformGeoData({ return " ".concat(name_1, "(").concat(coordinate.map(function (ele) { return transformGeoData({
type: element, type: element_2,
coordinate: ele, coordinate: ele,
}))})`; }); }), ")");
} }
} }
class MySqlTranslator extends sqlTranslator_1.SqlTranslator { var MySqlTranslator = /** @class */ (function (_super) {
getDefaultSelectFilter(alias, hint) { __extends(MySqlTranslator, _super);
if (hint?.includeDeleted) { function MySqlTranslator(schema) {
var _this = _super.call(this, schema) || this;
_this.maxAliasLength = 63;
// MySQL为geometry属性默认创建索引
_this.makeUpSchema();
return _this;
}
MySqlTranslator.prototype.getDefaultSelectFilter = function (alias, option) {
if (option === null || option === void 0 ? void 0 : option.includedDeleted) {
return ''; return '';
} }
return ` \`${alias}\`.\`$$deleteAt$$\` is null`; return " `".concat(alias, "`.`$$deleteAt$$` is null");
} };
modifySchema() { MySqlTranslator.prototype.makeUpSchema = function () {
for (const entity in this.schema) { for (var entity in this.schema) {
const { attributes, indexes } = this.schema[entity]; var _a = this.schema[entity], attributes = _a.attributes, indexes = _a.indexes;
const geoIndexes = []; var geoIndexes = [];
for (const attr in attributes) { var _loop_1 = function (attr) {
if (attributes[attr].type === 'geometry') { if (attributes[attr].type === 'geometry') {
const geoIndex = indexes?.find((idx) => idx.config?.type === 'spatial' && idx.attributes.find((attrDef) => attrDef.name === attr)); var geoIndex = indexes === null || indexes === void 0 ? void 0 : indexes.find(function (idx) {
var _a;
return ((_a = idx.config) === null || _a === void 0 ? void 0 : _a.type) === 'spatial' && idx.attributes.find(function (attrDef) { return attrDef.name === attr; });
});
if (!geoIndex) { if (!geoIndex) {
geoIndexes.push({ geoIndexes.push({
name: `${entity}_geo_${attr}`, name: "".concat(entity, "_geo_").concat(attr),
attributes: [{ attributes: [{
name: attr, name: attr,
}], }],
@ -99,10 +125,13 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
}); });
} }
} }
};
for (var attr in attributes) {
_loop_1(attr);
} }
if (geoIndexes.length > 0) { if (geoIndexes.length > 0) {
if (indexes) { if (indexes) {
indexes.push(...geoIndexes); indexes.push.apply(indexes, geoIndexes);
} }
else { else {
(0, lodash_1.assign)(this.schema[entity], { (0, lodash_1.assign)(this.schema[entity], {
@ -111,182 +140,37 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
} }
} }
} }
}
constructor(schema) {
super(schema);
// MySQL为geometry属性默认创建索引
this.modifySchema();
}
static supportedDataTypes = [
// numeric types
"bit",
"int",
"integer",
"tinyint",
"smallint",
"mediumint",
"bigint",
"float",
"double",
"double precision",
"real",
"decimal",
"dec",
"numeric",
"fixed",
"bool",
"boolean",
// date and time types
"date",
"datetime",
"timestamp",
"time",
"year",
// string types
"char",
"nchar",
"national char",
"varchar",
"nvarchar",
"national varchar",
"blob",
"text",
"tinyblob",
"tinytext",
"mediumblob",
"mediumtext",
"longblob",
"longtext",
"enum",
"set",
"binary",
"varbinary",
// json data type
"json",
// spatial data types
"geometry",
"point",
"linestring",
"polygon",
"multipoint",
"multilinestring",
"multipolygon",
"geometrycollection"
];
static spatialTypes = [
"geometry",
"point",
"linestring",
"polygon",
"multipoint",
"multilinestring",
"multipolygon",
"geometrycollection"
];
static withLengthDataTypes = [
"char",
"varchar",
"nvarchar",
"binary",
"varbinary"
];
static withPrecisionDataTypes = [
"decimal",
"dec",
"numeric",
"fixed",
"float",
"double",
"double precision",
"real",
"time",
"datetime",
"timestamp"
];
static withScaleDataTypes = [
"decimal",
"dec",
"numeric",
"fixed",
"float",
"double",
"double precision",
"real"
];
static unsignedAndZerofillTypes = [
"int",
"integer",
"smallint",
"tinyint",
"mediumint",
"bigint",
"decimal",
"dec",
"numeric",
"fixed",
"float",
"double",
"double precision",
"real"
];
static withWidthDataTypes = [
'int',
];
static dataTypeDefaults = {
"varchar": { length: 255 },
"nvarchar": { length: 255 },
"national varchar": { length: 255 },
"char": { length: 1 },
"binary": { length: 1 },
"varbinary": { length: 255 },
"decimal": { precision: 10, scale: 0 },
"dec": { precision: 10, scale: 0 },
"numeric": { precision: 10, scale: 0 },
"fixed": { precision: 10, scale: 0 },
"float": { precision: 12 },
"double": { precision: 22 },
"time": { precision: 0 },
"datetime": { precision: 0 },
"timestamp": { precision: 0 },
"bit": { width: 1 },
"int": { width: 11 },
"integer": { width: 11 },
"tinyint": { width: 4 },
"smallint": { width: 6 },
"mediumint": { width: 9 },
"bigint": { width: 20 }
}; };
maxAliasLength = 63; MySqlTranslator.prototype.populateDataTypeDef = function (type, params) {
populateDataTypeDef(type, params) {
if (MySqlTranslator.withLengthDataTypes.includes(type)) { if (MySqlTranslator.withLengthDataTypes.includes(type)) {
if (params) { if (params) {
const { length } = params; var length_1 = params.length;
return `${type}(${length}) `; return "".concat(type, "(").concat(length_1, ") ");
} }
else { else {
const { length } = MySqlTranslator.dataTypeDefaults[type]; var length_2 = MySqlTranslator.dataTypeDefaults[type].length;
return `${type}(${length}) `; return "".concat(type, "(").concat(length_2, ") ");
} }
} }
if (MySqlTranslator.withPrecisionDataTypes.includes(type)) { if (MySqlTranslator.withPrecisionDataTypes.includes(type)) {
if (params) { if (params) {
const { precision, scale } = params; var precision = params.precision, scale = params.scale;
if (typeof scale === 'number') { if (typeof scale === 'number') {
return `${type}(${precision}, ${scale}) `; return "".concat(type, "(").concat(precision, ", ").concat(scale, ") ");
} }
return `${type}(${precision})`; return "".concat(type, "(").concat(precision, ")");
} }
else { else {
const { precision, scale } = MySqlTranslator.dataTypeDefaults[type]; var _a = MySqlTranslator.dataTypeDefaults[type], precision = _a.precision, scale = _a.scale;
if (typeof scale === 'number') { if (typeof scale === 'number') {
return `${type}(${precision}, ${scale}) `; return "".concat(type, "(").concat(precision, ", ").concat(scale, ") ");
} }
return `${type}(${precision})`; return "".concat(type, "(").concat(precision, ")");
} }
} }
if (MySqlTranslator.withWidthDataTypes.includes(type)) { if (MySqlTranslator.withWidthDataTypes.includes(type)) {
(0, assert_1.default)(type === 'int'); (0, assert_1.default)(type === 'int');
const { width } = params; var width = params.width;
switch (width) { switch (width) {
case 1: { case 1: {
return 'tinyint'; return 'tinyint';
@ -317,19 +201,19 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
if (type === 'ref') { if (type === 'ref') {
return 'char(36)'; return 'char(36)';
} }
return `${type} `; return "".concat(type, " ");
} };
translateAttrProjection(dataType, alias, attr) { MySqlTranslator.prototype.translateAttrProjection = function (dataType, alias, attr) {
switch (dataType) { switch (dataType) {
case 'geometry': { case 'geometry': {
return ` st_astext(\`${alias}\`.\`${attr}\`)`; return " st_astext(`".concat(alias, "`.`").concat(attr, "`)");
} }
default: { default: {
return ` \`${alias}\`.\`${attr}\``; return " `".concat(alias, "`.`").concat(attr, "`");
} }
} }
} };
translateAttrValue(dataType, value) { MySqlTranslator.prototype.translateAttrValue = function (dataType, value) {
if (value === null) { if (value === null) {
return 'null'; return 'null';
} }
@ -360,32 +244,36 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return value; return value;
} }
} }
} };
translateFullTextSearch(value, entity, alias) { MySqlTranslator.prototype.translateFullTextSearch = function (value, entity, alias) {
const { $search } = value; var $search = value.$search;
const { indexes } = this.schema[entity]; var indexes = this.schema[entity].indexes;
const ftIndex = indexes && indexes.find((ele) => { var ftIndex = indexes && indexes.find(function (ele) {
const { config } = ele; var config = ele.config;
return config && config.type === 'fulltext'; return config && config.type === 'fulltext';
}); });
(0, assert_1.default)(ftIndex); (0, assert_1.default)(ftIndex);
const { attributes } = ftIndex; var attributes = ftIndex.attributes;
const columns2 = attributes.map(({ name }) => `${alias}.${name}`); var columns2 = attributes.map(function (_a) {
return ` match(${columns2.join(',')}) against ('${$search}' in natural language mode)`; var name = _a.name;
} return "".concat(alias, ".").concat(name);
translateCreateEntity(entity, options) { });
const replace = options?.replace; return " match(".concat(columns2.join(','), ") against ('").concat($search, "' in natural language mode)");
const { schema } = this; };
const entityDef = schema[entity]; MySqlTranslator.prototype.translateCreateEntity = function (entity, options) {
const { storageName, attributes, indexes, view } = entityDef; var _this = this;
var replace = options === null || options === void 0 ? void 0 : options.replace;
var schema = this.schema;
var entityDef = schema[entity];
var storageName = entityDef.storageName, attributes = entityDef.attributes, indexes = entityDef.indexes, view = entityDef.view;
// todo view暂还不支持 // todo view暂还不支持
const entityType = view ? 'view' : 'table'; var entityType = view ? 'view' : 'table';
let sql = `create ${entityType} `; var sql = "create ".concat(entityType, " ");
if (storageName) { if (storageName) {
sql += `\`${storageName}\` `; sql += "`".concat(storageName, "` ");
} }
else { else {
sql += `\`${entity}\` `; sql += "`".concat(entity, "` ");
} }
if (view) { if (view) {
throw new Error(' view unsupported yet'); throw new Error(' view unsupported yet');
@ -393,11 +281,11 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
else { else {
sql += '('; sql += '(';
// 翻译所有的属性 // 翻译所有的属性
Object.keys(attributes).forEach((attr, idx) => { Object.keys(attributes).forEach(function (attr, idx) {
const attrDef = attributes[attr]; var attrDef = attributes[attr];
const { type, params, default: defaultValue, unique, notNull, } = attrDef; var type = attrDef.type, params = attrDef.params, defaultValue = attrDef.default, unique = attrDef.unique, notNull = attrDef.notNull;
sql += `\`${attr}\` `; sql += "`".concat(attr, "` ");
sql += this.populateDataTypeDef(type, params); sql += _this.populateDataTypeDef(type, params);
if (notNull || type === 'geometry') { if (notNull || type === 'geometry') {
sql += ' not null '; sql += ' not null ';
} }
@ -406,7 +294,7 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
} }
if (defaultValue !== undefined) { if (defaultValue !== undefined) {
(0, assert_1.default)(type !== 'ref'); (0, assert_1.default)(type !== 'ref');
sql += ` default ${this.translateAttrValue(type, defaultValue)}`; sql += " default ".concat(_this.translateAttrValue(type, defaultValue));
} }
if (attr === 'id') { if (attr === 'id') {
sql += ' primary key'; sql += ' primary key';
@ -418,8 +306,9 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
// 翻译索引信息 // 翻译索引信息
if (indexes) { if (indexes) {
sql += ',\n'; sql += ',\n';
indexes.forEach(({ name, attributes, config }, idx) => { indexes.forEach(function (_a, idx) {
const { unique, type, parser } = config || {}; var name = _a.name, attributes = _a.attributes, config = _a.config;
var _b = config || {}, unique = _b.unique, type = _b.type, parser = _b.parser;
if (unique) { if (unique) {
sql += ' unique '; sql += ' unique ';
} }
@ -429,19 +318,20 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
else if (type === 'spatial') { else if (type === 'spatial') {
sql += ' spatial '; sql += ' spatial ';
} }
sql += `index ${name} `; sql += "index ".concat(name, " ");
if (type === 'hash') { if (type === 'hash') {
sql += ` using hash `; sql += " using hash ";
} }
sql += '('; sql += '(';
let includeDeleteAt = false; var includeDeleteAt = false;
attributes.forEach(({ name, size, direction }, idx2) => { attributes.forEach(function (_a, idx2) {
sql += `\`${name}\``; var name = _a.name, size = _a.size, direction = _a.direction;
sql += "`".concat(name, "`");
if (size) { if (size) {
sql += ` (${size})`; sql += " (".concat(size, ")");
} }
if (direction) { if (direction) {
sql += ` ${direction}`; sql += " ".concat(direction);
} }
if (idx2 < attributes.length - 1) { if (idx2 < attributes.length - 1) {
sql += ','; sql += ',';
@ -455,7 +345,7 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
} }
sql += ')'; sql += ')';
if (parser) { if (parser) {
sql += ` with parser ${parser}`; sql += " with parser ".concat(parser);
} }
if (idx < indexes.length - 1) { if (idx < indexes.length - 1) {
sql += ',\n'; sql += ',\n';
@ -467,9 +357,9 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
if (!replace) { if (!replace) {
return [sql]; return [sql];
} }
return [`drop ${entityType} \`${storageName || entity}\`;`, sql]; return ["drop ".concat(entityType, " if exists `").concat(storageName || entity, "`;"), sql];
} };
translateFnName(fnName, argumentNumber) { MySqlTranslator.prototype.translateFnName = function (fnName, argumentNumber) {
switch (fnName) { switch (fnName) {
case '$add': { case '$add': {
return '%s + %s'; return '%s + %s';
@ -532,8 +422,8 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return '%s = false'; return '%s = false';
} }
case '$and': { case '$and': {
let result = ''; var result = '';
for (let iter = 0; iter < argumentNumber; iter++) { for (var iter = 0; iter < argumentNumber; iter++) {
result += '%s'; result += '%s';
if (iter < argumentNumber - 1) { if (iter < argumentNumber - 1) {
result += ' and '; result += ' and ';
@ -542,8 +432,8 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return result; return result;
} }
case '$or': { case '$or': {
let result = ''; var result = '';
for (let iter = 0; iter < argumentNumber; iter++) { for (var iter = 0; iter < argumentNumber; iter++) {
result += '%s'; result += '%s';
if (iter < argumentNumber - 1) { if (iter < argumentNumber - 1) {
result += ' or '; result += ' or ';
@ -588,43 +478,44 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return 'ST_DISTANCE(%s, %s)'; return 'ST_DISTANCE(%s, %s)';
} }
default: { default: {
throw new Error(`unrecoganized function ${fnName}`); throw new Error("unrecoganized function ".concat(fnName));
} }
} }
} };
translateExpression(alias, expression, refDict) { MySqlTranslator.prototype.translateExpression = function (alias, expression, refDict) {
const translateConstant = (constant) => { var _this = this;
var translateConstant = function (constant) {
if (typeof constant === 'string') { if (typeof constant === 'string') {
return `'${constant}'`; return "'".concat(constant, "'");
} }
else if (constant instanceof Date) { else if (constant instanceof Date) {
return `'${luxon_1.DateTime.fromJSDate(constant).toFormat('yyyy-LL-dd HH:mm:ss')}'`; return "'".concat(luxon_1.DateTime.fromJSDate(constant).toFormat('yyyy-LL-dd HH:mm:ss'), "'");
} }
else { else {
(0, assert_1.default)(typeof constant === 'number'); (0, assert_1.default)(typeof constant === 'number');
return `${constant}`; return "".concat(constant);
} }
}; };
const translateInner = (expr) => { var translateInner = function (expr) {
const k = Object.keys(expr); var k = Object.keys(expr);
let result; var result;
if (k.includes('#attr')) { if (k.includes('#attr')) {
const attrText = `\`${alias}\`.\`${(expr)['#attr']}\``; var attrText = "`".concat(alias, "`.`").concat((expr)['#attr'], "`");
result = attrText; result = attrText;
} }
else if (k.includes('#refId')) { else if (k.includes('#refId')) {
const refId = (expr)['#refId']; var refId = (expr)['#refId'];
const refAttr = (expr)['#refAttr']; var refAttr = (expr)['#refAttr'];
(0, assert_1.default)(refDict[refId]); (0, assert_1.default)(refDict[refId]);
const attrText = `\`${refDict[refId]}\`.\`${refAttr}\``; var attrText = "`".concat(refDict[refId], "`.`").concat(refAttr, "`");
result = attrText; result = attrText;
} }
else { else {
(0, assert_1.default)(k.length === 1); (0, assert_1.default)(k.length === 1);
if ((expr)[k[0]] instanceof Array) { if ((expr)[k[0]] instanceof Array) {
const fnName = this.translateFnName(k[0], (expr)[k[0]].length); var fnName = _this.translateFnName(k[0], (expr)[k[0]].length);
const args = [fnName]; var args = [fnName];
args.push(...(expr)[k[0]].map((ele) => { args.push.apply(args, (expr)[k[0]].map(function (ele) {
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) { if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
return translateConstant(ele); return translateConstant(ele);
} }
@ -635,9 +526,9 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
result = util_1.format.apply(null, args); result = util_1.format.apply(null, args);
} }
else { else {
const fnName = this.translateFnName(k[0], 1); var fnName = _this.translateFnName(k[0], 1);
const args = [fnName]; var args = [fnName];
const arg = (expr)[k[0]]; var arg = (expr)[k[0]];
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) { if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
args.push(translateConstant(arg)); args.push(translateConstant(arg));
} }
@ -650,59 +541,198 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return result; return result;
}; };
return translateInner(expression); return translateInner(expression);
} };
populateSelectStmt(projectionText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count) { MySqlTranslator.prototype.populateSelectStmt = function (projectionText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count, option) {
const { hint } = selection;
// todo hint of use index // todo hint of use index
let sql = `select ${projectionText} from ${fromText}`; var sql = "select ".concat(projectionText, " from ").concat(fromText);
if (filterText) { if (filterText) {
sql += ` where ${filterText}`; sql += " where ".concat(filterText);
} }
if (sorterText) { if (sorterText) {
sql += ` order by ${sorterText}`; sql += " order by ".concat(sorterText);
} }
if (typeof indexFrom === 'number') { if (typeof indexFrom === 'number') {
(0, assert_1.default)(typeof count === 'number'); (0, assert_1.default)(typeof count === 'number');
sql += ` limit ${indexFrom}, ${count}`; sql += " limit ".concat(indexFrom, ", ").concat(count);
} }
if (hint?.mysql?.forUpdate) { if (option === null || option === void 0 ? void 0 : option.forUpdate) {
sql += ' for update'; sql += ' for update';
} }
return sql; return sql;
} };
populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, params) { MySqlTranslator.prototype.populateUpdateStmt = function (updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option) {
// todo using index // todo using index
const alias = aliasDict['./']; var alias = aliasDict['./'];
const now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss'); var now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
let sql = `update ${fromText} set ${updateText ? `${updateText},` : ''} \`${alias}\`.\`$$updateAt$$\` = '${now}'`; var sql = "update ".concat(fromText, " set ").concat(updateText ? "".concat(updateText, ",") : '', " `").concat(alias, "`.`$$updateAt$$` = '").concat(now, "'");
if (filterText) { if (filterText) {
sql += ` where ${filterText}`; sql += " where ".concat(filterText);
} }
if (sorterText) { if (sorterText) {
sql += ` order by ${sorterText}`; sql += " order by ".concat(sorterText);
} }
if (typeof indexFrom === 'number') { if (typeof indexFrom === 'number') {
(0, assert_1.default)(typeof count === 'number'); (0, assert_1.default)(typeof count === 'number');
sql += ` limit ${indexFrom}, ${count}`; sql += " limit ".concat(indexFrom, ", ").concat(count);
} }
return sql; return sql;
} };
populateRemoveStmt(removeText, fromText, aliasDict, filterText, sorterText, indexFrom, count, params) { MySqlTranslator.prototype.populateRemoveStmt = function (removeText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option) {
// todo using index // todo using index
const alias = aliasDict['./']; var alias = aliasDict['./'];
const now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss'); var now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
let sql = `update ${fromText} set \`${alias}\`.\`$$deleteAt$$\` = '${now}'`; var sql = "update ".concat(fromText, " set `").concat(alias, "`.`$$deleteAt$$` = '").concat(now, "'");
if (filterText) { if (filterText) {
sql += ` where ${filterText}`; sql += " where ".concat(filterText);
} }
if (sorterText) { if (sorterText) {
sql += ` order by ${sorterText}`; sql += " order by ".concat(sorterText);
} }
if (typeof indexFrom === 'number') { if (typeof indexFrom === 'number') {
(0, assert_1.default)(typeof count === 'number'); (0, assert_1.default)(typeof count === 'number');
sql += ` limit ${indexFrom}, ${count}`; sql += " limit ".concat(indexFrom, ", ").concat(count);
} }
return sql; return sql;
} };
} MySqlTranslator.supportedDataTypes = [
// numeric types
"bit",
"int",
"integer",
"tinyint",
"smallint",
"mediumint",
"bigint",
"float",
"double",
"double precision",
"real",
"decimal",
"dec",
"numeric",
"fixed",
"bool",
"boolean",
// date and time types
"date",
"datetime",
"timestamp",
"time",
"year",
// string types
"char",
"nchar",
"national char",
"varchar",
"nvarchar",
"national varchar",
"blob",
"text",
"tinyblob",
"tinytext",
"mediumblob",
"mediumtext",
"longblob",
"longtext",
"enum",
"set",
"binary",
"varbinary",
// json data type
"json",
// spatial data types
"geometry",
"point",
"linestring",
"polygon",
"multipoint",
"multilinestring",
"multipolygon",
"geometrycollection"
];
MySqlTranslator.spatialTypes = [
"geometry",
"point",
"linestring",
"polygon",
"multipoint",
"multilinestring",
"multipolygon",
"geometrycollection"
];
MySqlTranslator.withLengthDataTypes = [
"char",
"varchar",
"nvarchar",
"binary",
"varbinary"
];
MySqlTranslator.withPrecisionDataTypes = [
"decimal",
"dec",
"numeric",
"fixed",
"float",
"double",
"double precision",
"real",
"time",
"datetime",
"timestamp"
];
MySqlTranslator.withScaleDataTypes = [
"decimal",
"dec",
"numeric",
"fixed",
"float",
"double",
"double precision",
"real"
];
MySqlTranslator.unsignedAndZerofillTypes = [
"int",
"integer",
"smallint",
"tinyint",
"mediumint",
"bigint",
"decimal",
"dec",
"numeric",
"fixed",
"float",
"double",
"double precision",
"real"
];
MySqlTranslator.withWidthDataTypes = [
'int',
];
MySqlTranslator.dataTypeDefaults = {
"varchar": { length: 255 },
"nvarchar": { length: 255 },
"national varchar": { length: 255 },
"char": { length: 1 },
"binary": { length: 1 },
"varbinary": { length: 255 },
"decimal": { precision: 10, scale: 0 },
"dec": { precision: 10, scale: 0 },
"numeric": { precision: 10, scale: 0 },
"fixed": { precision: 10, scale: 0 },
"float": { precision: 12 },
"double": { precision: 22 },
"time": { precision: 0 },
"datetime": { precision: 0 },
"timestamp": { precision: 0 },
"bit": { width: 1 },
"int": { width: 11 },
"integer": { width: 11 },
"tinyint": { width: 4 },
"smallint": { width: 6 },
"mediumint": { width: 9 },
"bigint": { width: 20 }
};
return MySqlTranslator;
}(sqlTranslator_1.SqlTranslator));
exports.MySqlTranslator = MySqlTranslator; exports.MySqlTranslator = MySqlTranslator;

11
lib/MySQL/types/Configuration.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
export declare type MySQLConfiguration = {
host: string;
user: string;
password: string;
database: string;
charset: 'utf8mb4_general_ci';
connectionLimit: number;
};
export declare type Configuration = {
mysql: MySQLConfiguration;
};

1
lib/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from './MySQL/store';

51
lib/sqlTranslator.d.ts vendored Normal file
View File

@ -0,0 +1,51 @@
import { DeduceCreateOperationData, EntityDict, OperateOption, Q_FullTextValue, Ref, RefOrExpression, SelectOption, StorageSchema } from "oak-domain/lib/types";
import { DataType } from "oak-domain/lib/types/schema/DataTypes";
export interface SqlSelectOption extends SelectOption {
}
export interface SqlOperateOption extends OperateOption {
}
export declare abstract class SqlTranslator<ED extends EntityDict> {
readonly schema: StorageSchema<ED>;
constructor(schema: StorageSchema<ED>);
private makeFullSchema;
protected abstract getDefaultSelectFilter<OP extends SqlSelectOption>(alias: string, option?: OP): string;
protected abstract translateAttrProjection(dataType: DataType, alias: string, attr: string): string;
protected abstract translateAttrValue(dataType: DataType | Ref, value: any): string;
protected abstract translateFullTextSearch<T extends keyof ED>(value: Q_FullTextValue, entity: T, alias: string): string;
abstract translateCreateEntity<T extends keyof ED>(entity: T, option: {
replace?: boolean;
}): string[];
protected abstract populateSelectStmt<T extends keyof ED, OP extends SqlSelectOption>(projectionText: string, fromText: string, selection: ED[T]['Selection'], aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: OP): string;
protected abstract populateUpdateStmt<OP extends SqlOperateOption>(updateText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: OP): string;
protected abstract populateRemoveStmt<OP extends SqlOperateOption>(removeText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: OP): string;
protected abstract translateExpression<T extends keyof ED>(alias: string, expression: RefOrExpression<keyof ED[T]['OpSchema']>, refDict: Record<string, string>): string;
private getStorageName;
translateInsert<T extends keyof ED>(entity: T, data: DeduceCreateOperationData<ED[T]['OpSchema']>[]): string;
/**
* analyze the join relations in projection/query/sort
* left join处理
* {
* b: {
* name: {
* $exists: false,
* }
* }
* }
* query会把内表为空的行也返回
* @param param0
*/
private analyzeJoin;
private translateComparison;
private translateElement;
private translateEvaluation;
private translateFilter;
private translateSorter;
private translateProjection;
private translateSelectInner;
translateSelect<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: ED[T]['Selection'], option?: OP): string;
translateCount<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: Pick<ED[T]['Selection'], 'filter'>, option?: OP): string;
translateRemove<T extends keyof ED, OP extends SqlOperateOption>(entity: T, operation: ED[T]['Remove'], option?: OP): string;
translateUpdate<T extends keyof ED, OP extends SqlOperateOption>(entity: T, operation: ED[T]['Update'], option?: OP): string;
translateDestroyEntity(entity: string, truncate?: boolean): string;
escapeStringValue(value: string): string;
}

View File

@ -4,20 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.SqlTranslator = void 0; exports.SqlTranslator = void 0;
const assert_1 = __importDefault(require("assert")); var assert_1 = __importDefault(require("assert"));
const lodash_1 = require("lodash"); var lodash_1 = require("lodash");
const luxon_1 = require("luxon"); var luxon_1 = require("luxon");
const types_1 = require("oak-domain/lib/types"); var types_1 = require("oak-domain/lib/types");
const relation_1 = require("oak-domain/lib/store/relation"); var relation_1 = require("oak-domain/lib/store/relation");
class SqlTranslator { ;
schema; ;
constructor(schema) { var SqlTranslator = /** @class */ (function () {
function SqlTranslator(schema) {
this.schema = this.makeFullSchema(schema); this.schema = this.makeFullSchema(schema);
} }
makeFullSchema(schema2) { SqlTranslator.prototype.makeFullSchema = function (schema2) {
const schema = (0, lodash_1.cloneDeep)(schema2); var schema = (0, lodash_1.cloneDeep)(schema2);
for (const entity in schema) { for (var entity in schema) {
const { attributes, indexes } = schema[entity]; var _a = schema[entity], attributes = _a.attributes, indexes = _a.indexes;
// 增加默认的属性 // 增加默认的属性
(0, lodash_1.assign)(attributes, { (0, lodash_1.assign)(attributes, {
id: { id: {
@ -45,30 +46,29 @@ class SqlTranslator {
}, },
}); });
// 增加默认的索引 // 增加默认的索引
const intrinsticIndexes = [ var intrinsticIndexes = [
{ {
name: `${entity}_create_at`, name: "".concat(entity, "_create_at"),
attributes: [{ attributes: [{
name: '$$createAt$$', name: '$$createAt$$',
}] }]
}, { }, {
name: `${entity}_update_at`, name: "".concat(entity, "_update_at"),
attributes: [{ attributes: [{
name: '$$updateAt$$', name: '$$updateAt$$',
}], }],
}, { }, {
name: `${entity}_trigger_ts`, name: "".concat(entity, "_trigger_ts"),
attributes: [{ attributes: [{
name: '$$triggerTimestamp$$', name: '$$triggerTimestamp$$',
}], }],
} }
]; ];
// 增加外键上的索引 var _loop_1 = function (attr) {
for (const attr in attributes) {
if (attributes[attr].type === 'ref') { if (attributes[attr].type === 'ref') {
if (!(indexes?.find(ele => ele.attributes[0].name === attr))) { if (!(indexes === null || indexes === void 0 ? void 0 : indexes.find(function (ele) { return ele.attributes[0].name === attr; }))) {
intrinsticIndexes.push({ intrinsticIndexes.push({
name: `${entity}_fk_${attr}`, name: "".concat(entity, "_fk_").concat(attr),
attributes: [{ attributes: [{
name: attr, name: attr,
}] }]
@ -76,11 +76,11 @@ class SqlTranslator {
} }
} }
if (attr === 'entity' && attributes[attr].type === 'varchar') { if (attr === 'entity' && attributes[attr].type === 'varchar') {
const entityIdDef = attributes.entityId; var entityIdDef = attributes.entityId;
if (entityIdDef?.type === 'varchar') { if ((entityIdDef === null || entityIdDef === void 0 ? void 0 : entityIdDef.type) === 'varchar') {
if (!(indexes?.find(ele => ele.attributes[0].name === 'entity' && ele.attributes[1]?.name === 'entityId'))) { if (!(indexes === null || indexes === void 0 ? void 0 : indexes.find(function (ele) { var _a; return ele.attributes[0].name === 'entity' && ((_a = ele.attributes[1]) === null || _a === void 0 ? void 0 : _a.name) === 'entityId'; }))) {
intrinsticIndexes.push({ intrinsticIndexes.push({
name: `${entity}_fk_entity_entityId`, name: "".concat(entity, "_fk_entity_entityId"),
attributes: [{ attributes: [{
name: 'entity', name: 'entity',
}, { }, {
@ -90,9 +90,13 @@ class SqlTranslator {
} }
} }
} }
};
// 增加外键上的索引
for (var attr in attributes) {
_loop_1(attr);
} }
if (indexes) { if (indexes) {
indexes.push(...intrinsticIndexes); indexes.push.apply(indexes, intrinsticIndexes);
} }
else { else {
(0, lodash_1.assign)(schema[entity], { (0, lodash_1.assign)(schema[entity], {
@ -101,42 +105,43 @@ class SqlTranslator {
} }
} }
return schema; return schema;
} };
getStorageName(entity) { SqlTranslator.prototype.getStorageName = function (entity) {
const { storageName } = this.schema[entity]; var storageName = this.schema[entity].storageName;
return (storageName || entity); return (storageName || entity);
} };
translateInsert(entity, data) { SqlTranslator.prototype.translateInsert = function (entity, data) {
const { schema } = this; var _this = this;
const { attributes, storageName = entity } = schema[entity]; var schema = this.schema;
let sql = `insert into \`${storageName}\`(`; var _a = schema[entity], attributes = _a.attributes, _b = _a.storageName, storageName = _b === void 0 ? entity : _b;
const attrs = Object.keys(data[0]).filter(ele => attributes.hasOwnProperty(ele)); var sql = "insert into `".concat(storageName, "`(");
attrs.forEach((attr, idx) => { var attrs = Object.keys(data[0]).filter(function (ele) { return attributes.hasOwnProperty(ele); });
sql += ` \`${attr}\``; attrs.forEach(function (attr, idx) {
sql += " `".concat(attr, "`");
if (idx < attrs.length - 1) { if (idx < attrs.length - 1) {
sql += ','; sql += ',';
} }
}); });
sql += ', `$$createAt$$`, `$$updateAt$$`) values '; sql += ', `$$createAt$$`, `$$updateAt$$`) values ';
const now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss'); var now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
data.forEach((d, dataIndex) => { data.forEach(function (d, dataIndex) {
sql += '('; sql += '(';
attrs.forEach((attr, attrIdx) => { attrs.forEach(function (attr, attrIdx) {
const attrDef = attributes[attr]; var attrDef = attributes[attr];
const { type: dataType } = attrDef; var dataType = attrDef.type;
const value = this.translateAttrValue(dataType, d[attr]); var value = _this.translateAttrValue(dataType, d[attr]);
sql += value; sql += value;
if (attrIdx < attrs.length - 1) { if (attrIdx < attrs.length - 1) {
sql += ','; sql += ',';
} }
}); });
sql += `, '${now}', '${now}')`; sql += ", '".concat(now, "', '").concat(now, "')");
if (dataIndex < data.length - 1) { if (dataIndex < data.length - 1) {
sql += ','; sql += ',';
} }
}); });
return sql; return sql;
} };
/** /**
* analyze the join relations in projection/query/sort * analyze the join relations in projection/query/sort
* 所有的层次关系都当成left join处理如果有内表为空的情况请手动处理 * 所有的层次关系都当成left join处理如果有内表为空的情况请手动处理
@ -150,38 +155,43 @@ class SqlTranslator {
* 这样的query会把内表为空的行也返回 * 这样的query会把内表为空的行也返回
* @param param0 * @param param0
*/ */
analyzeJoin(entity, { projection, filter, sorter, isStat }, initialNumber) { SqlTranslator.prototype.analyzeJoin = function (entity, _a, initialNumber) {
const { schema } = this; var _this = this;
let number = initialNumber || 1; var projection = _a.projection, filter = _a.filter, sorter = _a.sorter, isStat = _a.isStat;
const projectionRefAlias = {}; var schema = this.schema;
const filterRefAlias = {}; var number = initialNumber || 1;
let extraWhere = ''; var projectionRefAlias = {};
const alias = `${entity}_${number++}`; var filterRefAlias = {};
let from = ` \`${this.getStorageName(entity)}\` \`${alias}\` `; var extraWhere = '';
const aliasDict = { var alias = "".concat(entity, "_").concat(number++);
var from = " `".concat(this.getStorageName(entity), "` `").concat(alias, "` ");
var aliasDict = {
'./': alias, './': alias,
}; };
const analyzeFilterNode = ({ node, path, entityName, alias }) => { var analyzeFilterNode = function (_a) {
Object.keys(node).forEach((op) => { var _b;
var node = _a.node, path = _a.path, entityName = _a.entityName, alias = _a.alias;
Object.keys(node).forEach(function (op) {
var _a, _b;
if (['$and', '$or'].includes(op)) { if (['$and', '$or'].includes(op)) {
node[op].forEach((subNode) => analyzeFilterNode({ node[op].forEach(function (subNode) { return analyzeFilterNode({
node: subNode, node: subNode,
path, path: path,
entityName, entityName: entityName,
alias, alias: alias,
})); }); });
} }
else { else {
const rel = (0, relation_1.judgeRelation)(this.schema, entityName, op); var rel = (0, relation_1.judgeRelation)(_this.schema, entityName, op);
if (typeof rel === 'string') { if (typeof rel === 'string') {
let alias2; var alias2 = void 0;
const pathAttr = `${path}${op}/`; var pathAttr = "".concat(path).concat(op, "/");
if (!aliasDict.hasOwnProperty(pathAttr)) { if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${rel}_${number++}`; alias2 = "".concat(rel, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, { (0, lodash_1.assign)(aliasDict, (_a = {},
[pathAttr]: alias2, _a[pathAttr] = alias2,
}); _a));
from += ` left join \`${this.getStorageName(rel)}\` \`${alias2}\` on \`${alias}\`.\`${op}Id\` = \`${alias2}\`.\`id\``; from += " left join `".concat(_this.getStorageName(rel), "` `").concat(alias2, "` on `").concat(alias, "`.`").concat(op, "Id` = `").concat(alias2, "`.`id`");
} }
else { else {
alias2 = aliasDict[pathAttr]; alias2 = aliasDict[pathAttr];
@ -194,15 +204,15 @@ class SqlTranslator {
}); });
} }
else if (rel === 2) { else if (rel === 2) {
let alias2; var alias2 = void 0;
const pathAttr = `${path}${op}/`; var pathAttr = "".concat(path).concat(op, "/");
if (!aliasDict.hasOwnProperty(pathAttr)) { if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${op}_${number++}`; alias2 = "".concat(op, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, { (0, lodash_1.assign)(aliasDict, (_b = {},
[pathAttr]: alias2, _b[pathAttr] = alias2,
}); _b));
from += ` left join \`${this.getStorageName(op)}\` \`${alias2}\` on \`${alias}\`.\`entityId\` = \`${alias2}\`.\`id\``; from += " left join `".concat(_this.getStorageName(op), "` `").concat(alias2, "` on `").concat(alias, "`.`entityId` = `").concat(alias2, "`.`id`");
extraWhere += `\`${alias}\`.\`entity\` = '${op}'`; extraWhere += "`".concat(alias, "`.`entity` = '").concat(op, "'");
} }
else { else {
alias2 = aliasDict[pathAttr]; alias2 = aliasDict[pathAttr];
@ -222,9 +232,9 @@ class SqlTranslator {
}); });
if (node['#id']) { if (node['#id']) {
(0, assert_1.default)(!filterRefAlias[node['#id']]); (0, assert_1.default)(!filterRefAlias[node['#id']]);
(0, lodash_1.assign)(filterRefAlias, { (0, lodash_1.assign)(filterRefAlias, (_b = {},
[node['#id']]: alias, _b[node['#id']] = alias,
}); _b));
} }
}; };
if (filter) { if (filter) {
@ -232,21 +242,23 @@ class SqlTranslator {
node: filter, node: filter,
path: './', path: './',
entityName: entity, entityName: entity,
alias, alias: alias,
}); });
} }
const analyzeSortNode = ({ node, path, entityName, alias }) => { var analyzeSortNode = function (_a) {
const attr = (0, lodash_1.keys)(node)[0]; var _b, _c;
const rel = (0, relation_1.judgeRelation)(this.schema, entityName, attr); var node = _a.node, path = _a.path, entityName = _a.entityName, alias = _a.alias;
var attr = (0, lodash_1.keys)(node)[0];
var rel = (0, relation_1.judgeRelation)(_this.schema, entityName, attr);
if (typeof rel === 'string') { if (typeof rel === 'string') {
const pathAttr = `${path}${attr}/`; var pathAttr = "".concat(path).concat(attr, "/");
let alias2; var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) { if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${rel}_${number++}`; alias2 = "".concat(rel, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, { (0, lodash_1.assign)(aliasDict, (_b = {},
[pathAttr]: alias2, _b[pathAttr] = alias2,
}); _b));
from += ` left join \`${this.getStorageName(rel)}\` \`${alias2}\` on \`${alias}\`.\`${attr}Id\` = \`${alias2}\`.\`id\``; from += " left join `".concat(_this.getStorageName(rel), "` `").concat(alias2, "` on `").concat(alias, "`.`").concat(attr, "Id` = `").concat(alias2, "`.`id`");
} }
else { else {
alias2 = aliasDict[pathAttr]; alias2 = aliasDict[pathAttr];
@ -259,15 +271,15 @@ class SqlTranslator {
}); });
} }
else if (rel === 2) { else if (rel === 2) {
const pathAttr = `${path}${attr}/`; var pathAttr = "".concat(path).concat(attr, "/");
let alias2; var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) { if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${attr}_${number++}`; alias2 = "".concat(attr, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, { (0, lodash_1.assign)(aliasDict, (_c = {},
[pathAttr]: alias2, _c[pathAttr] = alias2,
}); _c));
from += ` left join \`${this.getStorageName(attr)}\` \`${alias2}\` on \`${alias}\`.\`entityId\` = \`${alias2}\`.\`id\``; from += " left join `".concat(_this.getStorageName(attr), "` `").concat(alias2, "` on `").concat(alias, "`.`entityId` = `").concat(alias2, "`.`id`");
extraWhere += `\`${alias}\`.\`entity\` = '${attr}'`; extraWhere += "`".concat(alias, "`.`entity` = '").concat(attr, "'");
} }
else { else {
alias2 = aliasDict[pathAttr]; alias2 = aliasDict[pathAttr];
@ -284,28 +296,31 @@ class SqlTranslator {
} }
}; };
if (sorter) { if (sorter) {
sorter.forEach((sortNode) => { sorter.forEach(function (sortNode) {
analyzeSortNode({ analyzeSortNode({
node: sortNode.$attr, node: sortNode.$attr,
path: './', path: './',
entityName: entity, entityName: entity,
alias, alias: alias,
}); });
}); });
} }
const analyzeProjectionNode = ({ node, path, entityName, alias }) => { var analyzeProjectionNode = function (_a) {
const { attributes } = schema[entityName]; var _b;
Object.keys(node).forEach((attr) => { var node = _a.node, path = _a.path, entityName = _a.entityName, alias = _a.alias;
const rel = (0, relation_1.judgeRelation)(this.schema, entityName, attr); var attributes = schema[entityName].attributes;
Object.keys(node).forEach(function (attr) {
var _a, _b;
var rel = (0, relation_1.judgeRelation)(_this.schema, entityName, attr);
if (typeof rel === 'string') { if (typeof rel === 'string') {
const pathAttr = `${path}${attr}/`; var pathAttr = "".concat(path).concat(attr, "/");
let alias2; var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) { if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${rel}_${number++}`; alias2 = "".concat(rel, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, { (0, lodash_1.assign)(aliasDict, (_a = {},
[pathAttr]: alias2, _a[pathAttr] = alias2,
}); _a));
from += ` left join \`${this.getStorageName(rel)}\` \`${alias2}\` on \`${alias}\`.\`${attr}Id\` = \`${alias2}\`.\`id\``; from += " left join `".concat(_this.getStorageName(rel), "` `").concat(alias2, "` on `").concat(alias, "`.`").concat(attr, "Id` = `").concat(alias2, "`.`id`");
} }
else { else {
alias2 = aliasDict[pathAttr]; alias2 = aliasDict[pathAttr];
@ -318,14 +333,14 @@ class SqlTranslator {
}); });
} }
else if (rel === 2) { else if (rel === 2) {
const pathAttr = `${path}${attr}/`; var pathAttr = "".concat(path).concat(attr, "/");
let alias2; var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) { if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${attr}_${number++}`; alias2 = "".concat(attr, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, { (0, lodash_1.assign)(aliasDict, (_b = {},
[pathAttr]: alias2, _b[pathAttr] = alias2,
}); _b));
from += ` left join \`${this.getStorageName(attr)}\` \`${alias2}\` on \`${alias}\`.\`entityId\` = \`${alias2}\`.\`id\``; from += " left join `".concat(_this.getStorageName(attr), "` `").concat(alias2, "` on `").concat(alias, "`.`entityId` = `").concat(alias2, "`.`id`");
} }
else { else {
alias2 = aliasDict[pathAttr]; alias2 = aliasDict[pathAttr];
@ -336,30 +351,30 @@ class SqlTranslator {
entityName: attr, entityName: attr,
alias: alias2, alias: alias2,
}); });
extraWhere += `\`${alias}\`.\`entity\` = '${attr}'`; extraWhere += "`".concat(alias, "`.`entity` = '").concat(attr, "'");
} }
}); });
if (node['#id']) { if (node['#id']) {
(0, assert_1.default)(!projectionRefAlias[node['#id']]); (0, assert_1.default)(!projectionRefAlias[node['#id']]);
(0, lodash_1.assign)(projectionRefAlias, { (0, lodash_1.assign)(projectionRefAlias, (_b = {},
[node['#id']]: alias, _b[node['#id']] = alias,
}); _b));
} }
}; };
if (projection) { if (projection) {
analyzeProjectionNode({ node: projection, path: './', entityName: entity, alias }); analyzeProjectionNode({ node: projection, path: './', entityName: entity, alias: alias });
} }
return { return {
aliasDict, aliasDict: aliasDict,
from, from: from,
projectionRefAlias, projectionRefAlias: projectionRefAlias,
filterRefAlias, filterRefAlias: filterRefAlias,
extraWhere, extraWhere: extraWhere,
currentNumber: number, currentNumber: number,
}; };
} };
translateComparison(attr, value, type) { SqlTranslator.prototype.translateComparison = function (attr, value, type) {
const SQL_OP = { var SQL_OP = {
$gt: '>', $gt: '>',
$lt: '<', $lt: '<',
$gte: '>=', $gte: '>=',
@ -368,31 +383,31 @@ class SqlTranslator {
$ne: '<>', $ne: '<>',
}; };
if (Object.keys(SQL_OP).includes(attr)) { if (Object.keys(SQL_OP).includes(attr)) {
return ` ${SQL_OP[attr]} ${this.translateAttrValue(type, value)}`; return " ".concat(SQL_OP[attr], " ").concat(this.translateAttrValue(type, value));
} }
switch (attr) { switch (attr) {
case '$startsWith': { case '$startsWith': {
return ` like '${value}%'`; return " like '".concat(value, "%'");
} }
case '$endsWith': { case '$endsWith': {
return ` like '%${value}'`; return " like '%".concat(value, "'");
} }
case '$includes': { case '$includes': {
return ` like '%${value}%'`; return " like '%".concat(value, "%'");
} }
default: { default: {
throw new Error(`unrecoganized comparison operator ${attr}`); throw new Error("unrecoganized comparison operator ".concat(attr));
} }
} }
} };
translateElement(attr, value) { SqlTranslator.prototype.translateElement = function (attr, value) {
(0, assert_1.default)(attr === '$exists'); // only support one operator now (0, assert_1.default)(attr === '$exists'); // only support one operator now
if (value) { if (value) {
return ' is not null'; return ' is not null';
} }
return ' is null'; return ' is null';
} };
translateEvaluation(attr, value, entity, alias, type, initialNumber, refAlias) { SqlTranslator.prototype.translateEvaluation = function (attr, value, entity, alias, type, initialNumber, refAlias) {
switch (attr) { switch (attr) {
case '$text': { case '$text': {
// fulltext search // fulltext search
@ -403,22 +418,22 @@ class SqlTranslator {
} }
case '$in': case '$in':
case '$nin': { case '$nin': {
const IN_OP = { var IN_OP = {
$in: 'in', $in: 'in',
$nin: 'not in', $nin: 'not in',
}; };
if (value instanceof Array) { if (value instanceof Array) {
const values = value.map((v) => { var values = value.map(function (v) {
if (['varchar', 'char', 'text', 'nvarchar', 'ref'].includes(type)) { if (['varchar', 'char', 'text', 'nvarchar', 'ref'].includes(type)) {
return `'${v}'`; return "'".concat(v, "'");
} }
else { else {
return `${v}`; return "".concat(v);
} }
}); });
if (values.length > 0) { if (values.length > 0) {
return { return {
stmt: ` ${IN_OP[attr]}(${values.join(',')})`, stmt: " ".concat(IN_OP[attr], "(").concat(values.join(','), ")"),
currentNumber: initialNumber, currentNumber: initialNumber,
}; };
} }
@ -439,58 +454,59 @@ class SqlTranslator {
} }
else { else {
// sub query // sub query
const { stmt: subQueryStmt, currentNumber } = this.translateSelectInner(value.entity, value, initialNumber, refAlias, undefined); var _a = this.translateSelectInner(value.entity, value, initialNumber, refAlias, undefined), subQueryStmt = _a.stmt, currentNumber = _a.currentNumber;
return { return {
stmt: ` ${IN_OP[attr]}(${subQueryStmt})`, stmt: " ".concat(IN_OP[attr], "(").concat(subQueryStmt, ")"),
currentNumber, currentNumber: currentNumber,
}; };
} }
} }
default: { default: {
(0, assert_1.default)('$between' === attr); (0, assert_1.default)('$between' === attr);
const values = value.map((v) => { var values = value.map(function (v) {
if (['varchar', 'char', 'text', 'nvarchar', 'ref'].includes(type)) { if (['varchar', 'char', 'text', 'nvarchar', 'ref'].includes(type)) {
return `'${v}'`; return "'".concat(v, "'");
} }
else { else {
return `${v}`; return "".concat(v);
} }
}); });
return { return {
stmt: ` between ${values[0]} and ${values[1]}`, stmt: " between ".concat(values[0], " and ").concat(values[1]),
currentNumber: initialNumber, currentNumber: initialNumber,
}; };
} }
} }
} };
translateFilter(entity, selection, aliasDict, filterRefAlias, initialNumber, extraWhere) { SqlTranslator.prototype.translateFilter = function (entity, selection, aliasDict, filterRefAlias, initialNumber, extraWhere, option) {
const { schema } = this; var _this = this;
const { filter, hint } = selection; var schema = this.schema;
let currentNumber = initialNumber; var filter = selection.filter;
const translateInner = (entity2, path, filter2, type) => { var currentNumber = initialNumber;
const alias = aliasDict[path]; var translateInner = function (entity2, path, filter2, type) {
const { attributes } = schema[entity2]; var alias = aliasDict[path];
let whereText = type ? '' : this.getDefaultSelectFilter(alias, hint); var attributes = schema[entity2].attributes;
var whereText = type ? '' : _this.getDefaultSelectFilter(alias, option);
if (filter2) { if (filter2) {
const attrs = Object.keys(filter2).filter(ele => !ele.startsWith('#')); var attrs = Object.keys(filter2).filter(function (ele) { return !ele.startsWith('#'); });
attrs.forEach((attr) => { attrs.forEach(function (attr) {
if (whereText) { if (whereText) {
whereText += ' and '; whereText += ' and ';
} }
whereText + '('; whereText + '(';
if (['$and', '$or', '$xor', '$not'].includes(attr)) { if (['$and', '$or', '$xor', '$not'].includes(attr)) {
let result = ''; var result = '';
switch (attr) { switch (attr) {
case '$and': case '$and':
case '$or': case '$or':
case '$xor': { case '$xor': {
const logicQueries = filter2[attr]; var logicQueries_1 = filter2[attr];
logicQueries.forEach((logicQuery, index) => { logicQueries_1.forEach(function (logicQuery, index) {
const sql = translateInner(entity2, path, logicQuery); var sql = translateInner(entity2, path, logicQuery);
if (sql) { if (sql) {
whereText += ` (${sql})`; whereText += " (".concat(sql, ")");
if (index < logicQueries.length - 1) { if (index < logicQueries_1.length - 1) {
whereText += ` ${attr.slice(1)}`; whereText += " ".concat(attr.slice(1));
} }
} }
}); });
@ -498,10 +514,10 @@ class SqlTranslator {
} }
default: { default: {
(0, assert_1.default)(attr === '$not'); (0, assert_1.default)(attr === '$not');
const logicQuery = filter2[attr]; var logicQuery = filter2[attr];
const sql = translateInner(entity2, path, logicQuery); var sql = translateInner(entity2, path, logicQuery);
if (sql) { if (sql) {
whereText += ` not (${translateInner(entity2, path, logicQuery)})`; whereText += " not (".concat(translateInner(entity2, path, logicQuery), ")");
break; break;
} }
} }
@ -509,36 +525,36 @@ class SqlTranslator {
} }
else if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) { else if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
// expression // expression
whereText += ` (${this.translateExpression(alias, filter2[attr], filterRefAlias)})`; whereText += " (".concat(_this.translateExpression(alias, filter2[attr], filterRefAlias), ")");
} }
else if (['$gt', '$gte', '$lt', '$lte', '$eq', '$ne', '$startsWith', '$endsWith', '$includes'].includes(attr)) { else if (['$gt', '$gte', '$lt', '$lte', '$eq', '$ne', '$startsWith', '$endsWith', '$includes'].includes(attr)) {
whereText += this.translateComparison(attr, filter2[attr], type); whereText += _this.translateComparison(attr, filter2[attr], type);
} }
else if (['$exists'].includes(attr)) { else if (['$exists'].includes(attr)) {
whereText += this.translateElement(attr, filter2[attr]); whereText += _this.translateElement(attr, filter2[attr]);
} }
else if (['$text', '$in', '$nin', '$between'].includes(attr)) { else if (['$text', '$in', '$nin', '$between'].includes(attr)) {
const { stmt, currentNumber: cn2 } = this.translateEvaluation(attr, filter2[attr], entity2, alias, type, initialNumber, filterRefAlias); var _a = _this.translateEvaluation(attr, filter2[attr], entity2, alias, type, initialNumber, filterRefAlias), stmt = _a.stmt, cn2 = _a.currentNumber;
whereText += stmt; whereText += stmt;
currentNumber = cn2; currentNumber = cn2;
} }
else { else {
const rel = (0, relation_1.judgeRelation)(this.schema, entity, attr); var rel = (0, relation_1.judgeRelation)(_this.schema, entity, attr);
if (rel === 2) { if (rel === 2) {
whereText += ` ${translateInner(attr, `${path}${attr}/`, filter2[attr])}`; whereText += " ".concat(translateInner(attr, "".concat(path).concat(attr, "/"), filter2[attr]));
} }
else if (typeof rel === 'string') { else if (typeof rel === 'string') {
whereText += ` ${translateInner(rel, `${path}${attr}/`, filter2[attr])}`; whereText += " ".concat(translateInner(rel, "".concat(path).concat(attr, "/"), filter2[attr]));
} }
else { else {
(0, assert_1.default)(attributes.hasOwnProperty(attr), `非法的属性${attr}`); (0, assert_1.default)(attributes.hasOwnProperty(attr), "\u975E\u6CD5\u7684\u5C5E\u6027".concat(attr));
const { type: type2 } = attributes[attr]; var type2 = attributes[attr].type;
// assert (type2 !== 'ref'); // assert (type2 !== 'ref');
if (typeof filter2[attr] === 'object' && Object.keys(filter2[attr])[0] && Object.keys(filter2[attr])[0].startsWith('$')) { if (typeof filter2[attr] === 'object' && Object.keys(filter2[attr])[0] && Object.keys(filter2[attr])[0].startsWith('$')) {
whereText += ` \`${alias}\`.\`${attr}\` ${translateInner(entity2, path, filter2[attr], type2)}`; whereText += " `".concat(alias, "`.`").concat(attr, "` ").concat(translateInner(entity2, path, filter2[attr], type2));
} }
else { else {
whereText += ` \`${alias}\`.\`${attr}\` = ${this.translateAttrValue(type2, filter2[attr])}`; whereText += " `".concat(alias, "`.`").concat(attr, "` = ").concat(_this.translateAttrValue(type2, filter2[attr]));
} }
} }
} }
@ -550,88 +566,90 @@ class SqlTranslator {
} }
return whereText; return whereText;
}; };
const where = translateInner(entity, './', filter); var where = translateInner(entity, './', filter);
if (extraWhere && where) { if (extraWhere && where) {
return { return {
stmt: `${extraWhere} and ${where}`, stmt: "".concat(extraWhere, " and ").concat(where),
currentNumber, currentNumber: currentNumber,
}; };
} }
return { return {
stmt: extraWhere || where, stmt: extraWhere || where,
currentNumber, currentNumber: currentNumber,
}; };
} };
translateSorter(entity, sorter, aliasDict) { SqlTranslator.prototype.translateSorter = function (entity, sorter, aliasDict) {
const translateInner = (entity2, sortAttr, path) => { var _this = this;
var translateInner = function (entity2, sortAttr, path) {
(0, assert_1.default)(Object.keys(sortAttr).length === 1); (0, assert_1.default)(Object.keys(sortAttr).length === 1);
const attr = Object.keys(sortAttr)[0]; var attr = Object.keys(sortAttr)[0];
const alias = aliasDict[path]; var alias = aliasDict[path];
if (attr.toLocaleLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) { if (attr.toLocaleLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
return this.translateExpression(alias, sortAttr[attr], {}); return _this.translateExpression(alias, sortAttr[attr], {});
} }
else if (sortAttr[attr] === 1) { else if (sortAttr[attr] === 1) {
return `\`${alias}\`.\`${attr}\``; return "`".concat(alias, "`.`").concat(attr, "`");
} }
else { else {
const rel = (0, relation_1.judgeRelation)(this.schema, entity2, attr); var rel = (0, relation_1.judgeRelation)(_this.schema, entity2, attr);
if (typeof rel === 'string') { if (typeof rel === 'string') {
return translateInner(rel, sortAttr[attr], `${path}${attr}/`); return translateInner(rel, sortAttr[attr], "".concat(path).concat(attr, "/"));
} }
else { else {
(0, assert_1.default)(rel === 2); (0, assert_1.default)(rel === 2);
return translateInner(attr, sortAttr[attr], `${path}${attr}/`); return translateInner(attr, sortAttr[attr], "".concat(path).concat(attr, "/"));
} }
} }
}; };
let sortText = ''; var sortText = '';
sorter.forEach((sortNode, index) => { sorter.forEach(function (sortNode, index) {
const { $attr, $direction } = sortNode; var $attr = sortNode.$attr, $direction = sortNode.$direction;
sortText += translateInner(entity, $attr, './'); sortText += translateInner(entity, $attr, './');
if ($direction) { if ($direction) {
sortText += ` ${$direction}`; sortText += " ".concat($direction);
} }
if (index < sorter.length - 1) { if (index < sorter.length - 1) {
sortText += ','; sortText += ',';
} }
}); });
return sortText; return sortText;
} };
translateProjection(entity, projection, aliasDict, projectionRefAlias) { SqlTranslator.prototype.translateProjection = function (entity, projection, aliasDict, projectionRefAlias) {
const { schema } = this; var _this = this;
const translateInner = (entity2, projection2, path) => { var schema = this.schema;
const alias = aliasDict[path]; var translateInner = function (entity2, projection2, path) {
const { attributes } = schema[entity2]; var alias = aliasDict[path];
let projText = ''; var attributes = schema[entity2].attributes;
let prefix = path.slice(2).replace(/\//g, '.'); var projText = '';
const attrs = Object.keys(projection2).filter((attr) => { var prefix = path.slice(2).replace(/\//g, '.');
var attrs = Object.keys(projection2).filter(function (attr) {
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) { if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
return true; return true;
} }
const rel = (0, relation_1.judgeRelation)(this.schema, entity2, attr); var rel = (0, relation_1.judgeRelation)(_this.schema, entity2, attr);
return [1, 2].includes(rel) || typeof rel === 'string'; return [1, 2].includes(rel) || typeof rel === 'string';
}); });
attrs.forEach((attr, idx) => { attrs.forEach(function (attr, idx) {
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) { if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
const exprText = this.translateExpression(alias, projection2[attr], projectionRefAlias); var exprText = _this.translateExpression(alias, projection2[attr], projectionRefAlias);
projText += ` ${exprText} as ${prefix}${attr}`; projText += " ".concat(exprText, " as ").concat(prefix).concat(attr);
} }
else { else {
const rel = (0, relation_1.judgeRelation)(this.schema, entity2, attr); var rel = (0, relation_1.judgeRelation)(_this.schema, entity2, attr);
if (typeof rel === 'string') { if (typeof rel === 'string') {
projText += translateInner(rel, projection2[attr], `${path}${attr}/`); projText += translateInner(rel, projection2[attr], "".concat(path).concat(attr, "/"));
} }
else if (rel === 2) { else if (rel === 2) {
projText += translateInner(attr, projection2[attr], `${path}${attr}/`); projText += translateInner(attr, projection2[attr], "".concat(path).concat(attr, "/"));
} }
else if (rel === 1) { else if (rel === 1) {
const { type } = attributes[attr]; var type = attributes[attr].type;
if (projection2[attr] === 1) { if (projection2[attr] === 1) {
projText += ` ${this.translateAttrProjection(type, alias, attr)} as \`${prefix}${attr}\``; projText += " ".concat(_this.translateAttrProjection(type, alias, attr), " as `").concat(prefix).concat(attr, "`");
} }
else { else {
(0, assert_1.default)(typeof projection2 === 'string'); (0, assert_1.default)(typeof projection2 === 'string');
projText += ` ${this.translateAttrProjection(type, alias, attr)} as \`${prefix}${projection2[attr]}\``; projText += " ".concat(_this.translateAttrProjection(type, alias, attr), " as `").concat(prefix).concat(projection2[attr], "`");
} }
} }
} }
@ -642,78 +660,79 @@ class SqlTranslator {
return projText; return projText;
}; };
return translateInner(entity, projection, './'); return translateInner(entity, projection, './');
} };
translateSelectInner(entity, selection, initialNumber, refAlias, params) { SqlTranslator.prototype.translateSelectInner = function (entity, selection, initialNumber, refAlias, option) {
const { data, filter, sorter, indexFrom, count } = selection; var data = selection.data, filter = selection.filter, sorter = selection.sorter, indexFrom = selection.indexFrom, count = selection.count;
const { from: fromText, aliasDict, projectionRefAlias, extraWhere, filterRefAlias, currentNumber } = this.analyzeJoin(entity, { var _a = this.analyzeJoin(entity, {
projection: data, projection: data,
filter, filter: filter,
sorter, sorter: sorter,
}, initialNumber); }, initialNumber), fromText = _a.from, aliasDict = _a.aliasDict, projectionRefAlias = _a.projectionRefAlias, extraWhere = _a.extraWhere, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber;
(0, assert_1.default)((0, lodash_1.intersection)((0, lodash_1.keys)(refAlias), (0, lodash_1.keys)(filterRefAlias)).length === 0, 'filter中的#node结点定义有重复'); (0, assert_1.default)((0, lodash_1.intersection)((0, lodash_1.keys)(refAlias), (0, lodash_1.keys)(filterRefAlias)).length === 0, 'filter中的#node结点定义有重复');
(0, lodash_1.assign)(refAlias, filterRefAlias); (0, lodash_1.assign)(refAlias, filterRefAlias);
const projText = this.translateProjection(entity, data, aliasDict, projectionRefAlias); var projText = this.translateProjection(entity, data, aliasDict, projectionRefAlias);
const { stmt: filterText, currentNumber: currentNumber2 } = this.translateFilter(entity, selection, aliasDict, refAlias, currentNumber, extraWhere); var _b = this.translateFilter(entity, selection, aliasDict, refAlias, currentNumber, extraWhere, option), filterText = _b.stmt, currentNumber2 = _b.currentNumber;
const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict); var sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
return { return {
stmt: this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count), stmt: this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count, option),
currentNumber: currentNumber2, currentNumber: currentNumber2,
}; };
} };
translateSelect(entity, selection, params) { SqlTranslator.prototype.translateSelect = function (entity, selection, option) {
const { stmt } = this.translateSelectInner(entity, selection, 1, {}, params); var stmt = this.translateSelectInner(entity, selection, 1, {}, option).stmt;
return stmt; return stmt;
} };
translateCount(entity, selection, params) { SqlTranslator.prototype.translateCount = function (entity, selection, option) {
const { filter } = selection; var filter = selection.filter;
const { from: fromText, aliasDict, extraWhere, filterRefAlias, currentNumber } = this.analyzeJoin(entity, { var _a = this.analyzeJoin(entity, {
filter, filter: filter,
}); }), fromText = _a.from, aliasDict = _a.aliasDict, extraWhere = _a.extraWhere, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber;
const projText = 'count(1)'; var projText = 'count(1)';
const { stmt: filterText } = this.translateFilter(entity, selection, aliasDict, filterRefAlias, currentNumber, extraWhere); var filterText = this.translateFilter(entity, selection, aliasDict, filterRefAlias, currentNumber, extraWhere, option).stmt;
return this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, undefined, undefined, undefined); return this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, undefined, undefined, undefined, option);
} };
translateRemove(entity, operation, params) { SqlTranslator.prototype.translateRemove = function (entity, operation, option) {
const { filter, sorter, indexFrom, count } = operation; var filter = operation.filter, sorter = operation.sorter, indexFrom = operation.indexFrom, count = operation.count;
const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter }); var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, extraWhere = _a.extraWhere, fromText = _a.from, currentNumber = _a.currentNumber;
const alias = aliasDict['./']; var alias = aliasDict['./'];
const { stmt: filterText } = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere); var filterText = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere).stmt;
const sorterText = sorter && sorter.length > 0 ? this.translateSorter(entity, sorter, aliasDict) : undefined; var sorterText = sorter && sorter.length > 0 ? this.translateSorter(entity, sorter, aliasDict) : undefined;
return this.populateRemoveStmt(alias, fromText, aliasDict, filterText, sorterText, indexFrom, count, params); return this.populateRemoveStmt(alias, fromText, aliasDict, filterText, sorterText, indexFrom, count, option);
} };
translateUpdate(entity, operation, params) { SqlTranslator.prototype.translateUpdate = function (entity, operation, option) {
const { attributes } = this.schema[entity]; var attributes = this.schema[entity].attributes;
const { filter, sorter, indexFrom, count, data } = operation; var filter = operation.filter, sorter = operation.sorter, indexFrom = operation.indexFrom, count = operation.count, data = operation.data;
const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter }); var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, extraWhere = _a.extraWhere, fromText = _a.from, currentNumber = _a.currentNumber;
const alias = aliasDict['./']; var alias = aliasDict['./'];
let updateText = ''; var updateText = '';
for (const attr in data) { for (var attr in data) {
if (updateText) { if (updateText) {
updateText += ','; updateText += ',';
} }
(0, assert_1.default)(attributes.hasOwnProperty(attr) && attributes[attr].type !== 'ref'); (0, assert_1.default)(attributes.hasOwnProperty(attr) && attributes[attr].type !== 'ref');
const value = this.translateAttrValue(attributes[attr].type, data[attr]); var value = this.translateAttrValue(attributes[attr].type, data[attr]);
updateText += `\`${alias}\`.\`${attr}\` = ${value}`; updateText += "`".concat(alias, "`.`").concat(attr, "` = ").concat(value);
} }
const { stmt: filterText } = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere); var filterText = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere).stmt;
const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict); var sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
return this.populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, params); return this.populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option);
} };
translateDestroyEntity(entity, truncate) { SqlTranslator.prototype.translateDestroyEntity = function (entity, truncate) {
const { schema } = this; var schema = this.schema;
const { storageName = entity, view } = schema[entity]; var _a = schema[entity], _b = _a.storageName, storageName = _b === void 0 ? entity : _b, view = _a.view;
let sql; var sql;
if (view) { if (view) {
sql = `drop view if exists \`${storageName}\``; sql = "drop view if exists `".concat(storageName, "`");
} }
else { else {
sql = truncate ? `truncate table \`${storageName}\`` : `drop table if exists \`${storageName}\``; sql = truncate ? "truncate table `".concat(storageName, "`") : "drop table if exists `".concat(storageName, "`");
} }
return sql; return sql;
} };
escapeStringValue(value) { SqlTranslator.prototype.escapeStringValue = function (value) {
const result = `'${value.replace(/'/g, '\\\'')}'`; var result = "'".concat(value.replace(/'/g, '\\\''), "'");
return result; return result;
} };
} return SqlTranslator;
}());
exports.SqlTranslator = SqlTranslator; exports.SqlTranslator = SqlTranslator;

0
lib/types/Translator.d.ts vendored Normal file
View File

View File

@ -1,8 +1,8 @@
import { EntityDict, Context, DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperateParams, OperationResult, SelectionResult, TxnOption, SelectRowShape, StorageSchema, DeduceCreateMultipleOperation } from 'oak-domain/lib/types'; import { EntityDict, Context, DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperateOption, OperationResult, SelectionResult, TxnOption, SelectRowShape, StorageSchema, DeduceCreateMultipleOperation, SelectOption } from 'oak-domain/lib/types';
import { CascadeStore } from 'oak-domain/lib/store/CascadeStore'; import { CascadeStore } from 'oak-domain/lib/store/CascadeStore';
import { MySQLConfiguration } from './types/Configuration'; import { MySQLConfiguration } from './types/Configuration';
import { MySqlConnector } from './connector'; import { MySqlConnector } from './connector';
import { MySqlTranslator, MySqlSelectParams } from './translator'; import { MySqlTranslator, MySqlSelectOption, MysqlOperateOption } from './translator';
import { assign } from 'lodash'; import { assign } from 'lodash';
import assert from 'assert'; import assert from 'assert';
import { judgeRelation } from 'oak-domain/lib/store/relation'; import { judgeRelation } from 'oak-domain/lib/store/relation';
@ -168,9 +168,9 @@ export class MysqlStore<ED extends EntityDict, Cxt extends Context<ED>> extends
entity: T, entity: T,
selection: S, selection: S,
context: Cxt, context: Cxt,
params?: OperateParams & MySqlSelectParams option?: MySqlSelectOption
): Promise<SelectRowShape<ED[T]['Schema'], S['data']>[]> { ): Promise<SelectRowShape<ED[T]['Schema'], S['data']>[]> {
const sql = this.translator.translateSelect(entity, selection, params); const sql = this.translator.translateSelect(entity, selection, option);
const result = await this.connector.exec(sql, context.getCurrentTxnId()); const result = await this.connector.exec(sql, context.getCurrentTxnId());
return this.formResult(entity, result); return this.formResult(entity, result);
@ -179,7 +179,7 @@ export class MysqlStore<ED extends EntityDict, Cxt extends Context<ED>> extends
entity: T, entity: T,
operation: DeduceCreateMultipleOperation<ED[T]['Schema']> | DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, operation: DeduceCreateMultipleOperation<ED[T]['Schema']> | DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>,
context: Cxt, context: Cxt,
params?: OperateParams & MySqlSelectParams option?: MysqlOperateOption
): Promise<number> { ): Promise<number> {
const { translator, connector } = this; const { translator, connector } = this;
const { action } = operation; const { action } = operation;
@ -190,54 +190,60 @@ export class MysqlStore<ED extends EntityDict, Cxt extends Context<ED>> extends
const { data } = operation as DeduceCreateMultipleOperation<ED[T]['Schema']> | DeduceCreateSingleOperation<ED[T]['Schema']>; const { data } = operation as DeduceCreateMultipleOperation<ED[T]['Schema']> | DeduceCreateSingleOperation<ED[T]['Schema']>;
const sql = translator.translateInsert(entity, data instanceof Array ? data : [data]); const sql = translator.translateInsert(entity, data instanceof Array ? data : [data]);
await connector.exec(sql, txn); await connector.exec(sql, txn);
context.opRecords.push({ if (!option?.notCollect) {
a: 'c', context.opRecords.push({
d: data as any, a: 'c',
e: entity, d: data as any,
}); e: entity,
});
}
return data instanceof Array ? data.length : 1; return data instanceof Array ? data.length : 1;
} }
case 'remove': { case 'remove': {
const sql = translator.translateRemove(entity, operation as ED[T]['Remove'], params); const sql = translator.translateRemove(entity, operation as ED[T]['Remove'], option);
await connector.exec(sql, txn); await connector.exec(sql, txn);
// todo 这里对sorter和indexfrom/count的支持不完整 // todo 这里对sorter和indexfrom/count的支持不完整
context.opRecords.push({ if (!option?.notCollect) {
a: 'r', context.opRecords.push({
e: entity, a: 'r',
f: (operation as ED[T]['Remove']).filter, e: entity,
}); f: (operation as ED[T]['Remove']).filter,
});
}
return 1; return 1;
} }
default: { default: {
assert(!['select', 'download', 'stat'].includes(action)); assert(!['select', 'download', 'stat'].includes(action));
const sql = translator.translateUpdate(entity, operation as ED[T]['Update'], params); const sql = translator.translateUpdate(entity, operation as ED[T]['Update'], option);
await connector.exec(sql, txn); await connector.exec(sql, txn);
// todo 这里对sorter和indexfrom/count的支持不完整 // todo 这里对sorter和indexfrom/count的支持不完整
context.opRecords.push({ if (!option?.notCollect) {
a: 'u', context.opRecords.push({
e: entity, a: 'u',
d: (operation as ED[T]['Update']).data, e: entity,
f: (operation as ED[T]['Update']).filter, d: (operation as ED[T]['Update']).data,
}) f: (operation as ED[T]['Update']).filter,
});
}
return 1; return 1;
} }
} }
} }
async operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, params?: OperateParams): Promise<OperationResult<ED>> { async operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, params?: OperateOption): Promise<OperationResult<ED>> {
const { action } = operation; const { action } = operation;
assert(!['select', 'download', 'stat'].includes(action), '现在不支持使用select operation'); assert(!['select', 'download', 'stat'].includes(action), '现在不支持使用select operation');
return await this.cascadeUpdate(entity, operation as any, context, params); return await this.cascadeUpdate(entity, operation as any, context, params);
} }
async select<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt, params?: Object): Promise<SelectionResult<ED[T]['Schema'], S['data']>> { async select<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt, option?: SelectOption): Promise<SelectionResult<ED[T]['Schema'], S['data']>> {
const result = await this.cascadeSelect(entity, selection, context, params); const result = await this.cascadeSelect(entity, selection, context, option);
return { return {
result, result,
}; };
} }
async count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter'>, context: Cxt, params?: Object): Promise<number> { async count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option?: SelectOption): Promise<number> {
const sql = this.translator.translateCount(entity, selection, params); const sql = this.translator.translateCount(entity, selection, option);
const result = await this.connector.exec(sql, context.getCurrentTxnId()); const result = await this.connector.exec(sql, context.getCurrentTxnId());
return result.count as number; return result.count as number;

View File

@ -4,7 +4,7 @@ import { assign } from 'lodash';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { EntityDict, Geo, Q_FullTextValue, RefOrExpression, Ref, StorageSchema, Index, RefAttr } from "oak-domain/lib/types"; import { EntityDict, Geo, Q_FullTextValue, RefOrExpression, Ref, StorageSchema, Index, RefAttr } from "oak-domain/lib/types";
import { DataType, DataTypeParams } from "oak-domain/lib/types/schema/DataTypes"; import { DataType, DataTypeParams } from "oak-domain/lib/types/schema/DataTypes";
import { SelectParams, SqlTranslator } from "../sqlTranslator"; import { SqlOperateOption, SqlSelectOption, SqlTranslator } from "../sqlTranslator";
import { isDateExpression } from 'oak-domain/lib/types/Expression'; import { isDateExpression } from 'oak-domain/lib/types/Expression';
const GeoTypes = [ const GeoTypes = [
@ -92,17 +92,21 @@ type IndexHint = {
[k: string]: IndexHint; [k: string]: IndexHint;
} }
export interface MySqlSelectParams extends SelectParams { export interface MySqlSelectOption extends SqlSelectOption {
}
export interface MysqlOperateOption extends SqlOperateOption {
} }
export class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> { export class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> {
protected getDefaultSelectFilter<T extends keyof ED>(alias: string, hint: ED[T]['Selection']['hint']): string { protected getDefaultSelectFilter(alias: string, option?: MySqlSelectOption): string {
if (hint?.includeDeleted) { if (option?.includedDeleted) {
return ''; return '';
} }
return ` \`${alias}\`.\`$$deleteAt$$\` is null`; return ` \`${alias}\`.\`$$deleteAt$$\` is null`;
} }
private modifySchema() { private makeUpSchema() {
for (const entity in this.schema) { for (const entity in this.schema) {
const { attributes, indexes } = this.schema[entity]; const { attributes, indexes } = this.schema[entity];
const geoIndexes: Index<ED[keyof ED]['OpSchema']>[] = []; const geoIndexes: Index<ED[keyof ED]['OpSchema']>[] = [];
@ -143,7 +147,7 @@ export class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> {
constructor(schema: StorageSchema<ED>) { constructor(schema: StorageSchema<ED>) {
super(schema); super(schema);
// MySQL为geometry属性默认创建索引 // MySQL为geometry属性默认创建索引
this.modifySchema(); this.makeUpSchema();
} }
static supportedDataTypes: DataType[] = [ static supportedDataTypes: DataType[] = [
// numeric types // numeric types
@ -534,7 +538,7 @@ export class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> {
if (!replace) { if (!replace) {
return [sql]; return [sql];
} }
return [`drop ${entityType} \`${storageName || entity as string}\`;`, sql]; return [`drop ${entityType} if exists \`${storageName || entity as string}\`;`, sql];
} }
private translateFnName(fnName: string, argumentNumber: number): string { private translateFnName(fnName: string, argumentNumber: number): string {
@ -735,8 +739,8 @@ export class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> {
filterText: string, filterText: string,
sorterText?: string, sorterText?: string,
indexFrom?: number, indexFrom?: number,
count?: number): string { count?: number,
const { hint } = selection; option?: MySqlSelectOption): string {
// todo hint of use index // todo hint of use index
let sql = `select ${projectionText} from ${fromText}`; let sql = `select ${projectionText} from ${fromText}`;
if (filterText) { if (filterText) {
@ -749,13 +753,13 @@ export class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> {
assert (typeof count === 'number'); assert (typeof count === 'number');
sql += ` limit ${indexFrom}, ${count}`; sql += ` limit ${indexFrom}, ${count}`;
} }
if (hint?.mysql?.forUpdate) { if (option?.forUpdate) {
sql += ' for update'; sql += ' for update';
} }
return sql; return sql;
} }
protected populateUpdateStmt(updateText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, params?: MySqlSelectParams): string { protected populateUpdateStmt(updateText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: MysqlOperateOption): string {
// todo using index // todo using index
const alias = aliasDict['./']; const alias = aliasDict['./'];
const now = DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss'); const now = DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
@ -773,7 +777,7 @@ export class MySqlTranslator<ED extends EntityDict> extends SqlTranslator<ED> {
return sql; return sql;
} }
protected populateRemoveStmt(removeText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, params?: MySqlSelectParams): string { protected populateRemoveStmt(removeText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: MysqlOperateOption): string {
// todo using index // todo using index
const alias = aliasDict['./']; const alias = aliasDict['./'];
const now = DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss'); const now = DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');

View File

@ -1,11 +1,13 @@
import assert from 'assert'; import assert from 'assert';
import { assign, cloneDeep, intersection, keys, set } from 'lodash'; import { assign, cloneDeep, intersection, keys, set } from 'lodash';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { Attribute, DeduceCreateOperationData, DeduceSorterAttr, DeduceSorterItem, EntityDict, Expression, EXPRESSION_PREFIX, Index, Q_FullTextValue, Ref, RefOrExpression, StorageSchema } from "oak-domain/lib/types"; import { Attribute, DeduceCreateOperationData, DeduceSorterAttr, DeduceSorterItem, EntityDict, Expression, EXPRESSION_PREFIX, Index, OperateOption, Q_FullTextValue, Ref, RefOrExpression, SelectOption, StorageSchema } from "oak-domain/lib/types";
import { DataType } from "oak-domain/lib/types/schema/DataTypes"; import { DataType } from "oak-domain/lib/types/schema/DataTypes";
import { judgeRelation } from 'oak-domain/lib/store/relation'; import { judgeRelation } from 'oak-domain/lib/store/relation';
export type SelectParams = { export interface SqlSelectOption extends SelectOption {
};
export interface SqlOperateOption extends OperateOption {
}; };
export abstract class SqlTranslator<ED extends EntityDict> { export abstract class SqlTranslator<ED extends EntityDict> {
@ -112,7 +114,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
return schema; return schema;
} }
protected abstract getDefaultSelectFilter<T extends keyof ED>(alias: string, hint: ED[T]['Selection']['hint']): string; protected abstract getDefaultSelectFilter<OP extends SqlSelectOption>(alias: string, option?: OP): string;
protected abstract translateAttrProjection(dataType: DataType, alias: string, attr: string): string; protected abstract translateAttrProjection(dataType: DataType, alias: string, attr: string): string;
@ -122,7 +124,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
abstract translateCreateEntity<T extends keyof ED>(entity: T, option: { replace?: boolean }): string[]; abstract translateCreateEntity<T extends keyof ED>(entity: T, option: { replace?: boolean }): string[];
protected abstract populateSelectStmt<T extends keyof ED>( protected abstract populateSelectStmt<T extends keyof ED, OP extends SqlSelectOption>(
projectionText: string, projectionText: string,
fromText: string, fromText: string,
selection: ED[T]['Selection'], selection: ED[T]['Selection'],
@ -130,9 +132,10 @@ export abstract class SqlTranslator<ED extends EntityDict> {
filterText: string, filterText: string,
sorterText?: string, sorterText?: string,
indexFrom?: number, indexFrom?: number,
count?: number): string; count?: number,
option?: OP): string;
protected abstract populateUpdateStmt( protected abstract populateUpdateStmt<OP extends SqlOperateOption>(
updateText: string, updateText: string,
fromText: string, fromText: string,
aliasDict: Record<string, string>, aliasDict: Record<string, string>,
@ -140,9 +143,9 @@ export abstract class SqlTranslator<ED extends EntityDict> {
sorterText?: string, sorterText?: string,
indexFrom?: number, indexFrom?: number,
count?: number, count?: number,
params?: any): string; option?: OP): string;
protected abstract populateRemoveStmt( protected abstract populateRemoveStmt<OP extends SqlOperateOption>(
removeText: string, removeText: string,
fromText: string, fromText: string,
aliasDict: Record<string, string>, aliasDict: Record<string, string>,
@ -150,7 +153,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
sorterText?: string, sorterText?: string,
indexFrom?: number, indexFrom?: number,
count?: number, count?: number,
params?: any): string; option?: OP): string;
protected abstract translateExpression<T extends keyof ED>(alias: string, expression: RefOrExpression<keyof ED[T]['OpSchema']>, refDict: Record<string, string>): string; protected abstract translateExpression<T extends keyof ED>(alias: string, expression: RefOrExpression<keyof ED[T]['OpSchema']>, refDict: Record<string, string>): string;
@ -592,24 +595,25 @@ export abstract class SqlTranslator<ED extends EntityDict> {
} }
} }
private translateFilter<T extends keyof ED>( private translateFilter<T extends keyof ED, OP extends SqlSelectOption>(
entity: T, entity: T,
selection: Pick<ED[T]['Selection'], 'filter' | 'hint'>, selection: Pick<ED[T]['Selection'], 'filter'>,
aliasDict: Record<string, string>, aliasDict: Record<string, string>,
filterRefAlias: Record<string, string>, filterRefAlias: Record<string, string>,
initialNumber: number, initialNumber: number,
extraWhere?: string): { extraWhere?: string,
option?: OP): {
stmt: string; stmt: string;
currentNumber: number; currentNumber: number;
} { } {
const { schema } = this; const { schema } = this;
const { filter, hint } = selection; const { filter } = selection;
let currentNumber = initialNumber; let currentNumber = initialNumber;
const translateInner = <E extends keyof ED>(entity2: E, path: string, filter2?: ED[E]['Selection']['filter'], type?: DataType | Ref): string => { const translateInner = <E extends keyof ED>(entity2: E, path: string, filter2?: ED[E]['Selection']['filter'], type?: DataType | Ref): string => {
const alias = aliasDict[path]; const alias = aliasDict[path];
const { attributes } = schema[entity2]; const { attributes } = schema[entity2];
let whereText = type ? '' : this.getDefaultSelectFilter(alias, hint); let whereText = type ? '' : this.getDefaultSelectFilter(alias, option);
if (filter2) { if (filter2) {
const attrs = Object.keys(filter2).filter( const attrs = Object.keys(filter2).filter(
ele => !ele.startsWith('#') ele => !ele.startsWith('#')
@ -810,7 +814,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
return translateInner(entity, projection, './'); return translateInner(entity, projection, './');
} }
private translateSelectInner<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], initialNumber: number, refAlias: Record<string, string>, params?: SelectParams): { private translateSelectInner<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: ED[T]['Selection'], initialNumber: number, refAlias: Record<string, string>, option?: OP): {
stmt: string; stmt: string;
currentNumber: number; currentNumber: number;
} { } {
@ -825,36 +829,39 @@ export abstract class SqlTranslator<ED extends EntityDict> {
const projText = this.translateProjection(entity, data, aliasDict, projectionRefAlias); const projText = this.translateProjection(entity, data, aliasDict, projectionRefAlias);
const { stmt: filterText, currentNumber: currentNumber2 } = this.translateFilter(entity, selection, aliasDict, refAlias, currentNumber, extraWhere); const { stmt: filterText, currentNumber: currentNumber2 } = this.translateFilter(entity, selection, aliasDict, refAlias, currentNumber, extraWhere, option);
const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict); const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
return { return {
stmt: this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count), stmt: this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count, option),
currentNumber: currentNumber2, currentNumber: currentNumber2,
}; };
} }
translateSelect<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], params?: SelectParams): string { translateSelect<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: ED[T]['Selection'], option?: OP): string {
const { stmt } = this.translateSelectInner(entity, selection, 1, {}, params); const { stmt } = this.translateSelectInner(entity, selection, 1, {}, option);
return stmt; return stmt;
} }
translateCount<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter'>, params?: SelectParams): string { translateCount<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, option?: OP): string {
const { filter } = selection; const { filter, count } = selection;
const { from: fromText, aliasDict, extraWhere, filterRefAlias, currentNumber } = this.analyzeJoin(entity, { const { from: fromText, aliasDict, extraWhere, filterRefAlias, currentNumber } = this.analyzeJoin(entity, {
filter, filter,
}); });
const projText = 'count(1)'; const projText = 'count(1)';
const { stmt: filterText } = this.translateFilter(entity, selection as ED[T]['Selection'], aliasDict, filterRefAlias, currentNumber, extraWhere); const { stmt: filterText } = this.translateFilter(entity, selection as ED[T]['Selection'], aliasDict, filterRefAlias, currentNumber, extraWhere, option);
if (count) {
return this.populateSelectStmt(projText, fromText, selection as ED[T]['Selection'], aliasDict, filterText, undefined, undefined, undefined); const subQuerySql = this.populateSelectStmt('1', fromText, Object.assign({}, selection, { indexFrom: 0 }) as ED[T]['Selection'], aliasDict, filterText, undefined, undefined, undefined, option);
return `select count(1) from (${subQuerySql})`;
}
return this.populateSelectStmt(projText, fromText, selection as ED[T]['Selection'], aliasDict, filterText, undefined, undefined, undefined, option);
} }
translateRemove<T extends keyof ED>(entity: T, operation: ED[T]['Remove'], params?: SelectParams): string { translateRemove<T extends keyof ED, OP extends SqlOperateOption>(entity: T, operation: ED[T]['Remove'], option?: OP): string {
const { filter, sorter, indexFrom, count } = operation; const { filter, sorter, indexFrom, count } = operation;
const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter }); const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter });
@ -864,10 +871,10 @@ export abstract class SqlTranslator<ED extends EntityDict> {
const sorterText = sorter && sorter.length > 0 ? this.translateSorter(entity, sorter, aliasDict) : undefined; const sorterText = sorter && sorter.length > 0 ? this.translateSorter(entity, sorter, aliasDict) : undefined;
return this.populateRemoveStmt(alias, fromText, aliasDict, filterText, sorterText, indexFrom, count, params); return this.populateRemoveStmt(alias, fromText, aliasDict, filterText, sorterText, indexFrom, count, option);
} }
translateUpdate<T extends keyof ED>(entity: T, operation: ED[T]['Update'], params?: any): string { translateUpdate<T extends keyof ED, OP extends SqlOperateOption>(entity: T, operation: ED[T]['Update'], option?: OP): string {
const { attributes } = this.schema[entity]; const { attributes } = this.schema[entity];
const { filter, sorter, indexFrom, count, data } = operation; const { filter, sorter, indexFrom, count, data } = operation;
const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter }); const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter });
@ -887,7 +894,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
const { stmt: filterText } = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere); const { stmt: filterText } = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere);
const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict); const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
return this.populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, params); return this.populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option);
} }
translateDestroyEntity(entity: string, truncate?: boolean): string { translateDestroyEntity(entity: string, truncate?: boolean): string {

View File

@ -1,7 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "CommonJS", "module": "CommonJS",
"target": "esnext", "target": "es5",
"declaration": true,
"allowJs": false, "allowJs": false,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"esModuleInterop": true, "esModuleInterop": true,