@happyvertical/sql 0.79.0 → 0.80.1
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/AGENT.md +1 -1
- package/dist/backup/index.js +210 -188
- package/dist/backup/index.js.map +1 -1
- package/dist/chunks/alter-utils-K7tiuj5L.js +135 -0
- package/dist/chunks/alter-utils-K7tiuj5L.js.map +1 -0
- package/dist/chunks/duckdb-DykBBzd6.js +1056 -0
- package/dist/chunks/duckdb-DykBBzd6.js.map +1 -0
- package/dist/chunks/duckdb-schema-utils-B-XBP8QF.js +115 -0
- package/dist/chunks/duckdb-schema-utils-B-XBP8QF.js.map +1 -0
- package/dist/chunks/postgres-cli-DzZcXSIG.js +272 -0
- package/dist/chunks/postgres-cli-DzZcXSIG.js.map +1 -0
- package/dist/chunks/schema-manager-a8RlxIE2.js +450 -0
- package/dist/chunks/schema-manager-a8RlxIE2.js.map +1 -0
- package/dist/chunks/sqlite-YgU9LCVy.js +1045 -0
- package/dist/chunks/sqlite-YgU9LCVy.js.map +1 -0
- package/dist/chunks/sqlite-native-BGtpx7Ro.js +902 -0
- package/dist/chunks/sqlite-native-BGtpx7Ro.js.map +1 -0
- package/dist/cli/claude-context.js +17 -17
- package/dist/cli/claude-context.js.map +1 -1
- package/dist/index.js +3244 -35
- package/dist/index.js.map +1 -1
- package/metadata.json +2 -2
- package/package.json +5 -5
- package/dist/chunks/duckdb-B6m78QdL.js +0 -924
- package/dist/chunks/duckdb-B6m78QdL.js.map +0 -1
- package/dist/chunks/index-CzdYvRFp.js +0 -3692
- package/dist/chunks/index-CzdYvRFp.js.map +0 -1
- package/dist/chunks/postgres-cli-Cr6bBZ3q.js +0 -209
- package/dist/chunks/postgres-cli-Cr6bBZ3q.js.map +0 -1
- package/dist/chunks/sqlite-CPCNeJIH.js +0 -937
- package/dist/chunks/sqlite-CPCNeJIH.js.map +0 -1
- package/dist/chunks/sqlite-native-DqVPKNUu.js +0 -1129
- package/dist/chunks/sqlite-native-DqVPKNUu.js.map +0 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//#region src/shared/alter-utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Generates an ALTER TABLE ADD COLUMN statement
|
|
4
|
+
*
|
|
5
|
+
* @param table - Table name
|
|
6
|
+
* @param column - Column definition with name
|
|
7
|
+
* @param dialect - Database dialect
|
|
8
|
+
* @returns SQL statement string
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const sql = generateAddColumnStatement('users', {
|
|
13
|
+
* name: 'email',
|
|
14
|
+
* type: 'TEXT',
|
|
15
|
+
* unique: true,
|
|
16
|
+
* notNull: false
|
|
17
|
+
* }, 'sqlite');
|
|
18
|
+
* // Returns: "ALTER TABLE users ADD COLUMN email TEXT UNIQUE"
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function generateAddColumnStatement(table, column, _dialect) {
|
|
22
|
+
const parts = [column.name, column.type];
|
|
23
|
+
if (column.primaryKey) parts.push("PRIMARY KEY");
|
|
24
|
+
if (column.notNull) parts.push("NOT NULL");
|
|
25
|
+
if (column.unique) parts.push("UNIQUE");
|
|
26
|
+
if (column.defaultValue !== void 0) {
|
|
27
|
+
const defaultClause = generateDefaultClause(column.type, column.defaultValue);
|
|
28
|
+
parts.push(`DEFAULT ${defaultClause}`);
|
|
29
|
+
}
|
|
30
|
+
if (column.check) parts.push(`CHECK (${column.check})`);
|
|
31
|
+
if (column.foreignKey) {
|
|
32
|
+
const fk = column.foreignKey;
|
|
33
|
+
const fkParts = [`REFERENCES ${fk.table}(${fk.column})`];
|
|
34
|
+
if (fk.onDelete) fkParts.push(`ON DELETE ${fk.onDelete}`);
|
|
35
|
+
if (fk.onUpdate) fkParts.push(`ON UPDATE ${fk.onUpdate}`);
|
|
36
|
+
parts.push(fkParts.join(" "));
|
|
37
|
+
}
|
|
38
|
+
return `ALTER TABLE ${table} ADD COLUMN ${parts.join(" ")}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Generates a DEFAULT clause for a column definition
|
|
42
|
+
*
|
|
43
|
+
* Handles proper formatting and escaping of default values based on column type.
|
|
44
|
+
*
|
|
45
|
+
* @param columnType - SQL column type (TEXT, INTEGER, BOOLEAN, etc.)
|
|
46
|
+
* @param defaultValue - Default value to format
|
|
47
|
+
* @returns Formatted default value string
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* generateDefaultClause('TEXT', 'hello'); // Returns: "'hello'"
|
|
52
|
+
* generateDefaultClause('INTEGER', 42); // Returns: "42"
|
|
53
|
+
* generateDefaultClause('BOOLEAN', true); // Returns: "true"
|
|
54
|
+
* generateDefaultClause('TEXT', null); // Returns: "NULL"
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
function generateDefaultClause(columnType, defaultValue) {
|
|
58
|
+
if (defaultValue === null) return "NULL";
|
|
59
|
+
const typeUpper = columnType.toUpperCase();
|
|
60
|
+
if (typeUpper.includes("TEXT") || typeUpper.includes("CHAR") || typeUpper.includes("VARCHAR") || typeUpper.includes("STRING")) {
|
|
61
|
+
if (defaultValue === "") return "''";
|
|
62
|
+
return `'${String(defaultValue).replace(/'/g, "''")}'`;
|
|
63
|
+
}
|
|
64
|
+
if (typeUpper.includes("BOOL")) return defaultValue ? "true" : "false";
|
|
65
|
+
if (typeUpper.includes("INT") || typeUpper.includes("REAL") || typeUpper.includes("FLOAT") || typeUpper.includes("DOUBLE") || typeUpper.includes("NUMERIC") || typeUpper.includes("DECIMAL")) return String(defaultValue);
|
|
66
|
+
if (typeUpper.includes("JSON")) {
|
|
67
|
+
if (typeof defaultValue === "string") return `'${defaultValue.replace(/'/g, "''")}'`;
|
|
68
|
+
return `'${JSON.stringify(defaultValue).replace(/'/g, "''")}'`;
|
|
69
|
+
}
|
|
70
|
+
if (typeUpper.includes("DATE") || typeUpper.includes("TIME") || typeUpper.includes("TIMESTAMP")) {
|
|
71
|
+
if (defaultValue instanceof Date) return `'${defaultValue.toISOString()}'`;
|
|
72
|
+
return `'${String(defaultValue).replace(/'/g, "''")}'`;
|
|
73
|
+
}
|
|
74
|
+
if (typeof defaultValue === "string") return `'${defaultValue.replace(/'/g, "''")}'`;
|
|
75
|
+
return String(defaultValue);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Generates a CREATE INDEX statement
|
|
79
|
+
*
|
|
80
|
+
* @param table - Table name
|
|
81
|
+
* @param index - Index definition
|
|
82
|
+
* @returns SQL CREATE INDEX statement
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const sql = generateCreateIndexStatement('users', {
|
|
87
|
+
* name: 'idx_users_email',
|
|
88
|
+
* columns: ['email'],
|
|
89
|
+
* unique: true
|
|
90
|
+
* });
|
|
91
|
+
* // Returns: "CREATE UNIQUE INDEX idx_users_email ON users (email)"
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
function generateCreateIndexStatement(table, index) {
|
|
95
|
+
const parts = ["CREATE"];
|
|
96
|
+
if (index.unique) parts.push("UNIQUE");
|
|
97
|
+
parts.push("INDEX");
|
|
98
|
+
parts.push(index.name);
|
|
99
|
+
parts.push(`ON ${table}`);
|
|
100
|
+
const columns = index.columns.join(", ");
|
|
101
|
+
parts.push(`(${columns})`);
|
|
102
|
+
if (index.where) parts.push(`WHERE ${index.where}`);
|
|
103
|
+
return parts.join(" ");
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validates a table name to prevent SQL injection
|
|
107
|
+
*
|
|
108
|
+
* @param tableName - Table name to validate
|
|
109
|
+
* @throws Error if table name contains invalid characters
|
|
110
|
+
*/
|
|
111
|
+
function validateTableName(tableName) {
|
|
112
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) throw new Error(`Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validates a column name to prevent SQL injection
|
|
116
|
+
*
|
|
117
|
+
* @param columnName - Column name to validate
|
|
118
|
+
* @throws Error if column name contains invalid characters
|
|
119
|
+
*/
|
|
120
|
+
function validateColumnName(columnName) {
|
|
121
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(columnName)) throw new Error(`Invalid column name: ${columnName}. Column names must start with a letter or underscore and contain only alphanumeric characters and underscores.`);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Validates an index name to prevent SQL injection
|
|
125
|
+
*
|
|
126
|
+
* @param indexName - Index name to validate
|
|
127
|
+
* @throws Error if index name contains invalid characters
|
|
128
|
+
*/
|
|
129
|
+
function validateIndexName(indexName) {
|
|
130
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(indexName)) throw new Error(`Invalid index name: ${indexName}. Index names must start with a letter or underscore and contain only alphanumeric characters and underscores.`);
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { validateTableName as a, validateIndexName as i, generateCreateIndexStatement as n, validateColumnName as r, generateAddColumnStatement as t };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=alter-utils-K7tiuj5L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alter-utils-K7tiuj5L.js","names":[],"sources":["../../src/shared/alter-utils.ts"],"sourcesContent":["/**\n * Shared utilities for ALTER TABLE operations\n *\n * Provides database-agnostic utilities for generating ALTER TABLE statements,\n * handling column definitions, and creating indexes.\n */\n\nimport type { ColumnDefinitionWithName, IndexDefinition } from './types.js';\n\n/**\n * Database dialect type for SQL generation\n */\nexport type DatabaseDialect = 'sqlite' | 'postgres' | 'duckdb';\n\n/**\n * Generates an ALTER TABLE ADD COLUMN statement\n *\n * @param table - Table name\n * @param column - Column definition with name\n * @param dialect - Database dialect\n * @returns SQL statement string\n *\n * @example\n * ```typescript\n * const sql = generateAddColumnStatement('users', {\n * name: 'email',\n * type: 'TEXT',\n * unique: true,\n * notNull: false\n * }, 'sqlite');\n * // Returns: \"ALTER TABLE users ADD COLUMN email TEXT UNIQUE\"\n * ```\n */\nexport function generateAddColumnStatement(\n table: string,\n column: ColumnDefinitionWithName,\n _dialect: DatabaseDialect,\n): string {\n const parts: string[] = [column.name, column.type];\n\n // Primary key\n if (column.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n\n // NOT NULL constraint\n if (column.notNull) {\n parts.push('NOT NULL');\n }\n\n // UNIQUE constraint\n if (column.unique) {\n parts.push('UNIQUE');\n }\n\n // DEFAULT clause\n if (column.defaultValue !== undefined) {\n const defaultClause = generateDefaultClause(\n column.type,\n column.defaultValue,\n );\n parts.push(`DEFAULT ${defaultClause}`);\n }\n\n // CHECK constraint\n if (column.check) {\n parts.push(`CHECK (${column.check})`);\n }\n\n // Foreign key (inline for some databases)\n if (column.foreignKey) {\n const fk = column.foreignKey;\n const fkParts = [`REFERENCES ${fk.table}(${fk.column})`];\n\n if (fk.onDelete) {\n fkParts.push(`ON DELETE ${fk.onDelete}`);\n }\n\n if (fk.onUpdate) {\n fkParts.push(`ON UPDATE ${fk.onUpdate}`);\n }\n\n parts.push(fkParts.join(' '));\n }\n\n const columnDef = parts.join(' ');\n return `ALTER TABLE ${table} ADD COLUMN ${columnDef}`;\n}\n\n/**\n * Generates a DEFAULT clause for a column definition\n *\n * Handles proper formatting and escaping of default values based on column type.\n *\n * @param columnType - SQL column type (TEXT, INTEGER, BOOLEAN, etc.)\n * @param defaultValue - Default value to format\n * @returns Formatted default value string\n *\n * @example\n * ```typescript\n * generateDefaultClause('TEXT', 'hello'); // Returns: \"'hello'\"\n * generateDefaultClause('INTEGER', 42); // Returns: \"42\"\n * generateDefaultClause('BOOLEAN', true); // Returns: \"true\"\n * generateDefaultClause('TEXT', null); // Returns: \"NULL\"\n * ```\n */\nexport function generateDefaultClause(\n columnType: string,\n defaultValue: any,\n): string {\n if (defaultValue === null) {\n return 'NULL';\n }\n\n const typeUpper = columnType.toUpperCase();\n\n // String types - need quotes\n if (\n typeUpper.includes('TEXT') ||\n typeUpper.includes('CHAR') ||\n typeUpper.includes('VARCHAR') ||\n typeUpper.includes('STRING')\n ) {\n // Handle empty strings\n if (defaultValue === '') {\n return \"''\";\n }\n // Escape single quotes\n const escaped = String(defaultValue).replace(/'/g, \"''\");\n return `'${escaped}'`;\n }\n\n // Boolean types\n if (typeUpper.includes('BOOL')) {\n return defaultValue ? 'true' : 'false';\n }\n\n // Numeric types - no quotes\n if (\n typeUpper.includes('INT') ||\n typeUpper.includes('REAL') ||\n typeUpper.includes('FLOAT') ||\n typeUpper.includes('DOUBLE') ||\n typeUpper.includes('NUMERIC') ||\n typeUpper.includes('DECIMAL')\n ) {\n return String(defaultValue);\n }\n\n // JSON types\n if (typeUpper.includes('JSON')) {\n if (typeof defaultValue === 'string') {\n return `'${defaultValue.replace(/'/g, \"''\")}'`;\n }\n return `'${JSON.stringify(defaultValue).replace(/'/g, \"''\")}'`;\n }\n\n // Date/timestamp types\n if (\n typeUpper.includes('DATE') ||\n typeUpper.includes('TIME') ||\n typeUpper.includes('TIMESTAMP')\n ) {\n if (defaultValue instanceof Date) {\n return `'${defaultValue.toISOString()}'`;\n }\n return `'${String(defaultValue).replace(/'/g, \"''\")}'`;\n }\n\n // Default: treat as string\n if (typeof defaultValue === 'string') {\n return `'${defaultValue.replace(/'/g, \"''\")}'`;\n }\n\n return String(defaultValue);\n}\n\n/**\n * Generates a CREATE INDEX statement\n *\n * @param table - Table name\n * @param index - Index definition\n * @returns SQL CREATE INDEX statement\n *\n * @example\n * ```typescript\n * const sql = generateCreateIndexStatement('users', {\n * name: 'idx_users_email',\n * columns: ['email'],\n * unique: true\n * });\n * // Returns: \"CREATE UNIQUE INDEX idx_users_email ON users (email)\"\n * ```\n */\nexport function generateCreateIndexStatement(\n table: string,\n index: IndexDefinition,\n): string {\n const parts: string[] = ['CREATE'];\n\n // UNIQUE modifier\n if (index.unique) {\n parts.push('UNIQUE');\n }\n\n parts.push('INDEX');\n\n // Index name\n parts.push(index.name);\n\n // ON table\n parts.push(`ON ${table}`);\n\n // Column list\n const columns = index.columns.join(', ');\n parts.push(`(${columns})`);\n\n // WHERE clause (partial index)\n if (index.where) {\n parts.push(`WHERE ${index.where}`);\n }\n\n return parts.join(' ');\n}\n\n/**\n * Validates a table name to prevent SQL injection\n *\n * @param tableName - Table name to validate\n * @throws Error if table name contains invalid characters\n */\nexport function validateTableName(tableName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {\n throw new Error(\n `Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`,\n );\n }\n}\n\n/**\n * Validates a column name to prevent SQL injection\n *\n * @param columnName - Column name to validate\n * @throws Error if column name contains invalid characters\n */\nexport function validateColumnName(columnName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(columnName)) {\n throw new Error(\n `Invalid column name: ${columnName}. Column names must start with a letter or underscore and contain only alphanumeric characters and underscores.`,\n );\n }\n}\n\n/**\n * Validates an index name to prevent SQL injection\n *\n * @param indexName - Index name to validate\n * @throws Error if index name contains invalid characters\n */\nexport function validateIndexName(indexName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(indexName)) {\n throw new Error(\n `Invalid index name: ${indexName}. Index names must start with a letter or underscore and contain only alphanumeric characters and underscores.`,\n );\n }\n}\n"],"mappings":"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"}
|