对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";
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) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MySqlConnector = void 0;
const mysql2_1 = __importDefault(require("mysql2"));
const uuid_1 = require("uuid");
const assert_1 = __importDefault(require("assert"));
class MySqlConnector {
pool;
configuration;
txnDict;
constructor(configuration) {
var mysql2_1 = __importDefault(require("mysql2"));
var uuid_1 = require("uuid");
var assert_1 = __importDefault(require("assert"));
var MySqlConnector = /** @class */ (function () {
function MySqlConnector(configuration) {
this.configuration = configuration;
this.txnDict = {};
}
connect() {
MySqlConnector.prototype.connect = function () {
this.pool = mysql2_1.default.createPool(this.configuration);
}
disconnect() {
};
MySqlConnector.prototype.disconnect = function () {
this.pool.end();
}
startTransaction(option) {
return new Promise((resolve, reject) => {
this.pool.getConnection((err, connection) => {
};
MySqlConnector.prototype.startTransaction = function (option) {
var _this = this;
return new Promise(function (resolve, reject) {
_this.pool.getConnection(function (err, connection) {
if (err) {
return reject(err);
}
const { isolationLevel } = option || {};
const startTxn = () => {
let sql = 'START TRANSACTION;';
connection.query(sql, (err2) => {
var isolationLevel = (option || {}).isolationLevel;
var startTxn = function () {
var sql = 'START TRANSACTION;';
connection.query(sql, function (err2) {
var _a;
if (err2) {
connection.release();
return reject(err2);
}
const id = (0, uuid_1.v4)();
Object.assign(this.txnDict, {
[id]: connection,
});
var id = (0, uuid_1.v4)();
Object.assign(_this.txnDict, (_a = {},
_a[id] = connection,
_a));
resolve(id);
});
};
if (isolationLevel) {
connection.query(`SET TRANSACTION ISOLATION LEVEL ${isolationLevel};`, (err2) => {
connection.query("SET TRANSACTION ISOLATION LEVEL ".concat(isolationLevel, ";"), function (err2) {
if (err2) {
connection.release();
return reject(err2);
@ -56,44 +91,51 @@ class MySqlConnector {
}
});
});
}
async exec(sql, txn) {
if (process.env.NODE_ENV === 'development') {
console.log(sql);
}
if (txn) {
const connection = this.txnDict[txn];
(0, assert_1.default)(connection);
return new Promise((resolve, reject) => {
connection.query(sql, (err, result) => {
if (err) {
console.error(`sql exec err: ${sql}`, err);
return reject(err);
}
resolve(result);
});
};
MySqlConnector.prototype.exec = function (sql, txn) {
return __awaiter(this, void 0, void 0, function () {
var connection_1;
var _this = this;
return __generator(this, function (_a) {
if (process.env.NODE_ENV === 'development') {
console.log(sql);
}
if (txn) {
connection_1 = this.txnDict[txn];
(0, assert_1.default)(connection_1);
return [2 /*return*/, new Promise(function (resolve, reject) {
connection_1.query(sql, function (err, 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) => {
// if (process.env.DEBUG) {
// 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];
});
};
MySqlConnector.prototype.commitTransaction = function (txn) {
var connection = this.txnDict[txn];
(0, assert_1.default)(connection);
return new Promise((resolve, reject) => {
connection.query('COMMIT;', (err) => {
return new Promise(function (resolve, reject) {
connection.query('COMMIT;', function (err) {
if (err) {
return reject(err);
}
@ -101,12 +143,12 @@ class MySqlConnector {
resolve();
});
});
}
rollbackTransaction(txn) {
const connection = this.txnDict[txn];
};
MySqlConnector.prototype.rollbackTransaction = function (txn) {
var connection = this.txnDict[txn];
(0, assert_1.default)(connection);
return new Promise((resolve, reject) => {
connection.query('ROLLBACK;', (err) => {
return new Promise(function (resolve, reject) {
connection.query('ROLLBACK;', function (err) {
if (err) {
return reject(err);
}
@ -114,6 +156,7 @@ class MySqlConnector {
resolve();
});
});
}
}
};
return 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";
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) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MysqlStore = void 0;
const CascadeStore_1 = require("oak-domain/lib/store/CascadeStore");
const connector_1 = require("./connector");
const translator_1 = require("./translator");
const lodash_1 = require("lodash");
const assert_1 = __importDefault(require("assert"));
const relation_1 = require("oak-domain/lib/store/relation");
var CascadeStore_1 = require("oak-domain/lib/store/CascadeStore");
var connector_1 = require("./connector");
var translator_1 = require("./translator");
var lodash_1 = require("lodash");
var assert_1 = __importDefault(require("assert"));
var relation_1 = require("oak-domain/lib/store/relation");
function convertGeoTextToObject(geoText) {
if (geoText.startsWith('POINT')) {
const coord = geoText.match((/(\d|\.)+(?=\)|\s)/g));
var coord = geoText.match((/(\d|\.)+(?=\)|\s)/g));
return {
type: 'Point',
coordinates: coord.map(ele => parseFloat(ele)),
coordinates: coord.map(function (ele) { return parseFloat(ele); }),
};
}
else {
throw new Error('only support Point now');
}
}
class MysqlStore extends CascadeStore_1.CascadeStore {
connector;
translator;
constructor(storageSchema, configuration) {
super(storageSchema);
this.connector = new connector_1.MySqlConnector(configuration);
this.translator = new translator_1.MySqlTranslator(storageSchema);
var MysqlStore = /** @class */ (function (_super) {
__extends(MysqlStore, _super);
function MysqlStore(storageSchema, configuration) {
var _this = _super.call(this, storageSchema) || this;
_this.connector = new connector_1.MySqlConnector(configuration);
_this.translator = new translator_1.MySqlTranslator(storageSchema);
return _this;
}
supportManyToOneJoin() {
MysqlStore.prototype.supportManyToOneJoin = function () {
return true;
}
supportMultipleCreate() {
};
MysqlStore.prototype.supportMultipleCreate = function () {
return true;
}
formResult(entity, result) {
const schema = this.getSchema();
};
MysqlStore.prototype.formResult = function (entity, result) {
var schema = this.getSchema();
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) {
const i = attr.indexOf(".");
var i = attr.indexOf(".");
if (i !== -1) {
const attrHead = attr.slice(0, i);
const attrTail = attr.slice(i + 1);
var attrHead = attr.slice(0, i);
var attrTail = attr.slice(i + 1);
if (!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');
resolveAttribute(typeof rel === 'string' ? rel : attrHead, r[attrHead], attrTail, value);
}
else if (attributes[attr]) {
const { type } = attributes[attr];
var type = attributes[attr].type;
switch (type) {
case 'date':
case 'time': {
@ -87,7 +139,7 @@ class MysqlStore extends CascadeStore_1.CascadeStore {
case 'function': {
if (typeof value === 'string') {
// 函数的执行环境需要的参数只有创建函数者知悉只能由上层再创建Function
r[attr] = `return ${Buffer.from(value, 'base64').toString()}`;
r[attr] = "return ".concat(Buffer.from(value, 'base64').toString());
}
else {
r[attr] = value;
@ -117,15 +169,15 @@ class MysqlStore extends CascadeStore_1.CascadeStore {
}
}
else {
(0, lodash_1.assign)(r, {
[attr]: value,
});
(0, lodash_1.assign)(r, (_a = {},
_a[attr] = value,
_a));
}
}
function formalizeNullObject(r, e) {
const { attributes: a2 } = schema[e];
let allowFormalize = true;
for (let attr in r) {
var a2 = schema[e].attributes;
var allowFormalize = true;
for (var attr in r) {
if (typeof r[attr] === 'object' && a2[attr] && a2[attr].type === 'ref') {
if (formalizeNullObject(r[attr], a2[attr].ref)) {
r[attr] = null;
@ -141,106 +193,220 @@ class MysqlStore extends CascadeStore_1.CascadeStore {
return allowFormalize;
}
function formSingleRow(r) {
let result2 = {};
for (let attr in r) {
const value = r[attr];
var result2 = {};
for (var attr in r) {
var value = r[attr];
resolveAttribute(entity, result2, attr, value);
}
formalizeNullObject(result2, entity);
return result2;
}
if (result instanceof Array) {
return result.map(r => formSingleRow(r));
return result.map(function (r) { return formSingleRow(r); });
}
return formSingleRow(result);
}
async selectAbjointRow(entity, selection, context, params) {
const sql = this.translator.translateSelect(entity, selection, params);
const result = await this.connector.exec(sql, context.getCurrentTxnId());
return this.formResult(entity, result);
}
async updateAbjointRow(entity, operation, context, params) {
const { translator, connector } = this;
const { action } = operation;
const txn = context.getCurrentTxnId();
switch (action) {
case 'create': {
const { data } = operation;
const sql = translator.translateInsert(entity, data instanceof Array ? data : [data]);
await connector.exec(sql, txn);
context.opRecords.push({
a: 'c',
d: data,
e: entity,
});
return data instanceof Array ? data.length : 1;
}
case 'remove': {
const sql = translator.translateRemove(entity, operation, params);
await connector.exec(sql, txn);
// todo 这里对sorter和indexfrom/count的支持不完整
context.opRecords.push({
a: 'r',
e: entity,
f: operation.filter,
});
return 1;
}
default: {
(0, assert_1.default)(!['select', 'download', 'stat'].includes(action));
const sql = translator.translateUpdate(entity, operation, params);
await connector.exec(sql, txn);
// todo 这里对sorter和indexfrom/count的支持不完整
context.opRecords.push({
a: 'u',
e: entity,
d: operation.data,
f: operation.filter,
});
return 1;
}
}
}
async operate(entity, operation, context, params) {
const { action } = operation;
(0, assert_1.default)(!['select', 'download', 'stat'].includes(action), '现在不支持使用select operation');
return await this.cascadeUpdate(entity, operation, context, params);
}
async select(entity, selection, context, params) {
const result = await this.cascadeSelect(entity, selection, context, params);
return {
result,
};
}
async count(entity, selection, context, params) {
const sql = this.translator.translateCount(entity, selection, params);
const result = await this.connector.exec(sql, context.getCurrentTxnId());
return result.count;
}
async begin(option) {
const txn = await this.connector.startTransaction(option);
return txn;
}
async commit(txnId) {
await this.connector.commitTransaction(txnId);
}
async rollback(txnId) {
await this.connector.rollbackTransaction(txnId);
}
connect() {
};
MysqlStore.prototype.selectAbjointRow = 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.translateSelect(entity, selection, option);
return [4 /*yield*/, this.connector.exec(sql, context.getCurrentTxnId())];
case 1:
result = _a.sent();
return [2 /*return*/, this.formResult(entity, result)];
}
});
});
};
MysqlStore.prototype.updateAbjointRow = function (entity, operation, context, option) {
return __awaiter(this, void 0, void 0, function () {
var _a, translator, connector, action, txn, _b, data, sql, sql, sql;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = this, translator = _a.translator, connector = _a.connector;
action = operation.action;
txn = context.getCurrentTxnId();
_b = action;
switch (_b) {
case 'create': return [3 /*break*/, 1];
case 'remove': return [3 /*break*/, 3];
}
return [3 /*break*/, 5];
case 1:
data = operation.data;
sql = translator.translateInsert(entity, data instanceof Array ? data : [data]);
return [4 /*yield*/, connector.exec(sql, txn)];
case 2:
_c.sent();
if (!(option === null || option === void 0 ? void 0 : option.notCollect)) {
context.opRecords.push({
a: 'c',
d: data,
e: entity,
});
}
return [2 /*return*/, data instanceof Array ? data.length : 1];
case 3:
sql = translator.translateRemove(entity, operation, option);
return [4 /*yield*/, connector.exec(sql, txn)];
case 4:
_c.sent();
// todo 这里对sorter和indexfrom/count的支持不完整
if (!(option === null || option === void 0 ? void 0 : option.notCollect)) {
context.opRecords.push({
a: 'r',
e: entity,
f: operation.filter,
});
}
return [2 /*return*/, 1];
case 5:
(0, assert_1.default)(!['select', 'download', 'stat'].includes(action));
sql = translator.translateUpdate(entity, operation, option);
return [4 /*yield*/, connector.exec(sql, txn)];
case 6:
_c.sent();
// todo 这里对sorter和indexfrom/count的支持不完整
if (!(option === null || option === void 0 ? void 0 : option.notCollect)) {
context.opRecords.push({
a: 'u',
e: entity,
d: operation.data,
f: operation.filter,
});
}
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();
}
disconnect() {
};
MysqlStore.prototype.disconnect = function () {
this.connector.disconnect();
}
async initialize(dropIfExists) {
const schema = this.getSchema();
for (const entity in schema) {
const sqls = this.translator.translateCreateEntity(entity, { replace: dropIfExists });
for (const sql of sqls) {
await this.connector.exec(sql);
}
}
}
}
};
MysqlStore.prototype.initialize = function (dropIfExists) {
return __awaiter(this, void 0, void 0, function () {
var schema, _a, _b, _i, entity, sqls, _c, sqls_1, sql;
return __generator(this, function (_d) {
switch (_d.label) {
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;

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";
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) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MySqlTranslator = void 0;
const assert_1 = __importDefault(require("assert"));
const util_1 = require("util");
const lodash_1 = require("lodash");
const luxon_1 = require("luxon");
const sqlTranslator_1 = require("../sqlTranslator");
const GeoTypes = [
var assert_1 = __importDefault(require("assert"));
var util_1 = require("util");
var lodash_1 = require("lodash");
var luxon_1 = require("luxon");
var sqlTranslator_1 = require("../sqlTranslator");
var GeoTypes = [
{
type: 'point',
name: "Point"
@ -42,54 +57,65 @@ const GeoTypes = [
];
function transformGeoData(data) {
if (data instanceof Array) {
const element = data[0];
if (element instanceof Array) {
return ` GeometryCollection(${data.map(ele => transformGeoData(ele)).join(',')})`;
var element_1 = data[0];
if (element_1 instanceof Array) {
return " GeometryCollection(".concat(data.map(function (ele) { return transformGeoData(ele); }).join(','), ")");
}
else {
const geoType = GeoTypes.find(ele => ele.type === element.type);
if (!geoType) {
throw new Error(`${element.type} is not supported in MySQL`);
var geoType_1 = GeoTypes.find(function (ele) { return ele.type === element_1.type; });
if (!geoType_1) {
throw new Error("".concat(element_1.type, " is not supported in MySQL"));
}
const multiGeoType = GeoTypes.find(ele => ele.element === geoType.type && ele.multiple);
return ` ${multiGeoType.name}(${data.map(ele => transformGeoData(ele)).join(',')})`;
var multiGeoType = GeoTypes.find(function (ele) { return ele.element === geoType_1.type && ele.multiple; });
return " ".concat(multiGeoType.name, "(").concat(data.map(function (ele) { return transformGeoData(ele); }).join(','), ")");
}
}
else {
const { type, coordinate } = data;
const geoType = GeoTypes.find(ele => ele.type === type);
var type_1 = data.type, coordinate = data.coordinate;
var geoType = GeoTypes.find(function (ele) { return ele.type === type_1; });
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;
if (!element) {
var element_2 = geoType.element, name_1 = geoType.name;
if (!element_2) {
// Point
return ` ${name}(${coordinate.join(',')})`;
return " ".concat(name_1, "(").concat(coordinate.join(','), ")");
}
// Polygon or Linestring
return ` ${name}(${coordinate.map((ele) => transformGeoData({
type: element,
return " ".concat(name_1, "(").concat(coordinate.map(function (ele) { return transformGeoData({
type: element_2,
coordinate: ele,
}))})`;
}); }), ")");
}
}
class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
getDefaultSelectFilter(alias, hint) {
if (hint?.includeDeleted) {
var MySqlTranslator = /** @class */ (function (_super) {
__extends(MySqlTranslator, _super);
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 ` \`${alias}\`.\`$$deleteAt$$\` is null`;
}
modifySchema() {
for (const entity in this.schema) {
const { attributes, indexes } = this.schema[entity];
const geoIndexes = [];
for (const attr in attributes) {
return " `".concat(alias, "`.`$$deleteAt$$` is null");
};
MySqlTranslator.prototype.makeUpSchema = function () {
for (var entity in this.schema) {
var _a = this.schema[entity], attributes = _a.attributes, indexes = _a.indexes;
var geoIndexes = [];
var _loop_1 = function (attr) {
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) {
geoIndexes.push({
name: `${entity}_geo_${attr}`,
name: "".concat(entity, "_geo_").concat(attr),
attributes: [{
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 (indexes) {
indexes.push(...geoIndexes);
indexes.push.apply(indexes, geoIndexes);
}
else {
(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;
populateDataTypeDef(type, params) {
MySqlTranslator.prototype.populateDataTypeDef = function (type, params) {
if (MySqlTranslator.withLengthDataTypes.includes(type)) {
if (params) {
const { length } = params;
return `${type}(${length}) `;
var length_1 = params.length;
return "".concat(type, "(").concat(length_1, ") ");
}
else {
const { length } = MySqlTranslator.dataTypeDefaults[type];
return `${type}(${length}) `;
var length_2 = MySqlTranslator.dataTypeDefaults[type].length;
return "".concat(type, "(").concat(length_2, ") ");
}
}
if (MySqlTranslator.withPrecisionDataTypes.includes(type)) {
if (params) {
const { precision, scale } = params;
var precision = params.precision, scale = params.scale;
if (typeof scale === 'number') {
return `${type}(${precision}, ${scale}) `;
return "".concat(type, "(").concat(precision, ", ").concat(scale, ") ");
}
return `${type}(${precision})`;
return "".concat(type, "(").concat(precision, ")");
}
else {
const { precision, scale } = MySqlTranslator.dataTypeDefaults[type];
var _a = MySqlTranslator.dataTypeDefaults[type], precision = _a.precision, scale = _a.scale;
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)) {
(0, assert_1.default)(type === 'int');
const { width } = params;
var width = params.width;
switch (width) {
case 1: {
return 'tinyint';
@ -317,19 +201,19 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
if (type === 'ref') {
return 'char(36)';
}
return `${type} `;
}
translateAttrProjection(dataType, alias, attr) {
return "".concat(type, " ");
};
MySqlTranslator.prototype.translateAttrProjection = function (dataType, alias, attr) {
switch (dataType) {
case 'geometry': {
return ` st_astext(\`${alias}\`.\`${attr}\`)`;
return " st_astext(`".concat(alias, "`.`").concat(attr, "`)");
}
default: {
return ` \`${alias}\`.\`${attr}\``;
return " `".concat(alias, "`.`").concat(attr, "`");
}
}
}
translateAttrValue(dataType, value) {
};
MySqlTranslator.prototype.translateAttrValue = function (dataType, value) {
if (value === null) {
return 'null';
}
@ -360,32 +244,36 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return value;
}
}
}
translateFullTextSearch(value, entity, alias) {
const { $search } = value;
const { indexes } = this.schema[entity];
const ftIndex = indexes && indexes.find((ele) => {
const { config } = ele;
};
MySqlTranslator.prototype.translateFullTextSearch = function (value, entity, alias) {
var $search = value.$search;
var indexes = this.schema[entity].indexes;
var ftIndex = indexes && indexes.find(function (ele) {
var config = ele.config;
return config && config.type === 'fulltext';
});
(0, assert_1.default)(ftIndex);
const { attributes } = ftIndex;
const columns2 = attributes.map(({ name }) => `${alias}.${name}`);
return ` match(${columns2.join(',')}) against ('${$search}' in natural language mode)`;
}
translateCreateEntity(entity, options) {
const replace = options?.replace;
const { schema } = this;
const entityDef = schema[entity];
const { storageName, attributes, indexes, view } = entityDef;
var attributes = ftIndex.attributes;
var columns2 = attributes.map(function (_a) {
var name = _a.name;
return "".concat(alias, ".").concat(name);
});
return " match(".concat(columns2.join(','), ") against ('").concat($search, "' in natural language mode)");
};
MySqlTranslator.prototype.translateCreateEntity = function (entity, options) {
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暂还不支持
const entityType = view ? 'view' : 'table';
let sql = `create ${entityType} `;
var entityType = view ? 'view' : 'table';
var sql = "create ".concat(entityType, " ");
if (storageName) {
sql += `\`${storageName}\` `;
sql += "`".concat(storageName, "` ");
}
else {
sql += `\`${entity}\` `;
sql += "`".concat(entity, "` ");
}
if (view) {
throw new Error(' view unsupported yet');
@ -393,11 +281,11 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
else {
sql += '(';
// 翻译所有的属性
Object.keys(attributes).forEach((attr, idx) => {
const attrDef = attributes[attr];
const { type, params, default: defaultValue, unique, notNull, } = attrDef;
sql += `\`${attr}\` `;
sql += this.populateDataTypeDef(type, params);
Object.keys(attributes).forEach(function (attr, idx) {
var attrDef = attributes[attr];
var type = attrDef.type, params = attrDef.params, defaultValue = attrDef.default, unique = attrDef.unique, notNull = attrDef.notNull;
sql += "`".concat(attr, "` ");
sql += _this.populateDataTypeDef(type, params);
if (notNull || type === 'geometry') {
sql += ' not null ';
}
@ -406,7 +294,7 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
}
if (defaultValue !== undefined) {
(0, assert_1.default)(type !== 'ref');
sql += ` default ${this.translateAttrValue(type, defaultValue)}`;
sql += " default ".concat(_this.translateAttrValue(type, defaultValue));
}
if (attr === 'id') {
sql += ' primary key';
@ -418,8 +306,9 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
// 翻译索引信息
if (indexes) {
sql += ',\n';
indexes.forEach(({ name, attributes, config }, idx) => {
const { unique, type, parser } = config || {};
indexes.forEach(function (_a, idx) {
var name = _a.name, attributes = _a.attributes, config = _a.config;
var _b = config || {}, unique = _b.unique, type = _b.type, parser = _b.parser;
if (unique) {
sql += ' unique ';
}
@ -429,19 +318,20 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
else if (type === 'spatial') {
sql += ' spatial ';
}
sql += `index ${name} `;
sql += "index ".concat(name, " ");
if (type === 'hash') {
sql += ` using hash `;
sql += " using hash ";
}
sql += '(';
let includeDeleteAt = false;
attributes.forEach(({ name, size, direction }, idx2) => {
sql += `\`${name}\``;
var includeDeleteAt = false;
attributes.forEach(function (_a, idx2) {
var name = _a.name, size = _a.size, direction = _a.direction;
sql += "`".concat(name, "`");
if (size) {
sql += ` (${size})`;
sql += " (".concat(size, ")");
}
if (direction) {
sql += ` ${direction}`;
sql += " ".concat(direction);
}
if (idx2 < attributes.length - 1) {
sql += ',';
@ -455,7 +345,7 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
}
sql += ')';
if (parser) {
sql += ` with parser ${parser}`;
sql += " with parser ".concat(parser);
}
if (idx < indexes.length - 1) {
sql += ',\n';
@ -467,9 +357,9 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
if (!replace) {
return [sql];
}
return [`drop ${entityType} \`${storageName || entity}\`;`, sql];
}
translateFnName(fnName, argumentNumber) {
return ["drop ".concat(entityType, " if exists `").concat(storageName || entity, "`;"), sql];
};
MySqlTranslator.prototype.translateFnName = function (fnName, argumentNumber) {
switch (fnName) {
case '$add': {
return '%s + %s';
@ -532,8 +422,8 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return '%s = false';
}
case '$and': {
let result = '';
for (let iter = 0; iter < argumentNumber; iter++) {
var result = '';
for (var iter = 0; iter < argumentNumber; iter++) {
result += '%s';
if (iter < argumentNumber - 1) {
result += ' and ';
@ -542,8 +432,8 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return result;
}
case '$or': {
let result = '';
for (let iter = 0; iter < argumentNumber; iter++) {
var result = '';
for (var iter = 0; iter < argumentNumber; iter++) {
result += '%s';
if (iter < argumentNumber - 1) {
result += ' or ';
@ -588,43 +478,44 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return 'ST_DISTANCE(%s, %s)';
}
default: {
throw new Error(`unrecoganized function ${fnName}`);
throw new Error("unrecoganized function ".concat(fnName));
}
}
}
translateExpression(alias, expression, refDict) {
const translateConstant = (constant) => {
};
MySqlTranslator.prototype.translateExpression = function (alias, expression, refDict) {
var _this = this;
var translateConstant = function (constant) {
if (typeof constant === 'string') {
return `'${constant}'`;
return "'".concat(constant, "'");
}
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 {
(0, assert_1.default)(typeof constant === 'number');
return `${constant}`;
return "".concat(constant);
}
};
const translateInner = (expr) => {
const k = Object.keys(expr);
let result;
var translateInner = function (expr) {
var k = Object.keys(expr);
var result;
if (k.includes('#attr')) {
const attrText = `\`${alias}\`.\`${(expr)['#attr']}\``;
var attrText = "`".concat(alias, "`.`").concat((expr)['#attr'], "`");
result = attrText;
}
else if (k.includes('#refId')) {
const refId = (expr)['#refId'];
const refAttr = (expr)['#refAttr'];
var refId = (expr)['#refId'];
var refAttr = (expr)['#refAttr'];
(0, assert_1.default)(refDict[refId]);
const attrText = `\`${refDict[refId]}\`.\`${refAttr}\``;
var attrText = "`".concat(refDict[refId], "`.`").concat(refAttr, "`");
result = attrText;
}
else {
(0, assert_1.default)(k.length === 1);
if ((expr)[k[0]] instanceof Array) {
const fnName = this.translateFnName(k[0], (expr)[k[0]].length);
const args = [fnName];
args.push(...(expr)[k[0]].map((ele) => {
var fnName = _this.translateFnName(k[0], (expr)[k[0]].length);
var args = [fnName];
args.push.apply(args, (expr)[k[0]].map(function (ele) {
if (['string', 'number'].includes(typeof ele) || ele instanceof Date) {
return translateConstant(ele);
}
@ -635,9 +526,9 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
result = util_1.format.apply(null, args);
}
else {
const fnName = this.translateFnName(k[0], 1);
const args = [fnName];
const arg = (expr)[k[0]];
var fnName = _this.translateFnName(k[0], 1);
var args = [fnName];
var arg = (expr)[k[0]];
if (['string', 'number'].includes(typeof arg) || arg instanceof Date) {
args.push(translateConstant(arg));
}
@ -650,59 +541,198 @@ class MySqlTranslator extends sqlTranslator_1.SqlTranslator {
return result;
};
return translateInner(expression);
}
populateSelectStmt(projectionText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count) {
const { hint } = selection;
};
MySqlTranslator.prototype.populateSelectStmt = function (projectionText, fromText, selection, aliasDict, filterText, sorterText, indexFrom, count, option) {
// todo hint of use index
let sql = `select ${projectionText} from ${fromText}`;
var sql = "select ".concat(projectionText, " from ").concat(fromText);
if (filterText) {
sql += ` where ${filterText}`;
sql += " where ".concat(filterText);
}
if (sorterText) {
sql += ` order by ${sorterText}`;
sql += " order by ".concat(sorterText);
}
if (typeof indexFrom === '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';
}
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
const alias = aliasDict['./'];
const now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
let sql = `update ${fromText} set ${updateText ? `${updateText},` : ''} \`${alias}\`.\`$$updateAt$$\` = '${now}'`;
var alias = aliasDict['./'];
var now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
var sql = "update ".concat(fromText, " set ").concat(updateText ? "".concat(updateText, ",") : '', " `").concat(alias, "`.`$$updateAt$$` = '").concat(now, "'");
if (filterText) {
sql += ` where ${filterText}`;
sql += " where ".concat(filterText);
}
if (sorterText) {
sql += ` order by ${sorterText}`;
sql += " order by ".concat(sorterText);
}
if (typeof indexFrom === 'number') {
(0, assert_1.default)(typeof count === 'number');
sql += ` limit ${indexFrom}, ${count}`;
sql += " limit ".concat(indexFrom, ", ").concat(count);
}
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
const alias = aliasDict['./'];
const now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
let sql = `update ${fromText} set \`${alias}\`.\`$$deleteAt$$\` = '${now}'`;
var alias = aliasDict['./'];
var now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
var sql = "update ".concat(fromText, " set `").concat(alias, "`.`$$deleteAt$$` = '").concat(now, "'");
if (filterText) {
sql += ` where ${filterText}`;
sql += " where ".concat(filterText);
}
if (sorterText) {
sql += ` order by ${sorterText}`;
sql += " order by ".concat(sorterText);
}
if (typeof indexFrom === 'number') {
(0, assert_1.default)(typeof count === 'number');
sql += ` limit ${indexFrom}, ${count}`;
sql += " limit ".concat(indexFrom, ", ").concat(count);
}
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;

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 });
exports.SqlTranslator = void 0;
const assert_1 = __importDefault(require("assert"));
const lodash_1 = require("lodash");
const luxon_1 = require("luxon");
const types_1 = require("oak-domain/lib/types");
const relation_1 = require("oak-domain/lib/store/relation");
class SqlTranslator {
schema;
constructor(schema) {
var assert_1 = __importDefault(require("assert"));
var lodash_1 = require("lodash");
var luxon_1 = require("luxon");
var types_1 = require("oak-domain/lib/types");
var relation_1 = require("oak-domain/lib/store/relation");
;
;
var SqlTranslator = /** @class */ (function () {
function SqlTranslator(schema) {
this.schema = this.makeFullSchema(schema);
}
makeFullSchema(schema2) {
const schema = (0, lodash_1.cloneDeep)(schema2);
for (const entity in schema) {
const { attributes, indexes } = schema[entity];
SqlTranslator.prototype.makeFullSchema = function (schema2) {
var schema = (0, lodash_1.cloneDeep)(schema2);
for (var entity in schema) {
var _a = schema[entity], attributes = _a.attributes, indexes = _a.indexes;
// 增加默认的属性
(0, lodash_1.assign)(attributes, {
id: {
@ -45,30 +46,29 @@ class SqlTranslator {
},
});
// 增加默认的索引
const intrinsticIndexes = [
var intrinsticIndexes = [
{
name: `${entity}_create_at`,
name: "".concat(entity, "_create_at"),
attributes: [{
name: '$$createAt$$',
}]
}, {
name: `${entity}_update_at`,
name: "".concat(entity, "_update_at"),
attributes: [{
name: '$$updateAt$$',
}],
}, {
name: `${entity}_trigger_ts`,
name: "".concat(entity, "_trigger_ts"),
attributes: [{
name: '$$triggerTimestamp$$',
}],
}
];
// 增加外键上的索引
for (const attr in attributes) {
var _loop_1 = function (attr) {
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({
name: `${entity}_fk_${attr}`,
name: "".concat(entity, "_fk_").concat(attr),
attributes: [{
name: attr,
}]
@ -76,11 +76,11 @@ class SqlTranslator {
}
}
if (attr === 'entity' && attributes[attr].type === 'varchar') {
const entityIdDef = attributes.entityId;
if (entityIdDef?.type === 'varchar') {
if (!(indexes?.find(ele => ele.attributes[0].name === 'entity' && ele.attributes[1]?.name === 'entityId'))) {
var entityIdDef = attributes.entityId;
if ((entityIdDef === null || entityIdDef === void 0 ? void 0 : entityIdDef.type) === 'varchar') {
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({
name: `${entity}_fk_entity_entityId`,
name: "".concat(entity, "_fk_entity_entityId"),
attributes: [{
name: 'entity',
}, {
@ -90,9 +90,13 @@ class SqlTranslator {
}
}
}
};
// 增加外键上的索引
for (var attr in attributes) {
_loop_1(attr);
}
if (indexes) {
indexes.push(...intrinsticIndexes);
indexes.push.apply(indexes, intrinsticIndexes);
}
else {
(0, lodash_1.assign)(schema[entity], {
@ -101,42 +105,43 @@ class SqlTranslator {
}
}
return schema;
}
getStorageName(entity) {
const { storageName } = this.schema[entity];
};
SqlTranslator.prototype.getStorageName = function (entity) {
var storageName = this.schema[entity].storageName;
return (storageName || entity);
}
translateInsert(entity, data) {
const { schema } = this;
const { attributes, storageName = entity } = schema[entity];
let sql = `insert into \`${storageName}\`(`;
const attrs = Object.keys(data[0]).filter(ele => attributes.hasOwnProperty(ele));
attrs.forEach((attr, idx) => {
sql += ` \`${attr}\``;
};
SqlTranslator.prototype.translateInsert = function (entity, data) {
var _this = this;
var schema = this.schema;
var _a = schema[entity], attributes = _a.attributes, _b = _a.storageName, storageName = _b === void 0 ? entity : _b;
var sql = "insert into `".concat(storageName, "`(");
var attrs = Object.keys(data[0]).filter(function (ele) { return attributes.hasOwnProperty(ele); });
attrs.forEach(function (attr, idx) {
sql += " `".concat(attr, "`");
if (idx < attrs.length - 1) {
sql += ',';
}
});
sql += ', `$$createAt$$`, `$$updateAt$$`) values ';
const now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
data.forEach((d, dataIndex) => {
var now = luxon_1.DateTime.now().toFormat('yyyy-LL-dd HH:mm:ss');
data.forEach(function (d, dataIndex) {
sql += '(';
attrs.forEach((attr, attrIdx) => {
const attrDef = attributes[attr];
const { type: dataType } = attrDef;
const value = this.translateAttrValue(dataType, d[attr]);
attrs.forEach(function (attr, attrIdx) {
var attrDef = attributes[attr];
var dataType = attrDef.type;
var value = _this.translateAttrValue(dataType, d[attr]);
sql += value;
if (attrIdx < attrs.length - 1) {
sql += ',';
}
});
sql += `, '${now}', '${now}')`;
sql += ", '".concat(now, "', '").concat(now, "')");
if (dataIndex < data.length - 1) {
sql += ',';
}
});
return sql;
}
};
/**
* analyze the join relations in projection/query/sort
* 所有的层次关系都当成left join处理如果有内表为空的情况请手动处理
@ -150,38 +155,43 @@ class SqlTranslator {
* 这样的query会把内表为空的行也返回
* @param param0
*/
analyzeJoin(entity, { projection, filter, sorter, isStat }, initialNumber) {
const { schema } = this;
let number = initialNumber || 1;
const projectionRefAlias = {};
const filterRefAlias = {};
let extraWhere = '';
const alias = `${entity}_${number++}`;
let from = ` \`${this.getStorageName(entity)}\` \`${alias}\` `;
const aliasDict = {
SqlTranslator.prototype.analyzeJoin = function (entity, _a, initialNumber) {
var _this = this;
var projection = _a.projection, filter = _a.filter, sorter = _a.sorter, isStat = _a.isStat;
var schema = this.schema;
var number = initialNumber || 1;
var projectionRefAlias = {};
var filterRefAlias = {};
var extraWhere = '';
var alias = "".concat(entity, "_").concat(number++);
var from = " `".concat(this.getStorageName(entity), "` `").concat(alias, "` ");
var aliasDict = {
'./': alias,
};
const analyzeFilterNode = ({ node, path, entityName, alias }) => {
Object.keys(node).forEach((op) => {
var analyzeFilterNode = function (_a) {
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)) {
node[op].forEach((subNode) => analyzeFilterNode({
node[op].forEach(function (subNode) { return analyzeFilterNode({
node: subNode,
path,
entityName,
alias,
}));
path: path,
entityName: entityName,
alias: alias,
}); });
}
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') {
let alias2;
const pathAttr = `${path}${op}/`;
var alias2 = void 0;
var pathAttr = "".concat(path).concat(op, "/");
if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${rel}_${number++}`;
(0, lodash_1.assign)(aliasDict, {
[pathAttr]: alias2,
});
from += ` left join \`${this.getStorageName(rel)}\` \`${alias2}\` on \`${alias}\`.\`${op}Id\` = \`${alias2}\`.\`id\``;
alias2 = "".concat(rel, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, (_a = {},
_a[pathAttr] = alias2,
_a));
from += " left join `".concat(_this.getStorageName(rel), "` `").concat(alias2, "` on `").concat(alias, "`.`").concat(op, "Id` = `").concat(alias2, "`.`id`");
}
else {
alias2 = aliasDict[pathAttr];
@ -194,15 +204,15 @@ class SqlTranslator {
});
}
else if (rel === 2) {
let alias2;
const pathAttr = `${path}${op}/`;
var alias2 = void 0;
var pathAttr = "".concat(path).concat(op, "/");
if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${op}_${number++}`;
(0, lodash_1.assign)(aliasDict, {
[pathAttr]: alias2,
});
from += ` left join \`${this.getStorageName(op)}\` \`${alias2}\` on \`${alias}\`.\`entityId\` = \`${alias2}\`.\`id\``;
extraWhere += `\`${alias}\`.\`entity\` = '${op}'`;
alias2 = "".concat(op, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, (_b = {},
_b[pathAttr] = alias2,
_b));
from += " left join `".concat(_this.getStorageName(op), "` `").concat(alias2, "` on `").concat(alias, "`.`entityId` = `").concat(alias2, "`.`id`");
extraWhere += "`".concat(alias, "`.`entity` = '").concat(op, "'");
}
else {
alias2 = aliasDict[pathAttr];
@ -222,9 +232,9 @@ class SqlTranslator {
});
if (node['#id']) {
(0, assert_1.default)(!filterRefAlias[node['#id']]);
(0, lodash_1.assign)(filterRefAlias, {
[node['#id']]: alias,
});
(0, lodash_1.assign)(filterRefAlias, (_b = {},
_b[node['#id']] = alias,
_b));
}
};
if (filter) {
@ -232,21 +242,23 @@ class SqlTranslator {
node: filter,
path: './',
entityName: entity,
alias,
alias: alias,
});
}
const analyzeSortNode = ({ node, path, entityName, alias }) => {
const attr = (0, lodash_1.keys)(node)[0];
const rel = (0, relation_1.judgeRelation)(this.schema, entityName, attr);
var analyzeSortNode = function (_a) {
var _b, _c;
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') {
const pathAttr = `${path}${attr}/`;
let alias2;
var pathAttr = "".concat(path).concat(attr, "/");
var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${rel}_${number++}`;
(0, lodash_1.assign)(aliasDict, {
[pathAttr]: alias2,
});
from += ` left join \`${this.getStorageName(rel)}\` \`${alias2}\` on \`${alias}\`.\`${attr}Id\` = \`${alias2}\`.\`id\``;
alias2 = "".concat(rel, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, (_b = {},
_b[pathAttr] = alias2,
_b));
from += " left join `".concat(_this.getStorageName(rel), "` `").concat(alias2, "` on `").concat(alias, "`.`").concat(attr, "Id` = `").concat(alias2, "`.`id`");
}
else {
alias2 = aliasDict[pathAttr];
@ -259,15 +271,15 @@ class SqlTranslator {
});
}
else if (rel === 2) {
const pathAttr = `${path}${attr}/`;
let alias2;
var pathAttr = "".concat(path).concat(attr, "/");
var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${attr}_${number++}`;
(0, lodash_1.assign)(aliasDict, {
[pathAttr]: alias2,
});
from += ` left join \`${this.getStorageName(attr)}\` \`${alias2}\` on \`${alias}\`.\`entityId\` = \`${alias2}\`.\`id\``;
extraWhere += `\`${alias}\`.\`entity\` = '${attr}'`;
alias2 = "".concat(attr, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, (_c = {},
_c[pathAttr] = alias2,
_c));
from += " left join `".concat(_this.getStorageName(attr), "` `").concat(alias2, "` on `").concat(alias, "`.`entityId` = `").concat(alias2, "`.`id`");
extraWhere += "`".concat(alias, "`.`entity` = '").concat(attr, "'");
}
else {
alias2 = aliasDict[pathAttr];
@ -284,28 +296,31 @@ class SqlTranslator {
}
};
if (sorter) {
sorter.forEach((sortNode) => {
sorter.forEach(function (sortNode) {
analyzeSortNode({
node: sortNode.$attr,
path: './',
entityName: entity,
alias,
alias: alias,
});
});
}
const analyzeProjectionNode = ({ node, path, entityName, alias }) => {
const { attributes } = schema[entityName];
Object.keys(node).forEach((attr) => {
const rel = (0, relation_1.judgeRelation)(this.schema, entityName, attr);
var analyzeProjectionNode = function (_a) {
var _b;
var node = _a.node, path = _a.path, entityName = _a.entityName, alias = _a.alias;
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') {
const pathAttr = `${path}${attr}/`;
let alias2;
var pathAttr = "".concat(path).concat(attr, "/");
var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${rel}_${number++}`;
(0, lodash_1.assign)(aliasDict, {
[pathAttr]: alias2,
});
from += ` left join \`${this.getStorageName(rel)}\` \`${alias2}\` on \`${alias}\`.\`${attr}Id\` = \`${alias2}\`.\`id\``;
alias2 = "".concat(rel, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, (_a = {},
_a[pathAttr] = alias2,
_a));
from += " left join `".concat(_this.getStorageName(rel), "` `").concat(alias2, "` on `").concat(alias, "`.`").concat(attr, "Id` = `").concat(alias2, "`.`id`");
}
else {
alias2 = aliasDict[pathAttr];
@ -318,14 +333,14 @@ class SqlTranslator {
});
}
else if (rel === 2) {
const pathAttr = `${path}${attr}/`;
let alias2;
var pathAttr = "".concat(path).concat(attr, "/");
var alias2 = void 0;
if (!aliasDict.hasOwnProperty(pathAttr)) {
alias2 = `${attr}_${number++}`;
(0, lodash_1.assign)(aliasDict, {
[pathAttr]: alias2,
});
from += ` left join \`${this.getStorageName(attr)}\` \`${alias2}\` on \`${alias}\`.\`entityId\` = \`${alias2}\`.\`id\``;
alias2 = "".concat(attr, "_").concat(number++);
(0, lodash_1.assign)(aliasDict, (_b = {},
_b[pathAttr] = alias2,
_b));
from += " left join `".concat(_this.getStorageName(attr), "` `").concat(alias2, "` on `").concat(alias, "`.`entityId` = `").concat(alias2, "`.`id`");
}
else {
alias2 = aliasDict[pathAttr];
@ -336,30 +351,30 @@ class SqlTranslator {
entityName: attr,
alias: alias2,
});
extraWhere += `\`${alias}\`.\`entity\` = '${attr}'`;
extraWhere += "`".concat(alias, "`.`entity` = '").concat(attr, "'");
}
});
if (node['#id']) {
(0, assert_1.default)(!projectionRefAlias[node['#id']]);
(0, lodash_1.assign)(projectionRefAlias, {
[node['#id']]: alias,
});
(0, lodash_1.assign)(projectionRefAlias, (_b = {},
_b[node['#id']] = alias,
_b));
}
};
if (projection) {
analyzeProjectionNode({ node: projection, path: './', entityName: entity, alias });
analyzeProjectionNode({ node: projection, path: './', entityName: entity, alias: alias });
}
return {
aliasDict,
from,
projectionRefAlias,
filterRefAlias,
extraWhere,
aliasDict: aliasDict,
from: from,
projectionRefAlias: projectionRefAlias,
filterRefAlias: filterRefAlias,
extraWhere: extraWhere,
currentNumber: number,
};
}
translateComparison(attr, value, type) {
const SQL_OP = {
};
SqlTranslator.prototype.translateComparison = function (attr, value, type) {
var SQL_OP = {
$gt: '>',
$lt: '<',
$gte: '>=',
@ -368,31 +383,31 @@ class SqlTranslator {
$ne: '<>',
};
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) {
case '$startsWith': {
return ` like '${value}%'`;
return " like '".concat(value, "%'");
}
case '$endsWith': {
return ` like '%${value}'`;
return " like '%".concat(value, "'");
}
case '$includes': {
return ` like '%${value}%'`;
return " like '%".concat(value, "%'");
}
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
if (value) {
return ' is not 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) {
case '$text': {
// fulltext search
@ -403,22 +418,22 @@ class SqlTranslator {
}
case '$in':
case '$nin': {
const IN_OP = {
var IN_OP = {
$in: 'in',
$nin: 'not in',
};
if (value instanceof Array) {
const values = value.map((v) => {
var values = value.map(function (v) {
if (['varchar', 'char', 'text', 'nvarchar', 'ref'].includes(type)) {
return `'${v}'`;
return "'".concat(v, "'");
}
else {
return `${v}`;
return "".concat(v);
}
});
if (values.length > 0) {
return {
stmt: ` ${IN_OP[attr]}(${values.join(',')})`,
stmt: " ".concat(IN_OP[attr], "(").concat(values.join(','), ")"),
currentNumber: initialNumber,
};
}
@ -439,58 +454,59 @@ class SqlTranslator {
}
else {
// 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 {
stmt: ` ${IN_OP[attr]}(${subQueryStmt})`,
currentNumber,
stmt: " ".concat(IN_OP[attr], "(").concat(subQueryStmt, ")"),
currentNumber: currentNumber,
};
}
}
default: {
(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)) {
return `'${v}'`;
return "'".concat(v, "'");
}
else {
return `${v}`;
return "".concat(v);
}
});
return {
stmt: ` between ${values[0]} and ${values[1]}`,
stmt: " between ".concat(values[0], " and ").concat(values[1]),
currentNumber: initialNumber,
};
}
}
}
translateFilter(entity, selection, aliasDict, filterRefAlias, initialNumber, extraWhere) {
const { schema } = this;
const { filter, hint } = selection;
let currentNumber = initialNumber;
const translateInner = (entity2, path, filter2, type) => {
const alias = aliasDict[path];
const { attributes } = schema[entity2];
let whereText = type ? '' : this.getDefaultSelectFilter(alias, hint);
};
SqlTranslator.prototype.translateFilter = function (entity, selection, aliasDict, filterRefAlias, initialNumber, extraWhere, option) {
var _this = this;
var schema = this.schema;
var filter = selection.filter;
var currentNumber = initialNumber;
var translateInner = function (entity2, path, filter2, type) {
var alias = aliasDict[path];
var attributes = schema[entity2].attributes;
var whereText = type ? '' : _this.getDefaultSelectFilter(alias, option);
if (filter2) {
const attrs = Object.keys(filter2).filter(ele => !ele.startsWith('#'));
attrs.forEach((attr) => {
var attrs = Object.keys(filter2).filter(function (ele) { return !ele.startsWith('#'); });
attrs.forEach(function (attr) {
if (whereText) {
whereText += ' and ';
}
whereText + '(';
if (['$and', '$or', '$xor', '$not'].includes(attr)) {
let result = '';
var result = '';
switch (attr) {
case '$and':
case '$or':
case '$xor': {
const logicQueries = filter2[attr];
logicQueries.forEach((logicQuery, index) => {
const sql = translateInner(entity2, path, logicQuery);
var logicQueries_1 = filter2[attr];
logicQueries_1.forEach(function (logicQuery, index) {
var sql = translateInner(entity2, path, logicQuery);
if (sql) {
whereText += ` (${sql})`;
if (index < logicQueries.length - 1) {
whereText += ` ${attr.slice(1)}`;
whereText += " (".concat(sql, ")");
if (index < logicQueries_1.length - 1) {
whereText += " ".concat(attr.slice(1));
}
}
});
@ -498,10 +514,10 @@ class SqlTranslator {
}
default: {
(0, assert_1.default)(attr === '$not');
const logicQuery = filter2[attr];
const sql = translateInner(entity2, path, logicQuery);
var logicQuery = filter2[attr];
var sql = translateInner(entity2, path, logicQuery);
if (sql) {
whereText += ` not (${translateInner(entity2, path, logicQuery)})`;
whereText += " not (".concat(translateInner(entity2, path, logicQuery), ")");
break;
}
}
@ -509,36 +525,36 @@ class SqlTranslator {
}
else if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
// 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)) {
whereText += this.translateComparison(attr, filter2[attr], type);
whereText += _this.translateComparison(attr, filter2[attr], type);
}
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)) {
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;
currentNumber = cn2;
}
else {
const rel = (0, relation_1.judgeRelation)(this.schema, entity, attr);
var rel = (0, relation_1.judgeRelation)(_this.schema, entity, attr);
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') {
whereText += ` ${translateInner(rel, `${path}${attr}/`, filter2[attr])}`;
whereText += " ".concat(translateInner(rel, "".concat(path).concat(attr, "/"), filter2[attr]));
}
else {
(0, assert_1.default)(attributes.hasOwnProperty(attr), `非法的属性${attr}`);
const { type: type2 } = attributes[attr];
(0, assert_1.default)(attributes.hasOwnProperty(attr), "\u975E\u6CD5\u7684\u5C5E\u6027".concat(attr));
var type2 = attributes[attr].type;
// assert (type2 !== 'ref');
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 {
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;
};
const where = translateInner(entity, './', filter);
var where = translateInner(entity, './', filter);
if (extraWhere && where) {
return {
stmt: `${extraWhere} and ${where}`,
currentNumber,
stmt: "".concat(extraWhere, " and ").concat(where),
currentNumber: currentNumber,
};
}
return {
stmt: extraWhere || where,
currentNumber,
currentNumber: currentNumber,
};
}
translateSorter(entity, sorter, aliasDict) {
const translateInner = (entity2, sortAttr, path) => {
};
SqlTranslator.prototype.translateSorter = function (entity, sorter, aliasDict) {
var _this = this;
var translateInner = function (entity2, sortAttr, path) {
(0, assert_1.default)(Object.keys(sortAttr).length === 1);
const attr = Object.keys(sortAttr)[0];
const alias = aliasDict[path];
var attr = Object.keys(sortAttr)[0];
var alias = aliasDict[path];
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) {
return `\`${alias}\`.\`${attr}\``;
return "`".concat(alias, "`.`").concat(attr, "`");
}
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') {
return translateInner(rel, sortAttr[attr], `${path}${attr}/`);
return translateInner(rel, sortAttr[attr], "".concat(path).concat(attr, "/"));
}
else {
(0, assert_1.default)(rel === 2);
return translateInner(attr, sortAttr[attr], `${path}${attr}/`);
return translateInner(attr, sortAttr[attr], "".concat(path).concat(attr, "/"));
}
}
};
let sortText = '';
sorter.forEach((sortNode, index) => {
const { $attr, $direction } = sortNode;
var sortText = '';
sorter.forEach(function (sortNode, index) {
var $attr = sortNode.$attr, $direction = sortNode.$direction;
sortText += translateInner(entity, $attr, './');
if ($direction) {
sortText += ` ${$direction}`;
sortText += " ".concat($direction);
}
if (index < sorter.length - 1) {
sortText += ',';
}
});
return sortText;
}
translateProjection(entity, projection, aliasDict, projectionRefAlias) {
const { schema } = this;
const translateInner = (entity2, projection2, path) => {
const alias = aliasDict[path];
const { attributes } = schema[entity2];
let projText = '';
let prefix = path.slice(2).replace(/\//g, '.');
const attrs = Object.keys(projection2).filter((attr) => {
};
SqlTranslator.prototype.translateProjection = function (entity, projection, aliasDict, projectionRefAlias) {
var _this = this;
var schema = this.schema;
var translateInner = function (entity2, projection2, path) {
var alias = aliasDict[path];
var attributes = schema[entity2].attributes;
var projText = '';
var prefix = path.slice(2).replace(/\//g, '.');
var attrs = Object.keys(projection2).filter(function (attr) {
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
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';
});
attrs.forEach((attr, idx) => {
attrs.forEach(function (attr, idx) {
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
const exprText = this.translateExpression(alias, projection2[attr], projectionRefAlias);
projText += ` ${exprText} as ${prefix}${attr}`;
var exprText = _this.translateExpression(alias, projection2[attr], projectionRefAlias);
projText += " ".concat(exprText, " as ").concat(prefix).concat(attr);
}
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') {
projText += translateInner(rel, projection2[attr], `${path}${attr}/`);
projText += translateInner(rel, projection2[attr], "".concat(path).concat(attr, "/"));
}
else if (rel === 2) {
projText += translateInner(attr, projection2[attr], `${path}${attr}/`);
projText += translateInner(attr, projection2[attr], "".concat(path).concat(attr, "/"));
}
else if (rel === 1) {
const { type } = attributes[attr];
var type = attributes[attr].type;
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 {
(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 translateInner(entity, projection, './');
}
translateSelectInner(entity, selection, initialNumber, refAlias, params) {
const { data, filter, sorter, indexFrom, count } = selection;
const { from: fromText, aliasDict, projectionRefAlias, extraWhere, filterRefAlias, currentNumber } = this.analyzeJoin(entity, {
};
SqlTranslator.prototype.translateSelectInner = function (entity, selection, initialNumber, refAlias, option) {
var data = selection.data, filter = selection.filter, sorter = selection.sorter, indexFrom = selection.indexFrom, count = selection.count;
var _a = this.analyzeJoin(entity, {
projection: data,
filter,
sorter,
}, initialNumber);
filter: filter,
sorter: sorter,
}, 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, lodash_1.assign)(refAlias, filterRefAlias);
const projText = this.translateProjection(entity, data, aliasDict, projectionRefAlias);
const { stmt: filterText, currentNumber: currentNumber2 } = this.translateFilter(entity, selection, aliasDict, refAlias, currentNumber, extraWhere);
const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
var projText = this.translateProjection(entity, data, aliasDict, projectionRefAlias);
var _b = this.translateFilter(entity, selection, aliasDict, refAlias, currentNumber, extraWhere, option), filterText = _b.stmt, currentNumber2 = _b.currentNumber;
var sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
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,
};
}
translateSelect(entity, selection, params) {
const { stmt } = this.translateSelectInner(entity, selection, 1, {}, params);
};
SqlTranslator.prototype.translateSelect = function (entity, selection, option) {
var stmt = this.translateSelectInner(entity, selection, 1, {}, option).stmt;
return stmt;
}
translateCount(entity, selection, params) {
const { filter } = selection;
const { from: fromText, aliasDict, extraWhere, filterRefAlias, currentNumber } = this.analyzeJoin(entity, {
filter,
});
const projText = 'count(1)';
const { stmt: filterText } = this.translateFilter(entity, selection, aliasDict, filterRefAlias, currentNumber, extraWhere);
return this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, undefined, undefined, undefined);
}
translateRemove(entity, operation, params) {
const { filter, sorter, indexFrom, count } = operation;
const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter });
const alias = aliasDict['./'];
const { stmt: filterText } = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere);
const sorterText = sorter && sorter.length > 0 ? this.translateSorter(entity, sorter, aliasDict) : undefined;
return this.populateRemoveStmt(alias, fromText, aliasDict, filterText, sorterText, indexFrom, count, params);
}
translateUpdate(entity, operation, params) {
const { attributes } = this.schema[entity];
const { filter, sorter, indexFrom, count, data } = operation;
const { aliasDict, filterRefAlias, extraWhere, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter });
const alias = aliasDict['./'];
let updateText = '';
for (const attr in data) {
};
SqlTranslator.prototype.translateCount = function (entity, selection, option) {
var filter = selection.filter;
var _a = this.analyzeJoin(entity, {
filter: filter,
}), fromText = _a.from, aliasDict = _a.aliasDict, extraWhere = _a.extraWhere, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber;
var projText = 'count(1)';
var filterText = this.translateFilter(entity, selection, aliasDict, filterRefAlias, currentNumber, extraWhere, option).stmt;
return this.populateSelectStmt(projText, fromText, selection, aliasDict, filterText, undefined, undefined, undefined, option);
};
SqlTranslator.prototype.translateRemove = function (entity, operation, option) {
var filter = operation.filter, sorter = operation.sorter, indexFrom = operation.indexFrom, count = operation.count;
var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, extraWhere = _a.extraWhere, fromText = _a.from, currentNumber = _a.currentNumber;
var alias = aliasDict['./'];
var filterText = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere).stmt;
var sorterText = sorter && sorter.length > 0 ? this.translateSorter(entity, sorter, aliasDict) : undefined;
return this.populateRemoveStmt(alias, fromText, aliasDict, filterText, sorterText, indexFrom, count, option);
};
SqlTranslator.prototype.translateUpdate = function (entity, operation, option) {
var attributes = this.schema[entity].attributes;
var filter = operation.filter, sorter = operation.sorter, indexFrom = operation.indexFrom, count = operation.count, data = operation.data;
var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, extraWhere = _a.extraWhere, fromText = _a.from, currentNumber = _a.currentNumber;
var alias = aliasDict['./'];
var updateText = '';
for (var attr in data) {
if (updateText) {
updateText += ',';
}
(0, assert_1.default)(attributes.hasOwnProperty(attr) && attributes[attr].type !== 'ref');
const value = this.translateAttrValue(attributes[attr].type, data[attr]);
updateText += `\`${alias}\`.\`${attr}\` = ${value}`;
var value = this.translateAttrValue(attributes[attr].type, data[attr]);
updateText += "`".concat(alias, "`.`").concat(attr, "` = ").concat(value);
}
const { stmt: filterText } = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere);
const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
return this.populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, params);
}
translateDestroyEntity(entity, truncate) {
const { schema } = this;
const { storageName = entity, view } = schema[entity];
let sql;
var filterText = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere).stmt;
var sorterText = sorter && this.translateSorter(entity, sorter, aliasDict);
return this.populateUpdateStmt(updateText, fromText, aliasDict, filterText, sorterText, indexFrom, count, option);
};
SqlTranslator.prototype.translateDestroyEntity = function (entity, truncate) {
var schema = this.schema;
var _a = schema[entity], _b = _a.storageName, storageName = _b === void 0 ? entity : _b, view = _a.view;
var sql;
if (view) {
sql = `drop view if exists \`${storageName}\``;
sql = "drop view if exists `".concat(storageName, "`");
}
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;
}
escapeStringValue(value) {
const result = `'${value.replace(/'/g, '\\\'')}'`;
};
SqlTranslator.prototype.escapeStringValue = function (value) {
var result = "'".concat(value.replace(/'/g, '\\\''), "'");
return result;
}
}
};
return 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 { MySQLConfiguration } from './types/Configuration';
import { MySqlConnector } from './connector';
import { MySqlTranslator, MySqlSelectParams } from './translator';
import { MySqlTranslator, MySqlSelectOption, MysqlOperateOption } from './translator';
import { assign } from 'lodash';
import assert from 'assert';
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,
selection: S,
context: Cxt,
params?: OperateParams & MySqlSelectParams
option?: MySqlSelectOption
): 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());
return this.formResult(entity, result);
@ -179,7 +179,7 @@ export class MysqlStore<ED extends EntityDict, Cxt extends Context<ED>> extends
entity: T,
operation: DeduceCreateMultipleOperation<ED[T]['Schema']> | DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>,
context: Cxt,
params?: OperateParams & MySqlSelectParams
option?: MysqlOperateOption
): Promise<number> {
const { translator, connector } = this;
const { action } = operation;
@ -190,55 +190,61 @@ export class MysqlStore<ED extends EntityDict, Cxt extends Context<ED>> extends
const { data } = operation as DeduceCreateMultipleOperation<ED[T]['Schema']> | DeduceCreateSingleOperation<ED[T]['Schema']>;
const sql = translator.translateInsert(entity, data instanceof Array ? data : [data]);
await connector.exec(sql, txn);
context.opRecords.push({
a: 'c',
d: data as any,
e: entity,
});
if (!option?.notCollect) {
context.opRecords.push({
a: 'c',
d: data as any,
e: entity,
});
}
return data instanceof Array ? data.length : 1;
}
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);
// todo 这里对sorter和indexfrom/count的支持不完整
context.opRecords.push({
a: 'r',
e: entity,
f: (operation as ED[T]['Remove']).filter,
});
if (!option?.notCollect) {
context.opRecords.push({
a: 'r',
e: entity,
f: (operation as ED[T]['Remove']).filter,
});
}
return 1;
}
default: {
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);
// todo 这里对sorter和indexfrom/count的支持不完整
context.opRecords.push({
a: 'u',
e: entity,
d: (operation as ED[T]['Update']).data,
f: (operation as ED[T]['Update']).filter,
})
if (!option?.notCollect) {
context.opRecords.push({
a: 'u',
e: entity,
d: (operation as ED[T]['Update']).data,
f: (operation as ED[T]['Update']).filter,
});
}
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;
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']>> {
const result = await this.cascadeSelect(entity, selection, context, params);
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, option);
return {
result,
};
}
async count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter'>, context: Cxt, params?: Object): Promise<number> {
const sql = this.translator.translateCount(entity, selection, params);
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, option);
const result = await this.connector.exec(sql, context.getCurrentTxnId());
return result.count as number;
}

View File

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

View File

@ -1,11 +1,13 @@
import assert from 'assert';
import { assign, cloneDeep, intersection, keys, set } from 'lodash';
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 { 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> {
@ -112,7 +114,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
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;
@ -122,7 +124,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
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,
fromText: string,
selection: ED[T]['Selection'],
@ -130,9 +132,10 @@ export abstract class SqlTranslator<ED extends EntityDict> {
filterText: string,
sorterText?: string,
indexFrom?: number,
count?: number): string;
count?: number,
option?: OP): string;
protected abstract populateUpdateStmt(
protected abstract populateUpdateStmt<OP extends SqlOperateOption>(
updateText: string,
fromText: string,
aliasDict: Record<string, string>,
@ -140,9 +143,9 @@ export abstract class SqlTranslator<ED extends EntityDict> {
sorterText?: string,
indexFrom?: number,
count?: number,
params?: any): string;
option?: OP): string;
protected abstract populateRemoveStmt(
protected abstract populateRemoveStmt<OP extends SqlOperateOption>(
removeText: string,
fromText: string,
aliasDict: Record<string, string>,
@ -150,7 +153,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
sorterText?: string,
indexFrom?: 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;
@ -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,
selection: Pick<ED[T]['Selection'], 'filter' | 'hint'>,
selection: Pick<ED[T]['Selection'], 'filter'>,
aliasDict: Record<string, string>,
filterRefAlias: Record<string, string>,
initialNumber: number,
extraWhere?: string): {
extraWhere?: string,
option?: OP): {
stmt: string;
currentNumber: number;
} {
const { schema } = this;
const { filter, hint } = selection;
const { filter } = selection;
let currentNumber = initialNumber;
const translateInner = <E extends keyof ED>(entity2: E, path: string, filter2?: ED[E]['Selection']['filter'], type?: DataType | Ref): string => {
const alias = aliasDict[path];
const { attributes } = schema[entity2];
let whereText = type ? '' : this.getDefaultSelectFilter(alias, hint);
let whereText = type ? '' : this.getDefaultSelectFilter(alias, option);
if (filter2) {
const attrs = Object.keys(filter2).filter(
ele => !ele.startsWith('#')
@ -810,7 +814,7 @@ export abstract class SqlTranslator<ED extends EntityDict> {
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;
currentNumber: number;
} {
@ -825,36 +829,39 @@ export abstract class SqlTranslator<ED extends EntityDict> {
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);
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,
};
}
translateSelect<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], params?: SelectParams): string {
const { stmt } = this.translateSelectInner(entity, selection, 1, {}, params);
translateSelect<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: ED[T]['Selection'], option?: OP): string {
const { stmt } = this.translateSelectInner(entity, selection, 1, {}, option);
return stmt;
}
translateCount<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter'>, params?: SelectParams): string {
const { filter } = selection;
translateCount<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, option?: OP): string {
const { filter, count } = selection;
const { from: fromText, aliasDict, extraWhere, filterRefAlias, currentNumber } = this.analyzeJoin(entity, {
filter,
});
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);
return this.populateSelectStmt(projText, fromText, selection as ED[T]['Selection'], aliasDict, filterText, undefined, undefined, undefined);
if (count) {
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 { 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;
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 { filter, sorter, indexFrom, count, data } = operation;
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 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 {

View File

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