This module generates SQL DDL statements for schema modifications in database tables, with special support for complex nested data structures.
Project description
Spark SQL Generator
A Python library for generating Spark SQL DDL statements from structured input data. This package helps automate the creation and modification of Spark SQL tables, supporting operations such as adding, removing, moving, reordering, and replacing columns, including support for nested structures and arrays.
Features
- Generate
ALTER TABLEstatements for Spark SQL - Support for complex nested data structures (structs and arrays)
- Path-based field addressing for easy column manipulation
- Handling of schema modifications while preserving field order
- Comprehensive documentation for field types, comments, and paths
- Easily extensible for custom data types and operations
Installation
You can install the package directly from PyPI:
pip install spark-sql-generator
Or install from source:
# Clone the repository
git clone https://github.com/dinesh-kumar-g-31/spark-sql-generator.git
cd spark-sql-generator
# Install locally
pip install .
Usage
Basic Example
Here's a simple example showing how to add columns to a table:
from spark_sql_generator import SQLColumnGenerator
# Define your schema modifications
input_data = [
{
"operation": "ADD",
"columns": [
{"path": "id", "value": "int", "doc": "Primary key"},
{"path": "name", "value": "string", "doc": "User name"},
{"path": "address", "value": "object", "doc": "User address"},
{"path": "address.street", "value": "string", "doc": "Street name"},
{"path": "address.zip", "value": "int", "doc": "ZIP code"},
],
}
]
# Generate the SQL statements
generator = SQLColumnGenerator(input_data)
sql_statements = generator.generate_sql()
# Print or execute the statements
for stmt in sql_statements:
print(stmt)
Comprehensive Example
This example demonstrates all supported operations:
from spark_sql_generator import SQLColumnGenerator
# Define schema modifications with all supported operations
input_data = [
# ADD: Adding various column types including nested structures
{
"operation": "ADD",
"columns": [
{"path": "person.name", "value": "string", "doc": "Person's name"},
{"path": "person.age", "value": "integer", "doc": "Person's age"},
{"path": "person.height", "value": "number", "doc": "Height in meters"},
{"path": "person.active", "value": "boolean", "doc": "Is active"},
# Nested object
{"path": "person.address", "value": "object", "doc": "Person's address"},
{"path": "person.address.street", "value": "string", "doc": "Street name"},
{"path": "person.address.city", "value": "string", "doc": "City name"},
# Array of primitives
{"path": "person.tags", "value": "array", "arr_type": "string", "doc": "Tags"},
# Array of objects
{
"path": "person.contacts",
"value": "array",
"doc": "Contact list",
"nestedFields": {
"name": "type",
"type": "string",
"doc": "Contact type"
}
},
],
},
# REMOVE: Dropping columns
{"operation": "REMOVE", "columns": ["person.obsolete_field"]},
# REPLACE: Changing column properties
{
"operation": "REPLACE",
"columns": [
{"path": "person.email", "value": "string", "target_field": "type"}
],
},
# MOVE: Renaming columns
{
"operation": "MOVE",
"columns": [{"path": "person.old_name", "value": "full_name"}],
},
# REORDER: Changing column position
{
"operation": "REORDER",
"columns": [{"path": "person.id", "moveafter": "first"}],
},
# Another ADD operation (separate because operation type changed)
{
"operation": "ADD",
"columns": [
{"path": "person.created_at", "value": "timestamp", "doc": "Creation timestamp"}
],
},
]
# Generate the SQL statements
generator = SQLColumnGenerator(input_data)
sql = generator.generate_sql()
print("Generated SQL:", sql)
Multi-Level Array Example
Here's an example of working with multi-level arrays and complex struct types:
from spark_sql_generator import SQLColumnGenerator
# Example of multi-level arrays with structs
input_data = [
{
"operation": "ADD",
"columns": [
{
"path": "diagnosticReport",
"value": "object",
"doc": "Diagnostic analysis report"
},
{
"path": "diagnosticReport.testResults",
"value": "array",
"doc": "Array of test results",
"nestedFields": {
"testId": {
"type": "string",
"doc": "Unique test identifier"
},
"timestamp": {
"type": "timestamp",
"doc": "Time when test was executed"
},
"status": {
"type": "string",
"doc": "Test status (pass/fail/warning)"
},
"metrics": {
"type": "array",
"doc": "Test metrics",
"nestedFields": {
"name": {
"type": "string",
"doc": "Metric name"
},
"value": {
"type": "double",
"doc": "Metric value"
},
"threshold": {
"type": "double",
"doc": "Acceptance threshold"
},
"details": {
"type": "array",
"doc": "Detailed breakdown",
"nestedFields": {
"component": {
"type": "string",
"doc": "Component name"
},
"measurement": {
"type": "double",
"doc": "Component measurement"
}
}
}
}
}
}
}
]
}
]
generator = SQLColumnGenerator(input_data)
sql = generator.generate_sql()
print(sql)
This generates SQL for a structure with array of structs containing arrays of structs containing arrays of structs:
ALTER TABLE {table_name}
ADD COLUMNS (
`diagnosticReport` struct<> COMMENT 'Diagnostic analysis report',
`diagnosticReport`.`testResults` array<struct<
testId: string COMMENT 'Unique test identifier',
timestamp: timestamp COMMENT 'Time when test was executed',
status: string COMMENT 'Test status (pass/fail/warning)',
metrics: array<struct<
name: string COMMENT 'Metric name',
value: double COMMENT 'Metric value',
threshold: double COMMENT 'Acceptance threshold',
details: array<struct<
component: string COMMENT 'Component name',
measurement: double COMMENT 'Component measurement'
>> COMMENT 'Detailed breakdown'
>> COMMENT 'Test metrics'
>> COMMENT 'Array of test results'
)
Supported Operations
ADD
Adds new columns to a table. Supports:
- Simple columns:
{"path": "column_name", "value": "data_type", "doc": "comment"} - Nested columns: Use dot notation in path, e.g.,
"path": "parent.child" - Object columns:
{"path": "obj_name", "value": "object", "doc": "comment"} - Array columns:
- Simple array:
{"path": "array_name", "value": "array", "arr_type": "element_type", "doc": "comment"} - Array of objects: Use the
nestedFieldsproperty to define object structure
- Simple array:
REMOVE
Drops existing columns from a table:
{"operation": "REMOVE", "columns": ["column_path1", "column_path2"]}
MOVE
Renames columns while preserving their structure:
{"operation": "MOVE", "columns": [{"path": "old_path", "value": "new_name"}]}
REORDER
Changes the position of columns:
{"operation": "REORDER", "columns": [{"path": "column_path", "moveafter": "position"}]}
Position can be:
"first": Move to the beginning- Any valid column path: Move after the specified column
REPLACE
Modifies column properties such as type or comment:
{"operation": "REPLACE", "columns": [{"path": "column_path", "value": "new_value", "target_field": "field_to_change"}]}
Target fields can be:
"type": Change the data type"comment": Update the column comment
Supported Data Types
The library supports the following data types:
string: Text datainteger/int: Integer valuesbigint: Large integersnumber/float/double: Floating-point valuesboolean: Boolean valuestimestamp: Date/time valuesobject: Nested structuresarray: Collections of elements
Advanced Usage
Complex Nested Structures
The library can handle deeply nested structures, including arrays of structs and multi-level nesting. Here's an example of defining a complex nested structure:
from spark_sql_generator import SQLColumnGenerator
# Example of complex nested structure with arrays of structs
input_data = [
{
"operation": "ADD",
"columns": [
{
"path": "analytics",
"value": "object",
"doc": "Analytics data container"
},
{
"path": "analytics.reportData",
"value": "object",
"doc": "Report data structure"
},
{
"path": "analytics.reportData.validations",
"value": "array",
"doc": "Validation results array",
"nestedFields": {
"id": {
"type": "string",
"doc": "Validation identifier"
},
"status": {
"type": "string",
"doc": "Validation status (pass/fail)"
},
"details": {
"type": "object",
"doc": "Validation details",
"nestedFields": {
"code": {
"type": "string",
"doc": "Error code if failed"
},
"count": {
"type": "bigint",
"doc": "Count of validated items"
},
"metadata": {
"type": "array",
"arrayType": "string",
"doc": "Additional metadata"
}
}
}
}
}
]
}
]
generator = SQLColumnGenerator(input_data)
sql = generator.generate_sql()
print(sql)
This would generate SQL for a structure like:
ALTER TABLE {table_name}
ADD COLUMNS (
`analytics` struct<> COMMENT 'Analytics data container',
`analytics`.`reportData` struct<> COMMENT 'Report data structure',
`analytics`.`reportData`.`validations` array<struct<
id: string COMMENT 'Validation identifier',
status: string COMMENT 'Validation status (pass/fail)',
details: struct<
code: string COMMENT 'Error code if failed',
count: bigint COMMENT 'Count of validated items',
metadata: array<string> COMMENT 'Additional metadata'
> COMMENT 'Validation details'
>> COMMENT 'Validation results array'
)
Rules for Complex Structures
When working with complex nested structures:
- Define parent structures first: Always define object containers before their nested fields
- Use consistent paths: Ensure path hierarchy is consistent (e.g.,
parent.child.grandchild) - Nested arrays of structs: Use the
nestedFieldsproperty to define the structure of array elements - Multi-level nesting: You can nest objects within objects and arrays within structs to any depth
- Path escaping: For field names containing special characters, proper escaping is automatically applied
Output Example
The generated SQL statements will look like this:
-- Adding columns
ALTER TABLE {table_name}
ADD COLUMNS (
`person`.`name` string COMMENT 'Person\'s name',
`person`.`age` bigint COMMENT 'Person\'s age',
`person`.`height` double COMMENT 'Height in meters',
`person`.`active` boolean COMMENT 'Is active',
`person`.`address` struct<> COMMENT 'Person\'s address',
`person`.`address`.`street` string COMMENT 'Street name',
`person`.`address`.`city` string COMMENT 'City name',
`person`.`tags` array<string> COMMENT 'Tags',
`person`.`contacts` array<struct<type:string>> COMMENT 'Contact list'
)
-- Removing a column
ALTER TABLE {table_name} DROP COLUMN `person`.`obsolete_field`
-- Replacing column properties
ALTER TABLE {table_name} ALTER COLUMN `person`.`email` TYPE string
-- Moving/renaming columns
ALTER TABLE {table_name} RENAME COLUMN `person`.`old_name` TO `full_name`
-- Reordering columns
ALTER TABLE {table_name} ALTER COLUMN `person`.`id` FIRST
-- Adding more columns
ALTER TABLE {table_name}
ADD COLUMNS (
`person`.`created_at` timestamp COMMENT 'Creation timestamp'
)
Best Practices
- Group related operations: Group related column changes under a single operation type when possible
- Handle nested structures properly: Use dot notation for nested columns and make sure to define parent objects
- Use meaningful column comments: Add detailed descriptions to improve schema documentation
- Review SQL before execution: Always review generated SQL before applying to production databases
- Consider operation order: Some operations may depend on others, so order matters
- Define complex structures incrementally: For deeply nested structures, define the hierarchy layer by layer
- Validate generated SQL for complex structures: Always test complex nested structures in a development environment
- Use proper data types for nested structures: Choose the appropriate data types for nested fields
- Understand memory implications: Complex nested structures can have significant memory requirements
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Troubleshooting
Common Issues with Complex Structures
-
Missing Parent Objects
Problem: Error when adding nested fields without defining parent objects.
Solution: Always define parent objects before their nested fields:
# Correct approach {"path": "parent", "value": "object", "doc": "Parent object"} {"path": "parent.child", "value": "string", "doc": "Child field"}
-
Inconsistent Path Hierarchy
Problem: Unexpected SQL output due to inconsistent path naming.
Solution: Maintain consistent path hierarchies throughout your definition:
# Correct approach - consistent paths {"path": "report.metrics.value", "value": "double", "doc": "Metric value"} {"path": "report.metrics.name", "value": "string", "doc": "Metric name"} # Incorrect approach - inconsistent paths {"path": "report.metrics.value", "value": "double", "doc": "Metric value"} {"path": "report_metrics_name", "value": "string", "doc": "Metric name"}
-
Complex Array Structures
Problem: Incorrect definition of arrays containing complex structs.
Solution: Use the
nestedFieldsproperty with proper nesting:{ "path": "data.metrics", "value": "array", "doc": "Metrics array", "nestedFields": { "name": {"type": "string", "doc": "Metric name"}, "details": { "type": "object", "doc": "Details object", "nestedFields": { "value": {"type": "double", "doc": "Metric value"} } } } }
-
SQL Size Limitations
Problem: Generated SQL exceeds database size limitations for very complex structures.
Solution: Break down complex structures into smaller, manageable pieces:
# Instead of one large operation: input_data = [ {"operation": "ADD", "columns": [/* many complex columns */]}, # Split into multiple operations {"operation": "ADD", "columns": [/* first group */]}, {"operation": "ADD", "columns": [/* second group */]}, ]
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spark_sql_generator-0.0.2.tar.gz.
File metadata
- Download URL: spark_sql_generator-0.0.2.tar.gz
- Upload date:
- Size: 6.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e646865b63572979ed10f9e95f9d1003118b57f2446f184bbad3930e80492f55
|
|
| MD5 |
7518b97271c902e9ae4c2dcdbd7c996d
|
|
| BLAKE2b-256 |
63f473527488c606a629d1f49daf1738a812eb94beae896aff474b05db6dc95f
|
File details
Details for the file spark_sql_generator-0.0.2-py3-none-any.whl.
File metadata
- Download URL: spark_sql_generator-0.0.2-py3-none-any.whl
- Upload date:
- Size: 7.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2473ec780c877102aed734a5c26c625d67af8ca99ca7248537a4dd897c8d986f
|
|
| MD5 |
9cfd0818ea901a29974b7babdac359a5
|
|
| BLAKE2b-256 |
c5dbf250b603d19a3a61ac18e42ce667307e359318b6413435d92530fd17cda3
|