@gzl10/osx-cli 4.0.5 → 4.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,7 +51,11 @@ osx extract TABLA --tns PROD_DB
51
51
  # Multiples tablas
52
52
  osx extract TABLA1,TABLA2,TABLA3 --tns PROD_DB
53
53
 
54
- # Guardar en archivo (nombre auto: TABLA.json)
54
+ # Wildcards: * (multiples caracteres), ? (un caracter)
55
+ osx extract "D21*" --tns PROD_DB -o --pretty
56
+ osx extract "A200?100" --tns PROD_DB -o
57
+
58
+ # Guardar en archivo (nombre auto: TABLA.json o extractYYYYMMDD.json)
55
59
  osx extract TABLA --tns PROD_DB -o --pretty
56
60
 
57
61
  # Guardar con nombre especifico
@@ -67,6 +71,23 @@ osx extract TABLA --tns "servidor:1521/servicio" -u USER -p PASS
67
71
  osx extract TABLA --tns PROD_DB --schema OWNER_SCHEMA
68
72
  ```
69
73
 
74
+ ### Wildcards
75
+
76
+ Los wildcards permiten extraer multiples tablas con un patron:
77
+
78
+ - `*` - coincide con cualquier cantidad de caracteres
79
+ - `?` - coincide con un solo caracter
80
+
81
+ Cuando se usan wildcards, se aplica automaticamente un delay de 100ms entre tablas para no saturar la base de datos. Puedes ajustarlo con `-d`.
82
+
83
+ ```bash
84
+ # Todas las tablas que empiezan por D21
85
+ osx extract "D21*" --tns PROD_DB -o --pretty
86
+
87
+ # Con delay personalizado (500ms)
88
+ osx extract "D21*" --tns PROD_DB -o --pretty -d 500
89
+ ```
90
+
70
91
  ## Opciones
71
92
 
72
93
  | Opcion | Descripcion |
@@ -88,6 +88,16 @@ var TABLE_COMMENT = `
88
88
  WHERE owner = :schema AND table_name = :tableName
89
89
  `;
90
90
  var TABLE_COLUMNS = `
91
+ WITH PK_COLUMNS AS (
92
+ SELECT acc.COLUMN_NAME
93
+ FROM ALL_CONSTRAINTS ac
94
+ JOIN ALL_CONS_COLUMNS acc
95
+ ON ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME
96
+ AND ac.OWNER = acc.OWNER
97
+ WHERE ac.CONSTRAINT_TYPE = 'P'
98
+ AND ac.OWNER = :schema
99
+ AND ac.TABLE_NAME = :tableName
100
+ )
91
101
  SELECT
92
102
  COLS.COLUMN_NAME as "column_name",
93
103
  DESCRIP.COMMENTS as "comment",
@@ -96,28 +106,19 @@ var TABLE_COLUMNS = `
96
106
  COLS.DATA_PRECISION as "data_precision",
97
107
  COLS.NULLABLE as "nullable",
98
108
  CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN 'Y' ELSE 'N' END AS "is_pk"
99
- FROM SYS.ALL_TAB_COLUMNS COLS
100
- JOIN SYS.ALL_COL_COMMENTS DESCRIP
109
+ FROM ALL_TAB_COLUMNS COLS
110
+ JOIN ALL_COL_COMMENTS DESCRIP
101
111
  ON DESCRIP.OWNER = COLS.OWNER
102
112
  AND DESCRIP.TABLE_NAME = COLS.TABLE_NAME
103
113
  AND DESCRIP.COLUMN_NAME = COLS.COLUMN_NAME
104
- LEFT JOIN (
105
- SELECT acc.COLUMN_NAME
106
- FROM ALL_CONSTRAINTS ac
107
- JOIN ALL_CONS_COLUMNS acc
108
- ON ac.OWNER = acc.OWNER
109
- AND ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME
110
- WHERE ac.CONSTRAINT_TYPE = 'P'
111
- AND ac.OWNER = :schema
112
- AND acc.TABLE_NAME = :tableName
113
- ) PK ON PK.COLUMN_NAME = COLS.COLUMN_NAME
114
+ LEFT JOIN PK_COLUMNS PK ON PK.COLUMN_NAME = COLS.COLUMN_NAME
114
115
  WHERE COLS.OWNER = :schema AND COLS.TABLE_NAME = :tableName
115
116
  ORDER BY COLS.COLUMN_ID
116
117
  `;
