UNPKG

adaptus2-orm

Version:

A multi-database ORM supporting MySQL, PostgreSQL, MongoDB, and Snowflake

594 lines (532 loc) 19.1 kB
// src/adapters/PostgreSQLAdapter.js class PostgreSQLAdapter { async create(connection, table, data) { try { const fields = Object.keys(data); const values = Object.values(data); const placeholders = fields.map((_, index) => `$${index + 1}`).join(','); const query = `INSERT INTO ${table} (${fields.join(',')}) VALUES (${placeholders}) RETURNING *`; const client = await connection.pool.connect(); try { const result = await client.query(query, values); return { success: true, data: { record: result.rows[0], affectedRows: result.rowCount } }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async read(connection, table, params = {}) { try { let query = `SELECT * FROM ${table}`; let queryParams = []; let paramCounter = 1; if (params.where) { const conditions = Object.keys(params.where).map(key => { queryParams.push(params.where[key]); return `${key} = $${paramCounter++}`; }).join(' AND '); query += ` WHERE ${conditions}`; } if (params.orderBy) { query += ` ORDER BY ${params.orderBy} ${params.order || 'ASC'}`; } if (params.limit) { query += ` LIMIT $${paramCounter++}`; queryParams.push(parseInt(params.limit)); if (params.offset) { query += ` OFFSET $${paramCounter++}`; queryParams.push(parseInt(params.offset)); } } const client = await connection.pool.connect(); try { const result = await client.query(query, queryParams); return { success: true, data: result.rows }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async update(connection, table, params) { try { const { where, data } = params; if (!where || !data) { throw new Error('Both where and data parameters are required for update'); } let paramCounter = 1; const setFields = Object.keys(data).map(key => { return `${key} = $${paramCounter++}`; }).join(','); const setValues = Object.values(data); const whereFields = Object.keys(where).map(key => { return `${key} = $${paramCounter++}`; }).join(' AND '); const whereValues = Object.values(where); const query = `UPDATE ${table} SET ${setFields} WHERE ${whereFields} RETURNING *`; const queryParams = [...setValues, ...whereValues]; const client = await connection.pool.connect(); try { const result = await client.query(query, queryParams); return { success: true, data: { records: result.rows, affectedRows: result.rowCount } }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async delete(connection, table, params) { try { if (!params.where) { throw new Error('Where parameter is required for delete'); } let paramCounter = 1; const whereFields = Object.keys(params.where).map(key => { return `${key} = $${paramCounter++}`; }).join(' AND '); const whereValues = Object.values(params.where); const query = `DELETE FROM ${table} WHERE ${whereFields} RETURNING *`; const client = await connection.pool.connect(); try { const result = await client.query(query, whereValues); return { success: true, data: { deletedRecords: result.rows, affectedRows: result.rowCount } }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async exists(connection, table, params) { try { let query = `SELECT EXISTS(SELECT 1 FROM ${table}`; let queryParams = []; let paramCounter = 1; if (params.where) { const conditions = Object.keys(params.where).map(key => { queryParams.push(params.where[key]); return `${key} = $${paramCounter++}`; }).join(' AND '); query += ` WHERE ${conditions}`; } query += ')'; const client = await connection.pool.connect(); try { const result = await client.query(query, queryParams); return { success: true, data: result.rows[0].exists }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async query(connection, queryString, params = []) { try { const client = await connection.pool.connect(); try { const result = await client.query(queryString, params); return { success: true, data: result.rows || result }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async initDatabase(connection, config) { try { const client = await connection.pool.connect(); try { await client.query('SELECT 1'); return { success: true, data: 'Database initialized successfully' }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Utility methods for batch operations async bulkInsert(connection, table, records) { try { if (!records.length) { return { success: true, data: { affectedRows: 0 } }; } const fields = Object.keys(records[0]); const placeholders = records.map((_, recordIndex) => { return `(${fields.map((_, fieldIndex) => `$${recordIndex * fields.length + fieldIndex + 1}` ).join(',')})`; }).join(','); const query = `INSERT INTO ${table} (${fields.join(',')}) VALUES ${placeholders} RETURNING *`; const allValues = []; records.forEach(record => { allValues.push(...Object.values(record)); }); const client = await connection.pool.connect(); try { const result = await client.query(query, allValues); return { success: true, data: { records: result.rows, affectedRows: result.rowCount } }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async upsert(connection, table, data, conflictFields = [], updateFields = []) { try { const fields = Object.keys(data); const values = Object.values(data); const placeholders = fields.map((_, index) => `$${index + 1}`).join(','); let query = `INSERT INTO ${table} (${fields.join(',')}) VALUES (${placeholders})`; if (conflictFields.length > 0) { query += ` ON CONFLICT (${conflictFields.join(',')}) DO`; if (updateFields.length > 0) { const setClause = updateFields.map(field => `${field} = EXCLUDED.${field}` ).join(','); query += ` UPDATE SET ${setClause}`; } else { query += ' NOTHING'; } } query += ' RETURNING *'; const client = await connection.pool.connect(); try { const result = await client.query(query, values); return { success: true, data: { record: result.rows[0], affectedRows: result.rowCount } }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Transaction support async beginTransaction(connection) { try { const client = await connection.pool.connect(); await client.query('BEGIN'); return { success: true, client }; } catch (error) { return { success: false, error: error.message }; } } async commit(client) { try { await client.query('COMMIT'); client.release(); return { success: true }; } catch (error) { return { success: false, error: error.message }; } } async rollback(client) { try { await client.query('ROLLBACK'); client.release(); return { success: true }; } catch (error) { return { success: false, error: error.message }; } } // Advanced PostgreSQL features async createEnum(connection, enumName, values) { try { const query = `CREATE TYPE ${enumName} AS ENUM (${values.map(v => `'${v}'`).join(',')})`; const client = await connection.pool.connect(); try { await client.query(query); return { success: true, data: `Enum ${enumName} created successfully` }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } async dropEnum(connection, enumName) { try { const query = `DROP TYPE IF EXISTS ${enumName}`; const client = await connection.pool.connect(); try { await client.query(query); return { success: true, data: `Enum ${enumName} dropped successfully` }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // JSONB operations async jsonbQuery(connection, table, column, jsonPath, params = {}) { try { let query = `SELECT * FROM ${table} WHERE ${column} @> $1`; let queryParams = [jsonPath]; let paramCounter = 2; if (params.where) { const conditions = Object.keys(params.where).map(key => { queryParams.push(params.where[key]); return `${key} = $${paramCounter++}`; }).join(' AND '); query += ` AND ${conditions}`; } const client = await connection.pool.connect(); try { const result = await client.query(query, queryParams); return { success: true, data: result.rows }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Array operations async arrayContains(connection, table, column, value, params = {}) { try { let query = `SELECT * FROM ${table} WHERE $1 = ANY(${column})`; let queryParams = [value]; let paramCounter = 2; if (params.where) { const conditions = Object.keys(params.where).map(key => { queryParams.push(params.where[key]); return `${key} = $${paramCounter++}`; }).join(' AND '); query += ` AND ${conditions}`; } const client = await connection.pool.connect(); try { const result = await client.query(query, queryParams); return { success: true, data: result.rows }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Full-text search async fullTextSearch(connection, table, column, searchText, params = {}) { try { let query = `SELECT * FROM ${table} WHERE to_tsvector('english', ${column}) @@ to_tsquery('english', $1)`; let queryParams = [searchText]; let paramCounter = 2; if (params.where) { const conditions = Object.keys(params.where).map(key => { queryParams.push(params.where[key]); return `${key} = $${paramCounter++}`; }).join(' AND '); query += ` AND ${conditions}`; } // Add ranking if requested if (params.ranked) { query = query.replace('SELECT *', 'SELECT *, ts_rank_cd(to_tsvector(\'english\', ' + column + '), to_tsquery(\'english\', $1)) AS rank'); query += ' ORDER BY rank DESC'; } const client = await connection.pool.connect(); try { const result = await client.query(query, queryParams); return { success: true, data: result.rows }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Create extension async createExtension(connection, extensionName) { try { const query = `CREATE EXTENSION IF NOT EXISTS ${extensionName}`; const client = await connection.pool.connect(); try { await client.query(query); return { success: true, data: `Extension ${extensionName} created successfully` }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Vacuum and analyze async vacuum(connection, table, options = {}) { try { let query = 'VACUUM'; if (options.full) query += ' FULL'; if (options.analyze) query += ' ANALYZE'; if (table) query += ` ${table}`; const client = await connection.pool.connect(); try { await client.query(query); return { success: true, data: 'Vacuum completed successfully' }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Create materialized view async createMaterializedView(connection, viewName, query) { try { const createQuery = `CREATE MATERIALIZED VIEW ${viewName} AS ${query}`; const client = await connection.pool.connect(); try { await client.query(createQuery); return { success: true, data: `Materialized view ${viewName} created successfully` }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } // Refresh materialized view async refreshMaterializedView(connection, viewName, concurrently = false) { try { const query = `REFRESH MATERIALIZED VIEW ${concurrently ? 'CONCURRENTLY' : ''} ${viewName}`; const client = await connection.pool.connect(); try { await client.query(query); return { success: true, data: `Materialized view ${viewName} refreshed successfully` }; } finally { client.release(); } } catch (error) { return { success: false, error: error.message }; } } } module.exports = PostgreSQLAdapter;