artmapper
Version:
A simple and intuitive ORM for Node.js with TypeScript and JavaScript support
354 lines • 12.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseConnection = void 0;
const types_1 = require("./types");
class DatabaseConnection {
constructor(config) {
this.config = config;
this.type = config.type;
}
/**
* Connect to the database
*/
async connect() {
try {
switch (this.type) {
case 'mysql':
await this.connectMySQL();
break;
case 'postgresql':
await this.connectPostgreSQL();
break;
case 'sqlite':
await this.connectSQLite();
break;
case 'mongodb':
await this.connectMongoDB();
break;
default:
throw new types_1.ArtMapperError(`Unsupported database type: ${this.type}`);
}
}
catch (error) {
throw new types_1.ArtMapperError(`Failed to connect to database: ${error}`);
}
}
/**
* Execute a query
*/
async query(sql, params) {
if (!this.connection) {
throw new types_1.ArtMapperError('Database not connected');
}
try {
switch (this.type) {
case 'mysql':
return await this.executeMySQLQuery(sql, params);
case 'postgresql':
return await this.executePostgreSQLQuery(sql, params);
case 'sqlite':
return await this.executeSQLiteQuery(sql, params);
case 'mongodb':
return await this.executeMongoQuery(sql, params);
default:
throw new types_1.ArtMapperError(`Unsupported database type: ${this.type}`);
}
}
catch (error) {
throw new types_1.ArtMapperError(`Query failed: ${error}`);
}
}
/**
* Get database type
*/
getType() {
return this.type;
}
/**
* Close the connection
*/
async close() {
if (this.connection) {
try {
switch (this.type) {
case 'mysql':
await this.connection.end();
break;
case 'postgresql':
await this.connection.end();
break;
case 'sqlite':
await this.connection.close();
break;
case 'mongodb':
await this.connection.close();
break;
}
}
catch (error) {
console.error('Error closing connection:', error);
}
}
}
/**
* Connect to MySQL
*/
async connectMySQL() {
const mysql = require('mysql2/promise');
this.connection = await mysql.createConnection({
host: this.config.host || 'localhost',
port: this.config.port || 3306,
user: this.config.username,
password: this.config.password,
database: this.config.database,
...this.config.options
});
}
/**
* Connect to PostgreSQL
*/
async connectPostgreSQL() {
const { Client } = require('pg');
this.connection = new Client({
host: this.config.host || 'localhost',
port: this.config.port || 5432,
user: this.config.username,
password: this.config.password,
database: this.config.database,
...this.config.options
});
await this.connection.connect();
}
/**
* Connect to SQLite
*/
async connectSQLite() {
const sqlite3 = require('sqlite3').verbose();
const { open } = require('sqlite');
this.connection = await open({
filename: this.config.database,
driver: sqlite3.Database
});
}
/**
* Connect to MongoDB
*/
async connectMongoDB() {
const { MongoClient } = require('mongodb');
const url = this.buildMongoURL();
this.connection = new MongoClient(url, {
useNewUrlParser: true,
useUnifiedTopology: true,
...this.config.options
});
await this.connection.connect();
}
/**
* Build MongoDB connection URL
*/
buildMongoURL() {
if (this.config.host && this.config.username && this.config.password) {
return `mongodb://${this.config.username}:${this.config.password}@${this.config.host}:${this.config.port || 27017}/${this.config.database}`;
}
else if (this.config.host) {
return `mongodb://${this.config.host}:${this.config.port || 27017}/${this.config.database}`;
}
else {
return `mongodb://localhost:27017/${this.config.database}`;
}
}
/**
* Execute MySQL query with proper result handling
*/
async executeMySQLQuery(sql, params) {
const queryType = sql.trim().toLowerCase();
if (queryType.startsWith('select')) {
const [rows] = await this.connection.execute(sql, params);
return rows;
}
else if (queryType.startsWith('insert')) {
const [result] = await this.connection.execute(sql, params);
return { insertId: result.insertId, affectedRows: result.affectedRows };
}
else if (queryType.startsWith('update') || queryType.startsWith('delete')) {
const [result] = await this.connection.execute(sql, params);
return { affectedRows: result.affectedRows };
}
else {
// For other queries like CREATE, DROP, etc.
const [result] = await this.connection.execute(sql, params);
return result;
}
}
/**
* Execute PostgreSQL query with proper result handling
*/
async executePostgreSQLQuery(sql, params) {
const queryType = sql.trim().toLowerCase();
if (queryType.startsWith('select')) {
const result = await this.connection.query(sql, params);
return result.rows;
}
else if (queryType.startsWith('insert')) {
const result = await this.connection.query(sql, params);
return { insertId: result.rows[0]?.id, affectedRows: result.rowCount };
}
else if (queryType.startsWith('update') || queryType.startsWith('delete')) {
const result = await this.connection.query(sql, params);
return { affectedRows: result.rowCount };
}
else {
// For other queries like CREATE, DROP, etc.
const result = await this.connection.query(sql, params);
return result;
}
}
/**
* Execute SQLite query with proper result handling
*/
async executeSQLiteQuery(sql, params) {
const queryType = sql.trim().toLowerCase();
if (queryType.startsWith('select')) {
return await this.connection.all(sql, params);
}
else if (queryType.startsWith('insert')) {
const result = await this.connection.run(sql, params);
return { insertId: result.lastID, affectedRows: result.changes };
}
else if (queryType.startsWith('update') || queryType.startsWith('delete')) {
const result = await this.connection.run(sql, params);
return { affectedRows: result.changes };
}
else {
// For other queries like CREATE, DROP, etc.
return await this.connection.run(sql, params);
}
}
/**
* Execute MongoDB query (simplified)
*/
async executeMongoQuery(sql, params) {
const db = this.connection.db(this.config.database);
const collection = db.collection('default');
// Simple query parsing (in a real implementation, you'd want a proper SQL parser)
if (sql.toLowerCase().includes('select')) {
return await collection.find({}).toArray();
}
else if (sql.toLowerCase().includes('insert')) {
return await collection.insertOne(params?.[0] || {});
}
else if (sql.toLowerCase().includes('update')) {
return await collection.updateOne({}, { $set: params?.[0] || {} });
}
else if (sql.toLowerCase().includes('delete')) {
return await collection.deleteOne({});
}
throw new types_1.ArtMapperError(`Unsupported MongoDB query: ${sql}`);
}
/**
* Create a table based on model definition
*/
async createTable(definition) {
const { table, fields } = definition;
if (this.type === 'mongodb') {
// MongoDB doesn't need table creation
return;
}
const fieldDefinitions = Object.entries(fields).map(([name, field]) => {
let sqlType = this.mapFieldTypeToSQL(field.type);
if (field.primaryKey || field.primary) {
sqlType += ' PRIMARY KEY';
if (field.autoIncrement) {
// SQLite uses AUTOINCREMENT, others use AUTO_INCREMENT
if (this.type === 'sqlite') {
sqlType += ' AUTOINCREMENT';
}
else {
sqlType += ' AUTO_INCREMENT';
}
}
}
if (field.unique) {
sqlType += ' UNIQUE';
}
if (field.notNull || field.nullable === false) {
sqlType += ' NOT NULL';
}
if (field.default !== undefined) {
sqlType += ` DEFAULT ${this.quoteValue(field.default)}`;
}
return `${this.quoteField(name)} ${sqlType}`;
}).join(', ');
const sql = `CREATE TABLE IF NOT EXISTS ${this.quoteTable(table)} (${fieldDefinitions})`;
await this.query(sql);
}
/**
* Map field type to SQL type
*/
mapFieldTypeToSQL(type) {
switch (type.toLowerCase()) {
case 'string':
case 'varchar':
return this.type === 'sqlite' ? 'TEXT' : 'VARCHAR(255)';
case 'text':
return 'TEXT';
case 'number':
case 'int':
return this.type === 'sqlite' ? 'INTEGER' : 'INT';
case 'bigint':
return this.type === 'sqlite' ? 'INTEGER' : 'BIGINT';
case 'float':
case 'double':
return this.type === 'sqlite' ? 'REAL' : 'DOUBLE';
case 'boolean':
return this.type === 'sqlite' ? 'INTEGER' : 'BOOLEAN';
case 'date':
return this.type === 'sqlite' ? 'TEXT' : 'DATETIME';
case 'timestamp':
return this.type === 'sqlite' ? 'TEXT' : 'TIMESTAMP';
case 'json':
return this.type === 'sqlite' ? 'TEXT' : 'JSON';
default:
return this.type === 'sqlite' ? 'TEXT' : 'VARCHAR(255)';
}
}
/**
* Quote a field name
*/
quoteField(field) {
switch (this.type) {
case 'mysql':
return `\`${field}\``;
case 'postgresql':
return `"${field}"`;
case 'sqlite':
return `"${field}"`;
default:
return field;
}
}
/**
* Quote a table name
*/
quoteTable(table) {
return this.quoteField(table);
}
/**
* Quote a value
*/
quoteValue(value) {
if (typeof value === 'string') {
// Handle special SQL functions and keywords
if (value.toUpperCase() === 'CURRENT_TIMESTAMP' ||
value.toUpperCase() === 'CURRENT_DATE' ||
value.toUpperCase() === 'CURRENT_TIME' ||
value.toUpperCase() === 'NULL') {
return value.toUpperCase();
}
return `'${value.replace(/'/g, "''")}'`;
}
return String(value);
}
}
exports.DatabaseConnection = DatabaseConnection;
//# sourceMappingURL=connection.js.map