117
118
  var TABLE_CHILDREN = `
118
119
  SELECT DISTINCT ac_child.table_name AS "child_table"
119
- FROM all_constraints ac_parent
120
- JOIN all_constraints ac_child
120
+ FROM ALL_CONSTRAINTS ac_parent
121
+ JOIN ALL_CONSTRAINTS ac_child
121
122
  ON ac_parent.constraint_name = ac_child.r_constraint_name
122
123
  AND ac_parent.owner = ac_child.r_owner
123
124
  WHERE ac_parent.owner = :schema
@@ -131,15 +132,43 @@ var TABLE_STATS = `
131
132
  FROM ALL_TABLES
132
133
  WHERE OWNER = :schema AND TABLE_NAME = :tableName
133
134
  `;
135
+ var ORACLE_IDENTIFIER_REGEX = /^[A-Z][A-Z0-9_#$]{0,127}$/i;
136
+ function validateOracleIdentifier(name, type) {
137
+ if (!ORACLE_IDENTIFIER_REGEX.test(name)) {
138
+ throw new Error(`Nombre de ${type} inv\xE1lido: ${name}`);
139
+ }
140
+ }
134
141
  function buildExamplesQuery(schema, tableName, limit = 10) {
135
- return `SELECT * FROM ${schema}.${tableName} ORDER BY ROWID DESC FETCH FIRST ${limit} ROWS ONLY`;
142
+ validateOracleIdentifier(schema, "schema");
143
+ validateOracleIdentifier(tableName, "tabla");
144
+ const safeLimit = Math.min(Math.max(1, limit), 100);
145
+ return `SELECT * FROM ${schema}.${tableName} FETCH FIRST ${safeLimit} ROWS ONLY`;
136
146
  }
147
+ var LIST_TABLES = `
148
+ SELECT TABLE_NAME as "table_name"
149
+ FROM ALL_TABLES
150
+ WHERE OWNER = :schema
151
+ AND TABLE_NAME LIKE :pattern
152
+ ORDER BY TABLE_NAME
153
+ `;
137
154
 
138
155
  // src/services/SchemaExtractor.ts
139
156
  var SchemaExtractor = class {
140
157
  constructor(oracle) {
141
158
  this.oracle = oracle;
142
159
  }
160
+ /**
161
+ * Lista tablas que coinciden con un patrón
162
+ * Convierte wildcards: * → %, ? → _
163
+ */
164
+ async listTables(schema, pattern) {
165
+ const sqlPattern = pattern.replace(/\*/g, "%").replace(/\?/g, "_");
166
+ const rows = await this.oracle.query(LIST_TABLES, {
167
+ schema,
168
+ pattern: sqlPattern
169
+ });
170
+ return rows.map((r) => r.table_name);
171
+ }
143
172
  /**
144
173
  * Extrae el esquema completo de una tabla
145
174
  */
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  OracleService,
4
4
  SchemaExtractor
5
- } from "./chunk-R65IT4N7.js";
5
+ } from "./chunk-XHMPD7KT.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { readFileSync as readFileSync2 } from "fs";
@@ -120,7 +120,6 @@ async function extractCommand(tables, options) {
120
120
  const homeEnv = loadHomeEnv();
121
121
  const user = options.user || process.env.OSX_USER || homeEnv.user;
122
122
  const password = options.password || process.env.OSX_PASSWORD || homeEnv.password;
123
- const schema = options.schema || user?.toUpperCase();
124
123
  if (!options.tns) {
125
124
  console.error(
126
125
  '\n\u274C Error: Debes especificar el TNS o conexi\xF3n\n\nUso:\n osx extract <TABLA> --tns <ALIAS> # Usa tnsnames.ora\n osx extract <TABLA> --tns host:port/sid # Easy Connect\n\nEjemplos:\n osx extract D0001 --tns MDI1\n osx extract D0001 --tns "BDMDI1:1523/MDI1"\n'
@@ -133,21 +132,51 @@ async function extractCommand(tables, options) {
133
132
  );
134
133
  process.exit(1);
135
134
  }
135
+ const schema = options.schema || user.toUpperCase();
136
136
  setupTnsAdmin();
137
137
  const oracle = new OracleService();
138
138
  try {
139
139
  console.error(`Conectando a ${options.tns}...`);
140
140
  await oracle.connect({ tns: options.tns, user, password });
141
- const tableNames = tables.split(",").map((t) => t.trim().toUpperCase()).filter(Boolean);
142
- if (tableNames.length === 0) {
141
+ const tablePatterns = tables.split(",").map((t) => t.trim().toUpperCase()).filter(Boolean);
142
+ if (tablePatterns.length === 0) {
143
143
  console.error(
144
- "\n\u274C Error: No se especificaron tablas a extraer\n\nUso: osx extract <TABLA> [--tns ALIAS]\n\nEjemplos:\n osx extract D0001 --tns TRON_LD\n osx extract D0001,D0002,D0003 --tns TRON_LD\n"
144
+ '\n\u274C Error: No se especificaron tablas a extraer\n\nUso: osx extract <TABLA> [--tns ALIAS]\n\nEjemplos:\n osx extract D0001 --tns TRON_LD\n osx extract D0001,D0002,D0003 --tns TRON_LD\n osx extract "D21*" --tns TRON_LD # Wildcards\n'
145
145
  );
146
146
  process.exit(1);
147
147
  }
148
- console.error(`Extrayendo ${tableNames.length} tabla(s) de ${schema}...`);
149
148
  const extractor = new SchemaExtractor(oracle);
150
- const delayMs = options.delay ? parseInt(options.delay, 10) : 0;
149
+ const hasWildcards = tablePatterns.some((p) => p.includes("*") || p.includes("?"));
150
+ let tableNames = [];
151
+ if (hasWildcards) {
152
+ console.error(`Buscando tablas en ${schema}...`);
153
+ for (const pattern of tablePatterns) {
154
+ if (pattern.includes("*") || pattern.includes("?")) {
155
+ const matched = await extractor.listTables(schema, pattern);
156
+ if (matched.length === 0) {
157
+ console.error(` \u26A0\uFE0F No se encontraron tablas para: ${pattern}`);
158
+ } else {
159
+ console.error(` \u2713 ${pattern} \u2192 ${matched.length} tabla(s)`);
160
+ tableNames.push(...matched);
161
+ }
162
+ } else {
163
+ tableNames.push(pattern);
164
+ }
165
+ }
166
+ tableNames = [...new Set(tableNames)].sort();
167
+ if (tableNames.length === 0) {
168
+ console.error("\n\u274C Error: No se encontraron tablas que coincidan con los patrones\n");
169
+ process.exit(1);
170
+ }
171
+ console.error(`
172
+ Tablas a extraer (${tableNames.length}):`);
173
+ tableNames.forEach((t) => console.error(` - ${t}`));
174
+ console.error("");
175
+ } else {
176
+ tableNames = tablePatterns;
177
+ }
178
+ console.error(`Extrayendo ${tableNames.length} tabla(s) de ${schema}...`);
179
+ const delayMs = options.delay ? parseInt(options.delay, 10) : hasWildcards ? 100 : 0;
151
180
  const results = await extractor.extractTables(options.tns, schema, tableNames, delayMs);
152
181
  const output = results.length === 1 ? results[0] : results;
153
182
  const json = options.pretty ? JSON.stringify(output, null, 2) : JSON.stringify(output);
package/dist/index.d.ts CHANGED
@@ -70,6 +70,11 @@ declare class OracleService {
70
70
  declare class SchemaExtractor {
71
71
  private oracle;
72
72
  constructor(oracle: OracleService);
73
+ /**
74
+ * Lista tablas que coinciden con un patrón
75
+ * Convierte wildcards: * → %, ? → _
76
+ */
77
+ listTables(schema: string, pattern: string): Promise<string[]>;
73
78
  /**
74
79
  * Extrae el esquema completo de una tabla
75
80
  */
@@ -87,11 +92,12 @@ declare const TABLE_COMMENT = "\n SELECT comments\n FROM ALL_TAB_COMMENTS\n W
87
92
  /**
88
93
  * Query para obtener las columnas de una tabla con sus metadatos
89
94
  * Incluye: nombre, comentario, tipo de dato, nullable, si es PK
95
+ * Usa CTE para evaluar la subquery de PK una sola vez
90
96
  */
91
- declare const TABLE_COLUMNS = "\n SELECT\n COLS.COLUMN_NAME as \"column_name\",\n DESCRIP.COMMENTS as \"comment\",\n COLS.DATA_TYPE as \"data_type\",\n COLS.DATA_LENGTH as \"data_length\",\n COLS.DATA_PRECISION as \"data_precision\",\n COLS.NULLABLE as \"nullable\",\n CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN 'Y' ELSE 'N' END AS \"is_pk\"\n FROM SYS.ALL_TAB_COLUMNS COLS\n JOIN SYS.ALL_COL_COMMENTS DESCRIP\n ON DESCRIP.OWNER = COLS.OWNER\n AND DESCRIP.TABLE_NAME = COLS.TABLE_NAME\n AND DESCRIP.COLUMN_NAME = COLS.COLUMN_NAME\n LEFT JOIN (\n SELECT acc.COLUMN_NAME\n FROM ALL_CONSTRAINTS ac\n JOIN ALL_CONS_COLUMNS acc\n ON ac.OWNER = acc.OWNER\n AND ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME\n WHERE ac.CONSTRAINT_TYPE = 'P'\n AND ac.OWNER = :schema\n AND acc.TABLE_NAME = :tableName\n ) PK ON PK.COLUMN_NAME = COLS.COLUMN_NAME\n WHERE COLS.OWNER = :schema AND COLS.TABLE_NAME = :tableName\n ORDER BY COLS.COLUMN_ID\n";
97
+ declare const TABLE_COLUMNS = "\n WITH PK_COLUMNS AS (\n SELECT acc.COLUMN_NAME\n FROM ALL_CONSTRAINTS ac\n JOIN ALL_CONS_COLUMNS acc\n ON ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME\n AND ac.OWNER = acc.OWNER\n WHERE ac.CONSTRAINT_TYPE = 'P'\n AND ac.OWNER = :schema\n AND ac.TABLE_NAME = :tableName\n )\n SELECT\n COLS.COLUMN_NAME as \"column_name\",\n DESCRIP.COMMENTS as \"comment\",\n COLS.DATA_TYPE as \"data_type\",\n COLS.DATA_LENGTH as \"data_length\",\n COLS.DATA_PRECISION as \"data_precision\",\n COLS.NULLABLE as \"nullable\",\n CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN 'Y' ELSE 'N' END AS \"is_pk\"\n FROM ALL_TAB_COLUMNS COLS\n JOIN ALL_COL_COMMENTS DESCRIP\n ON DESCRIP.OWNER = COLS.OWNER\n AND DESCRIP.TABLE_NAME = COLS.TABLE_NAME\n AND DESCRIP.COLUMN_NAME = COLS.COLUMN_NAME\n LEFT JOIN PK_COLUMNS PK ON PK.COLUMN_NAME = COLS.COLUMN_NAME\n WHERE COLS.OWNER = :schema AND COLS.TABLE_NAME = :tableName\n ORDER BY COLS.COLUMN_ID\n";
92
98
  /**
93
99
  * Query para obtener las tablas hijas (que tienen FK hacia esta tabla)
94
100
  */
95
- declare const TABLE_CHILDREN = "\n SELECT DISTINCT ac_child.table_name AS \"child_table\"\n FROM all_constraints ac_parent\n JOIN all_constraints ac_child\n ON ac_parent.constraint_name = ac_child.r_constraint_name\n AND ac_parent.owner = ac_child.r_owner\n WHERE ac_parent.owner = :schema\n AND ac_parent.table_name = :tableName\n AND ac_parent.constraint_type = 'P'\n AND ac_child.constraint_type = 'R'\n AND ac_child.status = 'ENABLED'\n";
101
+ declare const TABLE_CHILDREN = "\n SELECT DISTINCT ac_child.table_name AS \"child_table\"\n FROM ALL_CONSTRAINTS ac_parent\n JOIN ALL_CONSTRAINTS ac_child\n ON ac_parent.constraint_name = ac_child.r_constraint_name\n AND ac_parent.owner = ac_child.r_owner\n WHERE ac_parent.owner = :schema\n AND ac_parent.table_name = :tableName\n AND ac_parent.constraint_type = 'P'\n AND ac_child.constraint_type = 'R'\n AND ac_child.status = 'ENABLED'\n";
96
102
 
97
103
  export { type ExtractOptions, type OracleColumn, type OracleConfig, OracleService, SchemaExtractor, TABLE_CHILDREN, TABLE_COLUMNS, TABLE_COMMENT, type TableSchema };
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  TABLE_CHILDREN,
5
5
  TABLE_COLUMNS,
6
6
  TABLE_COMMENT
7
- } from "./chunk-R65IT4N7.js";
7
+ } from "./chunk-XHMPD7KT.js";
8
8
  export {
9
9
  OracleService,
10
10
  SchemaExtractor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gzl10/osx-cli",
3
- "version": "4.0.5",
3
+ "version": "4.0.6",
4
4
  "description": "Oracle Schema Extractor CLI - Genera JSON compatible con Atlas OriginType=BBDD",
5
5
  "type": "module",
6
6
  "bin": {