UNPKG

artmapper

Version:

A simple and intuitive ORM for Node.js with TypeScript and JavaScript support

354 lines 12.4 kB
"use strict"; 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