target find

job-minus.js #!/usr/bin/env node 'use strict'; const fs = require('fs'); const path = require('path'); function printHelp() { console.log(`Usage: node json-minus.js Subtracts b.json from a.json by case-insensitive tableName + plainColumn. Rows from a.json are preserved as-is in out.json when their key does not exist in b.json. Example: node json-minus.js a.json b.json out.json `); } function parseArgs(argv) { if (argv.includes('--help') || argv.includes('-h')) { printHelp(); process.exit(0); } if (argv.length !== 3) { printHelp(); throw new Error('Exactly three arguments are required: .'); } return { aPath: path.resolve(argv[0]), bPath: path.resolve(argv[1]), outPath: path.resolve(argv[2]) }; } function readJsonArray(filePath, label) { if (!fs.existsSync(filePath)) { throw new Error(`${label} not found: ${filePath}`); } const value = JSON.parse(fs.readFileSync(filePath, 'utf8')); if (!Array.isArray(value)) { throw new Error(`${label} must be a JSON array: ${filePath}`); } return value; } function normalizeKey(row) { return `${String(row && row.tableName ? row.tableName : '').toUpperCase()}|${String(row && row.plainColumn ? row.plainColumn : '').toUpperCase()}`; } function hasValidKey(row) { return Boolean(row && row.tableName && row.plainColumn); } function main() { const args = parseArgs(process.argv.slice(2)); const aRows = readJsonArray(args.aPath, 'a.json'); const bRows = readJsonArray(args.bPath, 'b.json'); const bKeys = new Set(bRows.filter(hasValidKey).map(normalizeKey)); const outRows = aRows.filter((row) => !hasValidKey(row) || !bKeys.has(normalizeKey(row))); fs.writeFileSync(args.outPath, `${JSON.stringify(outRows, null, 2)}\n`, 'utf8'); console.log(`A rows: ${aRows.length}`); console.log(`B keys: ${bKeys.size}`); console.log(`Output rows: ${outRows.length}`); console.log(`Removed rows: ${aRows.length - outRows.length}`); console.log(`Output written: ${args.outPath}`); } main(); job-minus.js -- end print_allcol.js start #!/usr/bin/env node 'use strict'; const fs = require('fs'); const path = require('path'); const oracledb = require('oracledb'); function parseArgs(argv) { const args = { config: path.resolve(__dirname, '..', 'db.ini'), output: '', owners: process.env.SCAN_OWNERS || '', includeSystemOwners: process.env.SCAN_INCLUDE_SYSTEM_OWNERS === 'true' }; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]; const readValue = () => { if (i + 1 >= argv.length) { throw new Error(`${arg} option requires a value.`); } i += 1; return argv[i]; }; if (arg === '--config') args.config = path.resolve(readValue()); else if (arg === '--output') args.output = path.resolve(readValue()); else if (arg === '--owners') args.owners = readValue(); else if (arg === '--include-system-owners') args.includeSystemOwners = true; else if (arg === '--help' || arg === '-h') { printHelp(); process.exit(0); } else { throw new Error(`Unknown option: ${arg}`); } } return args; } function printHelp() { console.log(`Usage: node print_allcol.js [options] Prints every accessible table column in crypto-targets.json object format. Options: --config DB ini path. Default: ../db.ini --output write JSON to file instead of stdout --owners owner list. Default: all accessible non-system schemas --include-system-owners include system schemas `); } function readIni(filePath) { if (!fs.existsSync(filePath)) { return {}; } return fs.readFileSync(filePath, 'utf8') .split(/\r?\n/) .map((line) => line.trim()) .filter((line) => line && !line.startsWith('#') && !line.startsWith(';')) .reduce((acc, line) => { const index = line.indexOf('='); if (index > -1) { acc[line.slice(0, index).trim()] = line.slice(index + 1).trim(); } return acc; }, {}); } function buildDbConfig(args) { const ini = readIni(args.config); const user = process.env.DB_USER || process.env.ORACLE_USER || ini.id; const password = process.env.DB_PASSWORD || process.env.ORACLE_PASSWORD || ini.pass; const host = process.env.DB_HOST || ini.ip || '127.0.0.1'; const port = process.env.DB_PORT || ini.port || '1521'; const sid = process.env.DB_SID || ini.sid; const serviceName = process.env.DB_SERVICE_NAME || ini.serviceName || ini.service_name; const connectString = process.env.DB_CONNECT_STRING || ini.connectString || ini.connect_string || `${host}:${port}/${serviceName || sid}`; if (!user || !password || !connectString) { throw new Error('DB connection information is missing. Check db.ini or DB_* environment variables.'); } return { user, password, connectString }; } function splitCsv(value) { return String(value || '') .split(',') .map((item) => item.trim().toUpperCase()) .filter(Boolean); } function addInListPredicate(columnName, values, bindPrefix, binds) { const placeholders = values.map((value, index) => { const key = `${bindPrefix}${index}`; binds[key] = value; return `:${key}`; }); return `${columnName} IN (${placeholders.join(', ')})`; } async function getAllColumns(connection, args) { const owners = splitCsv(args.owners); const binds = {}; const predicates = [ 't.DROPPED = \'NO\'', 't.TEMPORARY = \'N\'', 't.NESTED = \'NO\'', 'c.TABLE_NAME NOT LIKE \'BIN$%\'' ]; if (owners.length > 0) { predicates.push(addInListPredicate('c.OWNER', owners, 'owner', binds)); } if (!args.includeSystemOwners) { predicates.push(`c.OWNER NOT IN ( 'SYS', 'SYSTEM', 'XDB', 'MDSYS', 'CTXSYS', 'ORDSYS', 'OLAPSYS', 'WMSYS', 'OUTLN', 'DBSNMP', 'APPQOSSYS', 'AUDSYS' )`); } const sql = ` SELECT c.TABLE_NAME, c.COLUMN_NAME FROM ALL_TAB_COLUMNS c JOIN ALL_TABLES t ON t.OWNER = c.OWNER AND t.TABLE_NAME = c.TABLE_NAME WHERE ${predicates.join('\n AND ')} ORDER BY c.OWNER, c.TABLE_NAME, c.COLUMN_ID `; const result = await connection.execute(sql, binds, { outFormat: oracledb.OUT_FORMAT_OBJECT }); return result.rows.map((row) => ({ tableName: row.TABLE_NAME, plainColumn: row.COLUMN_NAME, cryptoType: '', encColumn: '', hashColumn: '', javaProperty: '', encryptTypeHandler: '', hashTypeHandler: '' })); } async function main() { const args = parseArgs(process.argv.slice(2)); const dbConfig = buildDbConfig(args); oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT; const connection = await oracledb.getConnection(dbConfig); try { const columns = await getAllColumns(connection, args); const output = `${JSON.stringify(columns, null, 2)}\n`; if (args.output) { fs.writeFileSync(args.output, output, 'utf8'); console.error(`Output written: ${args.output}`); } else { process.stdout.write(output); } } finally { await connection.close(); } } main().catch((error) => { console.error(error.message); process.exitCode = 1; }); print_allcol.js end print-empty.js start #!/usr/bin/env node 'use strict'; const fs = require('fs'); const path = require('path'); const oracledb = require('oracledb'); function parseArgs(argv) { const args = { config: path.resolve(__dirname, '..', 'db.ini'), output: '', owners: process.env.SCAN_OWNERS || '', includeSystemOwners: process.env.SCAN_INCLUDE_SYSTEM_OWNERS === 'true' }; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]; const readValue = () => { if (i + 1 >= argv.length) { throw new Error(`${arg} option requires a value.`); } i += 1; return argv[i]; }; if (arg === '--config') args.config = path.resolve(readValue()); else if (arg === '--output') args.output = path.resolve(readValue()); else if (arg === '--owners') args.owners = readValue(); else if (arg === '--include-system-owners') args.includeSystemOwners = true; else if (arg === '--help' || arg === '-h') { printHelp(); process.exit(0); } else { throw new Error(`Unknown option: ${arg}`); } } return args; } function printHelp() { console.log(`Usage: node print-empty.js [options] Prints columns in crypto-targets.json object format when: 1. the table has zero rows, or 2. the table has rows but the column has no non-null values. Options: --config DB ini path. Default: ../db.ini --output write JSON to file instead of stdout --owners owner list. Default: all accessible non-system schemas --include-system-owners include system schemas `); } function readIni(filePath) { if (!fs.existsSync(filePath)) { return {}; } return fs.readFileSync(filePath, 'utf8') .split(/\r?\n/) .map((line) => line.trim()) .filter((line) => line && !line.startsWith('#') && !line.startsWith(';')) .reduce((acc, line) => { const index = line.indexOf('='); if (index > -1) { acc[line.slice(0, index).trim()] = line.slice(index + 1).trim(); } return acc; }, {}); } function buildDbConfig(args) { const ini = readIni(args.config); const user = process.env.DB_USER || process.env.ORACLE_USER || ini.id; const password = process.env.DB_PASSWORD || process.env.ORACLE_PASSWORD || ini.pass; const host = process.env.DB_HOST || ini.ip || '127.0.0.1'; const port = process.env.DB_PORT || ini.port || '1521'; const sid = process.env.DB_SID || ini.sid; const serviceName = process.env.DB_SERVICE_NAME || ini.serviceName || ini.service_name; const connectString = process.env.DB_CONNECT_STRING || ini.connectString || ini.connect_string || `${host}:${port}/${serviceName || sid}`; if (!user || !password || !connectString) { throw new Error('DB connection information is missing. Check db.ini or DB_* environment variables.'); } return { user, password, connectString }; } function splitCsv(value) { return String(value || '') .split(',') .map((item) => item.trim().toUpperCase()) .filter(Boolean); } function addInListPredicate(columnName, values, bindPrefix, binds) { const placeholders = values.map((value, index) => { const key = `${bindPrefix}${index}`; binds[key] = value; return `:${key}`; }); return `${columnName} IN (${placeholders.join(', ')})`; } function quoteIdentifier(identifier) { return `"${String(identifier).replace(/"/g, '""')}"`; } function toCryptoTarget(row) { return { tableName: row.TABLE_NAME, plainColumn: row.COLUMN_NAME, cryptoType: '', encColumn: '', hashColumn: '', javaProperty: '', encryptTypeHandler: '', hashTypeHandler: '' }; } async function getTables(connection, args) { const owners = splitCsv(args.owners); const binds = {}; const predicates = [ 'DROPPED = \'NO\'', 'TEMPORARY = \'N\'', 'NESTED = \'NO\'', 'TABLE_NAME NOT LIKE \'BIN$%\'' ]; if (owners.length > 0) { predicates.push(addInListPredicate('OWNER', owners, 'owner', binds)); } if (!args.includeSystemOwners) { predicates.push(`OWNER NOT IN ( 'SYS', 'SYSTEM', 'XDB', 'MDSYS', 'CTXSYS', 'ORDSYS', 'OLAPSYS', 'WMSYS', 'OUTLN', 'DBSNMP', 'APPQOSSYS', 'AUDSYS' )`); } const sql = ` SELECT OWNER, TABLE_NAME FROM ALL_TABLES WHERE ${predicates.join('\n AND ')} ORDER BY OWNER, TABLE_NAME `; const result = await connection.execute(sql, binds, { outFormat: oracledb.OUT_FORMAT_OBJECT }); return result.rows; } async function getColumns(connection, owner, tableName) { const result = await connection.execute( ` SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE OWNER = :owner AND TABLE_NAME = :tableName ORDER BY COLUMN_ID `, { owner, tableName }, { outFormat: oracledb.OUT_FORMAT_OBJECT } ); return result.rows; } async function getCount(connection, sql) { const result = await connection.execute(sql, [], { outFormat: oracledb.OUT_FORMAT_OBJECT }); return result.rows[0].CNT; } async function getEmptyColumns(connection, args) { const tables = await getTables(connection, args); const emptyColumns = []; let scannedColumns = 0; for (const [tableIndex, table] of tables.entries()) { const tableRef = `${quoteIdentifier(table.OWNER)}.${quoteIdentifier(table.TABLE_NAME)}`; process.stderr.write(`[${tableIndex + 1}/${tables.length}] ${table.OWNER}.${table.TABLE_NAME}\r`); const tableRows = await getCount(connection, `SELECT COUNT(*) AS CNT FROM ${tableRef}`); const columns = await getColumns(connection, table.OWNER, table.TABLE_NAME); scannedColumns += columns.length; for (const column of columns) { if (tableRows === 0) { emptyColumns.push(toCryptoTarget({ TABLE_NAME: table.TABLE_NAME, COLUMN_NAME: column.COLUMN_NAME })); continue; } const columnRows = await getCount( connection, `SELECT COUNT(*) AS CNT FROM ${tableRef} WHERE ${quoteIdentifier(column.COLUMN_NAME)} IS NOT NULL` ); if (columnRows === 0) { emptyColumns.push(toCryptoTarget({ TABLE_NAME: table.TABLE_NAME, COLUMN_NAME: column.COLUMN_NAME })); } } } process.stderr.write(' '.repeat(120) + '\r'); console.error(`Tables scanned: ${tables.length}`); console.error(`Columns scanned: ${scannedColumns}`); console.error(`Empty columns: ${emptyColumns.length}`); return emptyColumns; } async function main() { const args = parseArgs(process.argv.slice(2)); const dbConfig = buildDbConfig(args); oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT; const connection = await oracledb.getConnection(dbConfig); try { const columns = await getEmptyColumns(connection, args); const output = `${JSON.stringify(columns, null, 2)}\n`; if (args.output) { fs.writeFileSync(args.output, output, 'utf8'); console.error(`Output written: ${args.output}`); } else { process.stdout.write(output); } } finally { await connection.close(); } } main().catch((error) => { console.error(error.message); process.exitCode = 1; }); print-empty.js end scan-card-col.js start #!/usr/bin/env node 'use strict'; const fs = require('fs'); const path = require('path'); const oracledb = require('oracledb'); const STRING_TYPES = new Set([ 'CHAR', 'VARCHAR2', 'NCHAR', 'NVARCHAR2', 'CLOB', 'NCLOB' ]); const CARD_COLUMN_REGEX = /^(?:card_?no|card_?num|card_?number|credit_?card|credit_?card_?no|credit_?card_?number|cc_?no|cc_?num)$/i; function parseArgs(argv) { const args = { config: path.resolve(__dirname, '..', 'db.ini'), targets: path.resolve(__dirname, 'crypto-targets.json'), output: path.resolve(__dirname, 'findcardoutput.txt'), owners: process.env.SCAN_OWNERS || '', sampleLimit: Number(process.env.SCAN_SAMPLE_LIMIT || 5), maxLength: Number(process.env.SCAN_MAX_LENGTH || 4000), includeSystemOwners: process.env.SCAN_INCLUDE_SYSTEM_OWNERS === 'true' }; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]; const readValue = () => { if (i + 1 >= argv.length) { throw new Error(`${arg} option requires a value.`); } i += 1; return argv[i]; }; if (arg === '--config') args.config = path.resolve(readValue()); else if (arg === '--targets') args.targets = path.resolve(readValue()); else if (arg === '--output') args.output = path.resolve(readValue()); else if (arg === '--owners') args.owners = readValue(); else if (arg === '--sample-limit') args.sampleLimit = Number(readValue()); else if (arg === '--max-length') args.maxLength = Number(readValue()); else if (arg === '--include-system-owners') args.includeSystemOwners = true; else if (arg === '--help' || arg === '-h') { printHelp(); process.exit(0); } else { throw new Error(`Unknown option: ${arg}`); } } if (!Number.isInteger(args.sampleLimit) || args.sampleLimit < 1 || args.sampleLimit > 100) { throw new Error('--sample-limit must be an integer from 1 to 100.'); } if (!Number.isInteger(args.maxLength) || args.maxLength < 1 || args.maxLength > 4000) { throw new Error('--max-length must be an integer from 1 to 4000.'); } return args; } function printHelp() { console.log(`Usage: node scan-card-col.js [options] Finds table columns that are not present in crypto-targets.json and look like credit card candidates by sample value or column name. Options: --config DB ini path. Default: ../db.ini --targets crypto-targets.json path. Default: ./crypto-targets.json --output output file path. Default: ./findcardoutput.txt --owners owner list. Default: all accessible non-system schemas --sample-limit sample count per column. Default: 5 --max-length max value length for scan. Default: 4000 --include-system-owners include system schemas `); } function readIni(filePath) { if (!fs.existsSync(filePath)) { return {}; } return fs.readFileSync(filePath, 'utf8') .split(/\r?\n/) .map((line) => line.trim()) .filter((line) => line && !line.startsWith('#') && !line.startsWith(';')) .reduce((acc, line) => { const index = line.indexOf('='); if (index > -1) { acc[line.slice(0, index).trim()] = line.slice(index + 1).trim(); } return acc; }, {}); } function buildDbConfig(args) { const ini = readIni(args.config); const user = process.env.DB_USER || process.env.ORACLE_USER || ini.id; const password = process.env.DB_PASSWORD || process.env.ORACLE_PASSWORD || ini.pass; const host = process.env.DB_HOST || ini.ip || '127.0.0.1'; const port = process.env.DB_PORT || ini.port || '1521'; const sid = process.env.DB_SID || ini.sid; const serviceName = process.env.DB_SERVICE_NAME || ini.serviceName || ini.service_name; const connectString = process.env.DB_CONNECT_STRING || ini.connectString || ini.connect_string || `${host}:${port}/${serviceName || sid}`; if (!user || !password || !connectString) { throw new Error('DB connection information is missing. Check db.ini or DB_* environment variables.'); } return { user, password, connectString }; } function splitCsv(value) { return String(value || '') .split(',') .map((item) => item.trim().toUpperCase()) .filter(Boolean); } function addInListPredicate(columnName, values, bindPrefix, binds) { const placeholders = values.map((value, index) => { const key = `${bindPrefix}${index}`; binds[key] = value; return `:${key}`; }); return `${columnName} IN (${placeholders.join(', ')})`; } function normalizeKey(tableName, columnName) { return `${String(tableName || '').toUpperCase()}.${String(columnName || '').toUpperCase()}`; } function loadExistingTargets(filePath) { if (!fs.existsSync(filePath)) { throw new Error(`crypto-targets.json not found: ${filePath}`); } const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8')); if (!Array.isArray(parsed)) { throw new Error('crypto-targets.json must be a JSON array.'); } const keys = new Set(); for (const item of parsed) { if (item && item.tableName && item.plainColumn) { keys.add(normalizeKey(item.tableName, item.plainColumn)); } } return keys; } function quoteIdentifier(identifier) { return `"${String(identifier).replace(/"/g, '""')}"`; } function isLobType(dataType) { return dataType === 'CLOB' || dataType === 'NCLOB'; } function sampleExpression(column, dataType, maxLength) { const quotedColumn = quoteIdentifier(column); if (isLobType(dataType)) { return `DBMS_LOB.SUBSTR(${quotedColumn}, ${maxLength}, 1)`; } return `SUBSTR(${quotedColumn}, 1, ${maxLength})`; } async function getCandidateColumns(connection, args, existingTargets) { const owners = splitCsv(args.owners); const binds = {}; const stringTypePredicate = addInListPredicate('c.DATA_TYPE', Array.from(STRING_TYPES), 'type', binds); const predicates = [ `(${stringTypePredicate} OR REGEXP_LIKE(c.COLUMN_NAME, '^(CARD_?NO|CARD_?NUM|CARD_?NUMBER|CREDIT_?CARD|CREDIT_?CARD_?NO|CREDIT_?CARD_?NUMBER|CC_?NO|CC_?NUM)$', 'i'))`, 't.DROPPED = \'NO\'', 't.TEMPORARY = \'N\'', 't.NESTED = \'NO\'' ]; if (owners.length > 0) { predicates.push(addInListPredicate('c.OWNER', owners, 'owner', binds)); } if (!args.includeSystemOwners) { predicates.push(`c.OWNER NOT IN ( 'SYS', 'SYSTEM', 'XDB', 'MDSYS', 'CTXSYS', 'ORDSYS', 'OLAPSYS', 'WMSYS', 'OUTLN', 'DBSNMP', 'APPQOSSYS', 'AUDSYS' )`); } const sql = ` SELECT c.OWNER, c.TABLE_NAME, c.COLUMN_NAME, c.DATA_TYPE FROM ALL_TAB_COLUMNS c JOIN ALL_TABLES t ON t.OWNER = c.OWNER AND t.TABLE_NAME = c.TABLE_NAME WHERE ${predicates.join('\n AND ')} ORDER BY c.OWNER, c.TABLE_NAME, c.COLUMN_ID `; const result = await connection.execute(sql, binds, { outFormat: oracledb.OUT_FORMAT_OBJECT }); return result.rows.filter((column) => !existingTargets.has(normalizeKey(column.TABLE_NAME, column.COLUMN_NAME))); } async function getSamples(connection, column, args) { if (!STRING_TYPES.has(column.DATA_TYPE)) { return []; } const sql = ` SELECT SAMPLE_VALUE FROM ( SELECT ${sampleExpression(column.COLUMN_NAME, column.DATA_TYPE, args.maxLength)} AS SAMPLE_VALUE FROM ${quoteIdentifier(column.OWNER)}.${quoteIdentifier(column.TABLE_NAME)} WHERE ${quoteIdentifier(column.COLUMN_NAME)} IS NOT NULL AND ROWNUM <= :sampleLimit ) WHERE SAMPLE_VALUE IS NOT NULL `; const result = await connection.execute( sql, { sampleLimit: args.sampleLimit }, { outFormat: oracledb.OUT_FORMAT_OBJECT } ); return result.rows.map((row) => String(row.SAMPLE_VALUE)); } function luhnCheck(digits) { let sum = 0; let shouldDouble = false; for (let i = digits.length - 1; i >= 0; i -= 1) { let digit = Number(digits[i]); if (shouldDouble) { digit *= 2; if (digit > 9) { digit -= 9; } } sum += digit; shouldDouble = !shouldDouble; } return sum % 10 === 0; } function findCardNumbers(value) { const candidates = String(value).match(/\b(?:\d[ -]?){13,19}\b/g) || []; return candidates .map((candidate) => candidate.replace(/[ -]/g, '')) .filter((digits) => digits.length >= 13 && digits.length <= 19 && luhnCheck(digits)); } function classifyColumn(column, samples) { const reasons = []; const matchedSamples = []; if (CARD_COLUMN_REGEX.test(column.COLUMN_NAME)) { reasons.push('COLUMN_NAME_CARD'); } for (const sample of samples) { const cards = findCardNumbers(sample); if (cards.length > 0) { reasons.push('CARD_VALUE'); matchedSamples.push({ type: 'CARD_VALUE', value: sample, normalizedCards: cards }); } } return { reasons: Array.from(new Set(reasons)), matchedSamples }; } function maskLineBreaks(value) { return String(value).replace(/\r/g, '\\r').replace(/\n/g, '\\n'); } function maskCardNumber(digits) { if (digits.length <= 8) { return '*'.repeat(digits.length); } return `${digits.slice(0, 6)}${'*'.repeat(digits.length - 10)}${digits.slice(-4)}`; } function buildOutput({ args, existingTargetCount, scannedColumns, matches, errors }) { const lines = []; lines.push('Credit Card Column Candidates'); lines.push(`GeneratedAt=${new Date().toISOString()}`); lines.push(`TargetsFile=${args.targets}`); lines.push(`ExistingTargets=${existingTargetCount}`); lines.push(`ScannedColumns=${scannedColumns}`); lines.push(`MatchedColumns=${matches.length}`); lines.push(`ErrorColumns=${errors.length}`); lines.push(''); for (const match of matches) { lines.push(`[MATCH] ${match.owner}.${match.table}.${match.column} (${match.dataType})`); lines.push(`Reasons=${match.reasons.join(', ')}`); if (match.matchedSamples.length > 0) { lines.push('Samples='); for (const sample of match.matchedSamples) { const cards = sample.normalizedCards.map(maskCardNumber).join(', '); lines.push(` - ${sample.type}: ${maskLineBreaks(sample.value)} | cards=${cards}`); } } lines.push(''); } if (errors.length > 0) { lines.push('[ERRORS]'); for (const error of errors) { lines.push(`${error.owner}.${error.table}.${error.column}: ${error.error}`); } lines.push(''); } return lines.join('\n'); } async function main() { const args = parseArgs(process.argv.slice(2)); const existingTargets = loadExistingTargets(args.targets); const dbConfig = buildDbConfig(args); oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT; const connection = await oracledb.getConnection(dbConfig); try { const columns = await getCandidateColumns(connection, args, existingTargets); const matches = []; const errors = []; for (const [index, column] of columns.entries()) { const label = `${column.OWNER}.${column.TABLE_NAME}.${column.COLUMN_NAME}`; process.stderr.write(`[${index + 1}/${columns.length}] ${label}\r`); try { const samples = await getSamples(connection, column, args); const classified = classifyColumn(column, samples); if (classified.reasons.length > 0) { matches.push({ owner: column.OWNER, table: column.TABLE_NAME, column: column.COLUMN_NAME, dataType: column.DATA_TYPE, reasons: classified.reasons, matchedSamples: classified.matchedSamples }); } } catch (error) { errors.push({ owner: column.OWNER, table: column.TABLE_NAME, column: column.COLUMN_NAME, dataType: column.DATA_TYPE, error: error.message }); } } process.stderr.write(' '.repeat(120) + '\r'); const output = buildOutput({ args, existingTargetCount: existingTargets.size, scannedColumns: columns.length, matches, errors }); fs.writeFileSync(args.output, output, 'utf8'); console.log(`Output written: ${args.output}`); console.log(`Scanned columns: ${columns.length}`); console.log(`Matched columns: ${matches.length}`); console.log(`Error columns: ${errors.length}`); } finally { await connection.close(); } } main().catch((error) => { console.error(error.message); process.exitCode = 1; }); scan-card-col.js end

댓글

이 블로그의 인기 게시물

food eff privacy

판다 스픽 , 개인정보 처리방침 , Panda Speak Privacy Term

판다 수학 개인정보 처리방침 , Privacy , Panda Math