fix(query-runner): dialecto SQL correcto por tipo de BD en helpers DBA
- Detecta isPostgres desde tipo_db.nombre al cambiar conexión
- sqlHelpers: getter dinámico — chips distintos para PostgreSQL vs MySQL
PostgreSQL: pg_database, information_schema, pg_stat_activity, pg_indexes
MySQL: SHOW DATABASES/TABLES/USERS/PROCESSLIST/CREATE TABLE
- generateDbHelper: genera SQL correcto por dialecto
PostgreSQL: sin backticks, sin charset, sin @host, DO$$ para CREATE USER IF NOT EXISTS,
GRANT ALL ON DATABASE + SCHEMA + DEFAULT PRIVILEGES
MySQL: backticks, utf8mb4, 'user'@'host', FLUSH PRIVILEGES
- Badge PostgreSQL/MySQL en header del generador DBA
- Campo Host oculto en conexiones PostgreSQL
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
parent
34f2c3e582
commit
c126c15ead
@@ -192,6 +192,8 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
<span class="font-semibold">Generador DBA</span>
|
||||
<span x-show="isPostgres" class="ml-1 text-[10px] px-1.5 py-0.5 rounded bg-blue-100 text-blue-600 font-medium">PostgreSQL</span>
|
||||
<span x-show="!isPostgres" class="ml-1 text-[10px] px-1.5 py-0.5 rounded bg-orange-100 text-orange-600 font-medium">MySQL</span>
|
||||
<span class="ml-auto text-[10px] text-gray-400">CREATE · GRANT · DROP · Setup</span>
|
||||
</button>
|
||||
<div x-show="showDbHelper" x-transition class="p-3 bg-white border-t">
|
||||
@@ -224,7 +226,7 @@
|
||||
class="w-full border rounded px-2 py-1 font-mono focus:outline-none focus:ring-1 focus:ring-[#8eb02f]"
|
||||
placeholder="usuario_app">
|
||||
</div>
|
||||
<div x-show="['full_setup','create_user','grant','revoke','show_grants','drop_user'].includes(dbHelper.op)">
|
||||
<div x-show="['full_setup','create_user','grant','revoke','show_grants','drop_user'].includes(dbHelper.op) && !isPostgres">
|
||||
<label class="text-[10px] text-gray-500 font-semibold uppercase block mb-0.5">Host</label>
|
||||
<input x-model="dbHelper.host"
|
||||
class="w-full border rounded px-2 py-1 font-mono focus:outline-none focus:ring-1 focus:ring-[#8eb02f]"
|
||||
@@ -441,6 +443,7 @@ document.addEventListener('alpine:init', () => {
|
||||
testOk: true,
|
||||
toast: { show: false, msg: '', type: 'ok' },
|
||||
isMongo: false,
|
||||
isPostgres: false,
|
||||
selectedCollection: '',
|
||||
collectionFields: [],
|
||||
loadingFields: false,
|
||||
@@ -450,16 +453,29 @@ document.addEventListener('alpine:init', () => {
|
||||
// SQL DBA helpers
|
||||
showDbHelper: false,
|
||||
dbHelper: { db: '', user: '', pass: '', host: '%', op: 'full_setup' },
|
||||
sqlHelpers: [
|
||||
{ label: 'SHOW DATABASES', cmd: 'SHOW DATABASES;' },
|
||||
{ label: 'SHOW TABLES', cmd: 'SHOW TABLES;' },
|
||||
{ label: 'SHOW USERS', cmd: "SELECT User, Host, plugin FROM mysql.user ORDER BY User;" },
|
||||
{ label: 'SHOW GRANTS (actual)',cmd: 'SHOW GRANTS FOR CURRENT_USER;' },
|
||||
{ label: 'SHOW PROCESSLIST', cmd: 'SHOW FULL PROCESSLIST;' },
|
||||
{ label: 'SHOW CREATE TABLE', cmd: 'SHOW CREATE TABLE nombre_tabla;' },
|
||||
{ label: 'DESC tabla', cmd: 'DESCRIBE nombre_tabla;' },
|
||||
{ label: 'ENGINE/CHARSET', cmd: "SELECT table_name, engine, table_collation FROM information_schema.tables WHERE table_schema = DATABASE();" },
|
||||
],
|
||||
get sqlHelpers() {
|
||||
if (this.isPostgres) return [
|
||||
{ label: 'SHOW DATABASES', cmd: 'SELECT datname FROM pg_database ORDER BY datname;' },
|
||||
{ label: 'SHOW TABLES', cmd: "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;" },
|
||||
{ label: 'SHOW USERS', cmd: "SELECT usename, usesuper, usecreatedb, usecreaterole FROM pg_catalog.pg_user ORDER BY usename;" },
|
||||
{ label: 'SHOW GRANTS', cmd: "SELECT grantee, table_name, privilege_type FROM information_schema.role_table_grants WHERE table_schema = 'public' ORDER BY grantee, table_name;" },
|
||||
{ label: 'SHOW CONNECTIONS', cmd: "SELECT pid, usename, application_name, state, query_start, LEFT(query,80) AS query FROM pg_stat_activity WHERE state IS NOT NULL;" },
|
||||
{ label: 'DESC tabla', cmd: "SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = 'nombre_tabla' ORDER BY ordinal_position;" },
|
||||
{ label: 'SHOW INDEXES', cmd: "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'nombre_tabla';" },
|
||||
{ label: 'SIZE db', cmd: "SELECT pg_size_pretty(pg_database_size(current_database())) AS size;" },
|
||||
];
|
||||
// MySQL / MariaDB
|
||||
return [
|
||||
{ label: 'SHOW DATABASES', cmd: 'SHOW DATABASES;' },
|
||||
{ label: 'SHOW TABLES', cmd: 'SHOW TABLES;' },
|
||||
{ label: 'SHOW USERS', cmd: "SELECT User, Host, plugin FROM mysql.user ORDER BY User;" },
|
||||
{ label: 'SHOW GRANTS', cmd: 'SHOW GRANTS FOR CURRENT_USER;' },
|
||||
{ label: 'SHOW PROCESSLIST', cmd: 'SHOW FULL PROCESSLIST;' },
|
||||
{ label: 'SHOW CREATE TABLE', cmd: 'SHOW CREATE TABLE nombre_tabla;' },
|
||||
{ label: 'DESC tabla', cmd: 'DESCRIBE nombre_tabla;' },
|
||||
{ label: 'ENGINE/CHARSET', cmd: "SELECT table_name, engine, table_collation FROM information_schema.tables WHERE table_schema = DATABASE();" },
|
||||
];
|
||||
},
|
||||
mongoExamples: [
|
||||
{ label: 'find()', cmd: 'db.coleccion.find({})' },
|
||||
{ label: 'findOne()', cmd: 'db.coleccion.findOne({})' },
|
||||
@@ -502,7 +518,9 @@ document.addEventListener('alpine:init', () => {
|
||||
this.collectionFields = [];
|
||||
if (!this.selectedConxId) return;
|
||||
const conx = this.conexiones.find(c => c.ID == this.selectedConxId);
|
||||
this.isMongo = conx?.tipo_db?.nombre?.toLowerCase().includes('mongo') ?? false;
|
||||
const dbType = conx?.tipo_db?.nombre?.toLowerCase() || '';
|
||||
this.isMongo = dbType.includes('mongo');
|
||||
this.isPostgres = dbType.includes('postgres');
|
||||
try {
|
||||
const res = await axios.get('/app/query-runner/databases?conx_db_id=' + this.selectedConxId);
|
||||
this.databases = res.data.data || [];
|
||||
@@ -699,25 +717,47 @@ document.addEventListener('alpine:init', () => {
|
||||
const user = this.dbHelper.user || 'usuario_app';
|
||||
const host = this.dbHelper.host || '%';
|
||||
const pass = this.dbHelper.pass || 'Contraseña123!';
|
||||
let map;
|
||||
|
||||
const createDb = `CREATE DATABASE IF NOT EXISTS \`${db}\`\n CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`;
|
||||
const createUser = `CREATE USER IF NOT EXISTS '${user}'@'${host}' IDENTIFIED BY '${pass}';`;
|
||||
const grant = `GRANT ALL PRIVILEGES ON \`${db}\`.* TO '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
||||
const revoke = `REVOKE ALL PRIVILEGES ON \`${db}\`.* FROM '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
||||
const showGrants = `SHOW GRANTS FOR '${user}'@'${host}';`;
|
||||
const dropUser = `DROP USER IF EXISTS '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
||||
const dropDb = `DROP DATABASE IF EXISTS \`${db}\`;`;
|
||||
|
||||
const map = {
|
||||
full_setup: `-- 1. Crear base de datos\n${createDb}\n\n-- 2. Crear usuario\n${createUser}\n\n-- 3. Dar permisos totales\n${grant}`,
|
||||
create_db: createDb,
|
||||
create_user: createUser,
|
||||
grant: grant,
|
||||
revoke: revoke,
|
||||
show_grants: showGrants,
|
||||
drop_user: dropUser,
|
||||
drop_db: dropDb,
|
||||
};
|
||||
if (this.isPostgres) {
|
||||
// ── PostgreSQL ──────────────────────────────────────────────
|
||||
const createDb = `CREATE DATABASE "${db}"\n WITH ENCODING 'UTF8'\n LC_COLLATE = 'en_US.UTF-8'\n LC_CTYPE = 'en_US.UTF-8'\n TEMPLATE = template0;`;
|
||||
const createUser = `DO $$\nBEGIN\n IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '${user}') THEN\n CREATE USER "${user}" WITH PASSWORD '${pass}';\n END IF;\nEND\n$$;`;
|
||||
const grant = `GRANT ALL PRIVILEGES ON DATABASE "${db}" TO "${user}";\nGRANT ALL ON SCHEMA public TO "${user}";\nALTER DEFAULT PRIVILEGES IN SCHEMA public\n GRANT ALL ON TABLES TO "${user}";\nALTER DEFAULT PRIVILEGES IN SCHEMA public\n GRANT ALL ON SEQUENCES TO "${user}";`;
|
||||
const revoke = `REVOKE ALL PRIVILEGES ON DATABASE "${db}" FROM "${user}";\nREVOKE ALL ON SCHEMA public FROM "${user}";`;
|
||||
const showGrants = `SELECT grantee, privilege_type, table_name\nFROM information_schema.role_table_grants\nWHERE grantee = '${user}'\nORDER BY table_name;`;
|
||||
const dropUser = `DROP USER IF EXISTS "${user}";`;
|
||||
const dropDb = `DROP DATABASE IF EXISTS "${db}";`;
|
||||
map = {
|
||||
full_setup: `-- 1. Crear base de datos\n${createDb}\n\n-- 2. Crear usuario (si no existe)\n${createUser}\n\n-- 3. Dar permisos totales\n${grant}`,
|
||||
create_db: createDb,
|
||||
create_user: createUser,
|
||||
grant: grant,
|
||||
revoke: revoke,
|
||||
show_grants: showGrants,
|
||||
drop_user: dropUser,
|
||||
drop_db: dropDb,
|
||||
};
|
||||
} else {
|
||||
// ── MySQL / MariaDB ─────────────────────────────────────────
|
||||
const createDb = `CREATE DATABASE IF NOT EXISTS \`${db}\`\n CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`;
|
||||
const createUser = `CREATE USER IF NOT EXISTS '${user}'@'${host}' IDENTIFIED BY '${pass}';`;
|
||||
const grant = `GRANT ALL PRIVILEGES ON \`${db}\`.* TO '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
||||
const revoke = `REVOKE ALL PRIVILEGES ON \`${db}\`.* FROM '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
||||
const showGrants = `SHOW GRANTS FOR '${user}'@'${host}';`;
|
||||
const dropUser = `DROP USER IF EXISTS '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
||||
const dropDb = `DROP DATABASE IF EXISTS \`${db}\`;`;
|
||||
map = {
|
||||
full_setup: `-- 1. Crear base de datos\n${createDb}\n\n-- 2. Crear usuario\n${createUser}\n\n-- 3. Dar permisos totales\n${grant}`,
|
||||
create_db: createDb,
|
||||
create_user: createUser,
|
||||
grant: grant,
|
||||
revoke: revoke,
|
||||
show_grants: showGrants,
|
||||
drop_user: dropUser,
|
||||
drop_db: dropDb,
|
||||
};
|
||||
}
|
||||
|
||||
this.sqlText = map[this.dbHelper.op] || '';
|
||||
document.getElementById('sql-editor')?.focus();
|
||||
|
||||
Reference in New Issue
Block a user