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"/>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="font-semibold">Generador DBA</span>
|
<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>
|
<span class="ml-auto text-[10px] text-gray-400">CREATE · GRANT · DROP · Setup</span>
|
||||||
</button>
|
</button>
|
||||||
<div x-show="showDbHelper" x-transition class="p-3 bg-white border-t">
|
<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]"
|
class="w-full border rounded px-2 py-1 font-mono focus:outline-none focus:ring-1 focus:ring-[#8eb02f]"
|
||||||
placeholder="usuario_app">
|
placeholder="usuario_app">
|
||||||
</div>
|
</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>
|
<label class="text-[10px] text-gray-500 font-semibold uppercase block mb-0.5">Host</label>
|
||||||
<input x-model="dbHelper.host"
|
<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]"
|
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,
|
testOk: true,
|
||||||
toast: { show: false, msg: '', type: 'ok' },
|
toast: { show: false, msg: '', type: 'ok' },
|
||||||
isMongo: false,
|
isMongo: false,
|
||||||
|
isPostgres: false,
|
||||||
selectedCollection: '',
|
selectedCollection: '',
|
||||||
collectionFields: [],
|
collectionFields: [],
|
||||||
loadingFields: false,
|
loadingFields: false,
|
||||||
@@ -450,16 +453,29 @@ document.addEventListener('alpine:init', () => {
|
|||||||
// SQL DBA helpers
|
// SQL DBA helpers
|
||||||
showDbHelper: false,
|
showDbHelper: false,
|
||||||
dbHelper: { db: '', user: '', pass: '', host: '%', op: 'full_setup' },
|
dbHelper: { db: '', user: '', pass: '', host: '%', op: 'full_setup' },
|
||||||
sqlHelpers: [
|
get sqlHelpers() {
|
||||||
{ label: 'SHOW DATABASES', cmd: 'SHOW DATABASES;' },
|
if (this.isPostgres) return [
|
||||||
{ label: 'SHOW TABLES', cmd: 'SHOW TABLES;' },
|
{ label: 'SHOW DATABASES', cmd: 'SELECT datname FROM pg_database ORDER BY datname;' },
|
||||||
{ label: 'SHOW USERS', cmd: "SELECT User, Host, plugin FROM mysql.user ORDER BY User;" },
|
{ label: 'SHOW TABLES', cmd: "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;" },
|
||||||
{ label: 'SHOW GRANTS (actual)',cmd: 'SHOW GRANTS FOR CURRENT_USER;' },
|
{ label: 'SHOW USERS', cmd: "SELECT usename, usesuper, usecreatedb, usecreaterole FROM pg_catalog.pg_user ORDER BY usename;" },
|
||||||
{ label: 'SHOW PROCESSLIST', cmd: 'SHOW FULL PROCESSLIST;' },
|
{ 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 CREATE TABLE', cmd: 'SHOW CREATE TABLE nombre_tabla;' },
|
{ 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: 'DESCRIBE nombre_tabla;' },
|
{ 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: 'ENGINE/CHARSET', cmd: "SELECT table_name, engine, table_collation FROM information_schema.tables WHERE table_schema = DATABASE();" },
|
{ 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: [
|
mongoExamples: [
|
||||||
{ label: 'find()', cmd: 'db.coleccion.find({})' },
|
{ label: 'find()', cmd: 'db.coleccion.find({})' },
|
||||||
{ label: 'findOne()', cmd: 'db.coleccion.findOne({})' },
|
{ label: 'findOne()', cmd: 'db.coleccion.findOne({})' },
|
||||||
@@ -502,7 +518,9 @@ document.addEventListener('alpine:init', () => {
|
|||||||
this.collectionFields = [];
|
this.collectionFields = [];
|
||||||
if (!this.selectedConxId) return;
|
if (!this.selectedConxId) return;
|
||||||
const conx = this.conexiones.find(c => c.ID == this.selectedConxId);
|
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 {
|
try {
|
||||||
const res = await axios.get('/app/query-runner/databases?conx_db_id=' + this.selectedConxId);
|
const res = await axios.get('/app/query-runner/databases?conx_db_id=' + this.selectedConxId);
|
||||||
this.databases = res.data.data || [];
|
this.databases = res.data.data || [];
|
||||||
@@ -699,25 +717,47 @@ document.addEventListener('alpine:init', () => {
|
|||||||
const user = this.dbHelper.user || 'usuario_app';
|
const user = this.dbHelper.user || 'usuario_app';
|
||||||
const host = this.dbHelper.host || '%';
|
const host = this.dbHelper.host || '%';
|
||||||
const pass = this.dbHelper.pass || 'Contraseña123!';
|
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;`;
|
if (this.isPostgres) {
|
||||||
const createUser = `CREATE USER IF NOT EXISTS '${user}'@'${host}' IDENTIFIED BY '${pass}';`;
|
// ── PostgreSQL ──────────────────────────────────────────────
|
||||||
const grant = `GRANT ALL PRIVILEGES ON \`${db}\`.* TO '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
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 revoke = `REVOKE ALL PRIVILEGES ON \`${db}\`.* FROM '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
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 showGrants = `SHOW GRANTS FOR '${user}'@'${host}';`;
|
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 dropUser = `DROP USER IF EXISTS '${user}'@'${host}';\nFLUSH PRIVILEGES;`;
|
const revoke = `REVOKE ALL PRIVILEGES ON DATABASE "${db}" FROM "${user}";\nREVOKE ALL ON SCHEMA public FROM "${user}";`;
|
||||||
const dropDb = `DROP DATABASE IF EXISTS \`${db}\`;`;
|
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 map = {
|
const dropDb = `DROP DATABASE IF EXISTS "${db}";`;
|
||||||
full_setup: `-- 1. Crear base de datos\n${createDb}\n\n-- 2. Crear usuario\n${createUser}\n\n-- 3. Dar permisos totales\n${grant}`,
|
map = {
|
||||||
create_db: createDb,
|
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_user: createUser,
|
create_db: createDb,
|
||||||
grant: grant,
|
create_user: createUser,
|
||||||
revoke: revoke,
|
grant: grant,
|
||||||
show_grants: showGrants,
|
revoke: revoke,
|
||||||
drop_user: dropUser,
|
show_grants: showGrants,
|
||||||
drop_db: dropDb,
|
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] || '';
|
this.sqlText = map[this.dbHelper.op] || '';
|
||||||
document.getElementById('sql-editor')?.focus();
|
document.getElementById('sql-editor')?.focus();
|
||||||
|
|||||||
Reference in New Issue
Block a user