@reldens/storage
Version:
231 lines (216 loc) • 7.83 kB
JavaScript
/**
*
* Reldens - PrismaDataServer
*
*/
const { BaseDataServer } = require('../base-data-server');
const { PrismaDriver } = require('./prisma-driver');
const { MySQLTablesProvider } = require('../mysql-tables-provider');
const { PrismaClient } = require('@prisma/client');
const { Logger, sc } = require('@reldens/utils');
class PrismaDataServer extends BaseDataServer
{
constructor(props)
{
super(props);
this.prisma = sc.get(props, 'prismaClient', false);
}
async connect()
{
if(this.initialized){
return this.initialized;
}
try {
if(!this.prisma){
this.prisma = new PrismaClient({
datasources: {db: {url: this.connectString}},
log: this.debug ? ['query', 'info', 'warn', 'error'] : ['error']
});
}
await this.prisma.$connect();
let dbTest = await this.prisma.$queryRaw`SELECT DATABASE() as current_db`;
Logger.info('Connected to database: ' + dbTest[0].current_db);
this.initialized = Date.now();
return this.initialized;
} catch(error) {
Logger.critical('Connection failed, Prisma error: '+error.message);
}
return false;
}
generateEntities()
{
if(!this.initialized){
Logger.warning('Connection was not initialized, please use the connect method first.');
return {};
}
if(!this.rawEntities){
Logger.warning('Empty raw entities array, none entities generated.');
return {};
}
this.entities = {};
for(let i of Object.keys(this.rawEntities)){
let rawEntity = this.rawEntities[i];
let tableName = rawEntity.tableName;
let prismaModel = this.prisma[tableName];
if(!prismaModel){
Logger.critical('Invalid raw entity "'+i+'". No matching Prisma model found for "'+tableName+'".');
continue;
}
this.entities[i] = new PrismaDriver({
rawModel: rawEntity,
id: i,
name: i,
config: this.config,
prisma: this.prisma,
model: prismaModel,
server: this
});
}
this.entityManager.setEntities(this.entities);
return this.entities;
}
name()
{
return this.name || 'Prisma Data Server Driver';
}
async rawQuery(content)
{
try {
let statements = this.splitSqlStatements(content);
let results = [];
for(let statement of statements){
let cleanStatement = statement.trim();
if('' === cleanStatement){
continue;
}
try {
let result = await this.executeStatement(cleanStatement);
results.push(result);
//Logger.debug('Statement executed, result: ' + JSON.stringify(result));
} catch(stmtError) {
Logger.error('Statement "'+cleanStatement+'" execution failed: '+stmtError.message);
return false;
}
}
if(0 === results.length){
//Logger.error('Statement results length is zero.', results);
return false;
}
return 1 === results.length ? results[0] : results;
} catch(error) {
Logger.error('Raw query failed: '+error.message);
return false;
}
}
async executeStatement(statement)
{
let trimmedStatement = statement.trim().toUpperCase();
if(trimmedStatement.startsWith('SELECT')){
return await this.prisma.$queryRawUnsafe(statement);
}
if(trimmedStatement.startsWith('SHOW')){
return await this.prisma.$queryRawUnsafe(statement);
}
let result = await this.prisma.$executeRawUnsafe(statement);
Logger.debug('Raw result from Prisma: ' + result);
if(
trimmedStatement.startsWith('CREATE')
|| trimmedStatement.startsWith('ALTER')
|| trimmedStatement.startsWith('DROP')
){
return { affectedRows: result };
}
return result;
}
splitSqlStatements(sqlContent)
{
let statements = [];
let currentStatement = '';
let inQuote = false;
let quoteChar = '';
let inComment = false;
let commentType = '';
for(let i = 0; i < sqlContent.length; i++){
let char = sqlContent.charAt(i);
let nextChar = i < sqlContent.length - 1 ? sqlContent.charAt(i + 1) : '';
if(inComment){
let shouldEndComment = false;
if('*' === commentType && '*' === char && '/' === nextChar){
shouldEndComment = true;
i++;
}
if('-' === commentType && '\n' === char){
shouldEndComment = true;
}
if(shouldEndComment){
inComment = false;
}
continue;
}
let shouldStartComment = this.shouldStartComment(char, nextChar, inQuote);
if(shouldStartComment){
inComment = true;
commentType = '/' === char ? '*' : '-';
continue;
}
let isQuoteChar = ('"' === char || '\'' === char || '`' === char);
let isEscaped = (i > 0 && '\\' === sqlContent.charAt(i-1));
if(isQuoteChar && !isEscaped){
if(!inQuote){
inQuote = true;
quoteChar = char;
currentStatement += char;
continue;
}
let shouldEndQuote = quoteChar === char;
if(shouldEndQuote){
inQuote = false;
}
currentStatement += char;
continue;
}
let shouldEndStatement = (';' === char && !inQuote);
if(shouldEndStatement){
statements.push(currentStatement);
currentStatement = '';
continue;
}
currentStatement += char;
}
let hasRemainingStatement = '' !== currentStatement.trim();
if(hasRemainingStatement){
statements.push(currentStatement);
}
return statements;
}
shouldStartComment(char, nextChar, inQuote)
{
if(inQuote){
return false;
}
let isBlockComment = ('/' === char && '*' === nextChar);
let isLineComment = ('-' === char && '-' === nextChar);
return isBlockComment || isLineComment;
}
async fetchEntitiesFromDatabase()
{
if(!this.initialized){
Logger.critical('Connection was not initialized, please use the connect method first.');
return false;
}
try {
return await MySQLTablesProvider.fetchTables(this);
} catch(error) {
Logger.critical('Prisma Data Server tables fetch failed. '+error.message);
return false;
}
}
async disconnect()
{
if(this.prisma){
await this.prisma.$disconnect();
}
this.initialized = false;
}
}
module.exports.PrismaDataServer = PrismaDataServer;