Skip to content
Closed
2 changes: 1 addition & 1 deletion frontend/src/models/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ const System = {

/**
* Validates a SQL connection string.
* @param {'postgresql'|'mysql'|'sql-server'} engine - the database engine identifier
* @param {'postgresql'|'mysql'|'sql-server'|'oracle'} engine - the database engine identifier
* @param {string} connectionString - the connection string to validate
* @returns {Promise<{success: boolean, error: string | null}>}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import PostgreSQLLogo from "./icons/postgresql.png";
import MySQLLogo from "./icons/mysql.png";
import MSSQLLogo from "./icons/mssql.png";
import OracleLogo from "./icons/oracle.png";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PNG needs a white bg and be 330x330px if not already

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes are made in the Review PR


import { PencilSimple, X } from "@phosphor-icons/react";
import { useModal } from "@/hooks/useModal";
import EditSQLConnection from "./SQLConnectionModal";
Expand All @@ -9,6 +11,7 @@ export const DB_LOGOS = {
postgresql: PostgreSQLLogo,
mysql: MySQLLogo,
"sql-server": MSSQLLogo,
oracle: OracleLogo,
};

export default function DBConnection({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ function assembleConnectionString({
return `mysql://${username}:${password}@${host}:${port}/${database}`;
case "sql-server":
return `mssql://${username}:${password}@${host}:${port}/${database}?encrypt=${encrypt}`;
case "oracle":
return `oracle://${username}:${password}@${host}:${port}/${database}`;
default:
return null;
}
Expand Down Expand Up @@ -318,6 +320,11 @@ export default function SQLConnectionModal({
active={engine === "sql-server"}
onClick={() => setEngine("sql-server")}
/>
<DBEngine
provider="oracle"
active={engine === "oracle"}
onClick={() => setEngine("oracle")}
/>
</div>
</div>

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"mysql2": "^3.9.8",
"ollama": "^0.6.3",
"openai": "4.95.1",
"oracledb": "^6.10.0",
"pg": "^8.11.5",
"pinecone-client": "^1.1.0",
"pluralize": "^8.0.0",
Expand Down
115 changes: 115 additions & 0 deletions server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/Oracle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@


const { ConnectionStringParser } = require("./utils");

// -- oracledb.initOracleClient({libDir: "/opt/oracle/instantclient_23_26",});
// Thin mode: DO NOT call initOracleClient()





class OracleConnector {
#connected = false;
database_id = "";

constructor(
config = {
connectionString: null,
}
) {
// Load the Oracle driver on demand when this constructor is called
this.oracledb = require("oracledb");

// Set global output format once (will be set each time an instance is created,
// but that's harmless due to caching)
this.oracledb.outFormat = this.oracledb.OUT_FORMAT_OBJECT;

// Log thin mode status when an instance is created
console.log("Oracle driver thin mode:", oracledb.thin);

this.className = "OracleConnector";
this.connectionString = config.connectionString;
this._client = null;
this.database_id = this.#parseDatabase();
}

#parseDatabase() {
const connectionParser = new ConnectionStringParser({ scheme: "oracle" });
const parsed = connectionParser.parse(this.connectionString);
return parsed?.endpoint; // service name
}

async connect() {
const connectionParser = new ConnectionStringParser({ scheme: "oracle" });
const parsed = connectionParser.parse(this.connectionString);

const host = parsed.hosts[0]?.host;
const port = parsed.hosts[0]?.port || 1521;
const service = parsed.endpoint;

this._client = await oracledb.getConnection({
user: parsed.username,
password: parsed.password,
connectString: `${host}:${port}/${service}`,
});

this.#connected = true;
return this._client;
}

/**
* @param {string} queryString
* @returns {Promise<import(".").QueryResult>}
*/
async runQuery(queryString = "") {
const result = { rows: [], count: 0, error: null };

try {
if (!this.#connected) await this.connect();

const query = await this._client.execute(queryString, [], {
autoCommit: true,
});

result.rows = query.rows || [];
result.count = query.rows ? query.rows.length : 0;
} catch (err) {
console.log(this.className, err);
result.error = err.message;
} finally {
if (this._client) {
await this._client.close();
this.#connected = false;
}
}

return result;
}

async validateConnection() {
try {
const result = await this.runQuery("SELECT 1 FROM DUAL");
return { success: !result.error, error: result.error };
} catch (error) {
return { success: false, error: error.message };
}
}

getTablesSql() {
return `
SELECT table_name
FROM user_tables
`;
}

getTableSchemaSql(table_name) {
return `
SELECT column_name, data_type
FROM user_tab_columns
WHERE table_name = UPPER('${table_name}')
`;
}
}

module.exports.OracleConnector = OracleConnector;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { SystemSettings } = require("../../../../../../models/systemSettings");
const { safeJsonParse } = require("../../../../../http");

/**
* @typedef {('postgresql'|'mysql'|'sql-server')} SQLEngine
* @typedef {('postgresql'|'mysql'|'sql-server'|'oracle')} SQLEngine
*/

/**
Expand Down Expand Up @@ -36,6 +36,9 @@ function getDBClient(identifier = "", connectionConfig = {}) {
case "sql-server":
const { MSSQLConnector } = require("./MSSQL");
return new MSSQLConnector(connectionConfig);
case "oracle":
const { OracleConnector } = require("./Oracle");
return new OracleConnector(connectionConfig);
default:
throw new Error(
`There is no supported database connector for ${identifier}`
Expand Down Expand Up @@ -64,8 +67,8 @@ async function validateConnection(identifier = "", connectionConfig = {}) {
try {
const client = getDBClient(identifier, connectionConfig);
return await client.validateConnection();
} catch {
console.log(`Failed to connect to ${identifier} database.`);
} catch (error) {
console.log(`Failed to connect to ${identifier} database.`, error);
return {
success: false,
error: `Unable to connect to ${identifier}. Please verify your connection details.`,
Expand Down