@gzl10/osx-cli 4.0.5 → 4.0.7

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 |
@@ -49,6 +49,9 @@ var OracleService = class {
49
49
  * Conecta a Oracle usando TNS
50
50
  */
51
51
  async connect(config) {
52
+ if (!process.env.NLS_LANG) {
53
+ process.env.NLS_LANG = "SPANISH_SPAIN.AL32UTF8";
54
+ }
52
55
  initThickMode();
53
56
  this.connection = await oracledb.getConnection({
54
57
  user: config.user,
@@ -88,6 +91,16 @@ var TABLE_COMMENT = `
88
91
  WHERE owner = :schema AND table_name = :tableName
89
92
  `;
90
93
  var TABLE_COLUMNS = `
94
+ WITH PK_COLUMNS AS (
95
+ SELECT acc.COLUMN_NAME
96
+ FROM ALL_CONSTRAINTS ac
97
+ JOIN ALL_CONS_COLUMNS acc
98
+ ON ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME
99
+ AND ac.OWNER = acc.OWNER
100
+ WHERE ac.CONSTRAINT_TYPE = 'P'
101
+ AND ac.OWNER = :schema
102
+ AND ac.TABLE_NAME = :tableName
103
+ )
91
104
  SELECT
92
105
  COLS.COLUMN_NAME as "column_name",
93
106
  DESCRIP.COMMENTS as "comment",
@@ -96,28 +109,19 @@ var TABLE_COLUMNS = `
96
109
  COLS.DATA_PRECISION as "data_precision",
97
110
  COLS.NULLABLE as "nullable",
98
111
  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
112
+ FROM ALL_TAB_COLUMNS COLS
113
+ JOIN ALL_COL_COMMENTS DESCRIP
101
114
  ON DESCRIP.OWNER = COLS.OWNER
102
115
  AND DESCRIP.TABLE_NAME = COLS.TABLE_NAME
103
116
  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
117
+ LEFT JOIN PK_COLUMNS PK ON PK.COLUMN_NAME = COLS.COLUMN_NAME
114
118
  WHERE COLS.OWNER = :schema AND COLS.TABLE_NAME = :tableName
115
119
  ORDER BY COLS.COLUMN_ID
116
120
  `;
117
121
  var TABLE_CHILDREN = `
118
122
  SELECT DISTINCT ac_child.table_name AS "child_table"
119
- FROM all_constraints ac_parent
120
- JOIN all_constraints ac_child
123
+ FROM ALL_CONSTRAINTS ac_parent
124
+ JOIN ALL_CONSTRAINTS ac_child
121
125
  ON ac_parent.constraint_name = ac_child.r_constraint_name
122
126
  AND ac_parent.owner = ac_child.r_owner
123
127
  WHERE ac_parent.owner = :schema
@@ -131,15 +135,43 @@ var TABLE_STATS = `
131
135
  FROM ALL_TABLES
132
136
  WHERE OWNER = :schema AND TABLE_NAME = :tableName
133
137
  `;
138
+ var ORACLE_IDENTIFIER_REGEX = /^[A-Z][A-Z0-9_#$]{0,127}$/i;
139
+ function validateOracleIdentifier(name, type) {
140
+ if (!ORACLE_IDENTIFIER_REGEX.test(name)) {
141
+ throw new Error(`Nombre de ${type} inv\xE1lido: ${name}`);
142
+ }
143
+ }
134
144
  function buildExamplesQuery(schema, tableName, limit = 10) {
135
- return `SELECT * FROM ${schema}.${tableName} ORDER BY ROWID DESC FETCH FIRST ${limit} ROWS ONLY`;
145
+ validateOracleIdentifier(schema, "schema");
146
+ validateOracleIdentifier(tableName, "tabla");
147
+ const safeLimit = Math.min(Math.max(1, limit), 100);
148
+ return `SELECT * FROM ${schema}.${tableName} FETCH FIRST ${safeLimit} ROWS ONLY`;
136
149
  }
150
+ var LIST_TABLES = `
151
+ SELECT TABLE_NAME as "table_name"
152
+ FROM ALL_TABLES
153
+ WHERE OWNER = :schema
154
+ AND TABLE_NAME LIKE :pattern
155
+ ORDER BY TABLE_NAME
156
+ `;
137
157
 
138
158
  // src/services/SchemaExtractor.ts
139
159
  var SchemaExtractor = class {
140
160
  constructor(oracle) {
141
161
  this.oracle = oracle;
142
162
  }
163
+ /**
164
+ * Lista tablas que coinciden con un patrón
165
+ * Convierte wildcards: * → %, ? → _
166
+ */
167
+ async listTables(schema, pattern) {
168
+ const sqlPattern = pattern.replace(/\*/g, "%").replace(/\?/g, "_");
169
+ const rows = await this.oracle.query(LIST_TABLES, {
170
+ schema,
171
+ pattern: sqlPattern
172
+ });
173
+ return rows.map((r) => r.table_name);
174
+ }
143
175
  /**
144
176
  * Extrae el esquema completo de una tabla
145
177
  */
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-Q7WNFJU6.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-Q7WNFJU6.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.7",
4
4
  "description": "Oracle Schema Extractor CLI - Genera JSON compatible con Atlas OriginType=BBDD",
5
5
  "type": "module",
6
6
  "bin": {