sandwich 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

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.
sandwich/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from importlib.metadata import version
2
+
3
+ SANDWICH_VERSION = version("sandwich")
@@ -0,0 +1,12 @@
1
+ """Dialects package for SQL code generation."""
2
+ from src.sandwich.dialects.base import DialectHandler
3
+ from src.sandwich.dialects.factory import DialectHandlerFactory
4
+ from src.sandwich.dialects.mssql import MssqlDialectHandler
5
+ from src.sandwich.dialects.postgres import PostgresDialectHandler
6
+
7
+ __all__ = [
8
+ "DialectHandler",
9
+ "DialectHandlerFactory",
10
+ "MssqlDialectHandler",
11
+ "PostgresDialectHandler",
12
+ ]
@@ -0,0 +1,166 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Tuple
3
+
4
+ from sqlalchemy import Table
5
+
6
+ class DialectHandler(ABC):
7
+ @abstractmethod
8
+ def get_boolean_type(self): ...
9
+
10
+ @abstractmethod
11
+ def get_proc_name_format(self, schema: str, operation: str, entity_name: str) -> str:
12
+ pass
13
+
14
+ @abstractmethod
15
+ def apply_proc_template(self, proc_name: str, sql_body: str, header: str) -> str:
16
+ """Wrap SQL body in procedure template with error handling and logging.
17
+
18
+ Args:
19
+ proc_name: Name of the procedure
20
+ sql_body: The main SQL logic to execute
21
+ header: Auto-generated header comment
22
+
23
+ Returns:
24
+ Complete procedure definition
25
+ """
26
+ pass
27
+
28
+ @abstractmethod
29
+ def make_stg_materialization_proc(
30
+ self,
31
+ entity_name: str,
32
+ header: str
33
+ ) -> Tuple[str, str, str]:
34
+ """Generate staging table materialization procedure.
35
+
36
+ Args:
37
+ entity_name: Entity name
38
+ columns_list: Comma-separated list of columns
39
+ header: Auto-generated header comment
40
+
41
+ Returns:
42
+ Tuple of (procedure_code, procedure_name)
43
+ """
44
+ pass
45
+
46
+ @abstractmethod
47
+ def make_hub_proc(
48
+ self,
49
+ hub_table: Table,
50
+ bk_keys: list,
51
+ header: str
52
+ ) -> Tuple[str, str, str]:
53
+ """Generate hub population procedure.
54
+
55
+ Args:
56
+ hub_table: SQLAlchemy Table object for hub
57
+ bk_keys: List of business key tuples (name, type)
58
+ columns_list: Comma-separated list of columns
59
+ header: Auto-generated header comment
60
+
61
+ Returns:
62
+ Tuple of (procedure_code, procedure_name)
63
+ """
64
+ pass
65
+
66
+ @abstractmethod
67
+ def make_sat_proc(
68
+ self,
69
+ sat_table: Table,
70
+ hk_name: str,
71
+ hashdiff_col: str,
72
+ is_available_col: str,
73
+ loaddate_col: str,
74
+ stg_schema: str,
75
+ header: str
76
+ ) -> Tuple[str, str, str]:
77
+ """Generate satellite population procedure.
78
+
79
+ Args:
80
+ sat_table: SQLAlchemy Table object for satellite
81
+ hk_name: Hash key column name
82
+ hashdiff_col: Hash diff column name
83
+ is_available_col: Is available column name
84
+ loaddate_col: Load date column name
85
+ columns_list: Comma-separated list of columns
86
+ stg_schema: Staging schema name ('stg' or 'proxy')
87
+ header: Auto-generated header comment
88
+
89
+ Returns:
90
+ Tuple of (procedure_code, procedure_name)
91
+ """
92
+ pass
93
+
94
+ @abstractmethod
95
+ def make_dim_scd2_proc(
96
+ self,
97
+ dim_table: Table,
98
+ bk_keys: list,
99
+ header: str
100
+ ) -> Tuple[str, str, str]:
101
+ """Generate dimension SCD2 recalculation procedure.
102
+
103
+ Args:
104
+ dim_table: SQLAlchemy Table object for dimension
105
+ bk_keys: List of business key tuples (name, type)
106
+ columns_list: Comma-separated list of columns
107
+ header: Auto-generated header comment
108
+
109
+ Returns:
110
+ Tuple of (procedure_code, procedure_name)
111
+ """
112
+ pass
113
+
114
+ @abstractmethod
115
+ def make_job_proc(
116
+ self,
117
+ entity_name: str,
118
+ hub_proc_name: str,
119
+ sat_proc_name: str,
120
+ dim_proc_name: str,
121
+ stg_proc_name: str | None,
122
+ header: str
123
+ ) -> Tuple[str, str, str]:
124
+ """Generate main job orchestration procedure.
125
+
126
+ Args:
127
+ entity_name: Entity name
128
+ hub_proc_name: Name of hub population procedure
129
+ sat_proc_name: Name of satellite population procedure
130
+ dim_proc_name: Name of dimension recalculation procedure
131
+ stg_proc_name: Name of staging materialization procedure (optional)
132
+ header: Auto-generated header comment
133
+
134
+ Returns:
135
+ Tuple of (procedure_code, procedure_name)
136
+ """
137
+ pass
138
+
139
+ @abstractmethod
140
+ def make_drop_proc(
141
+ self,
142
+ entity_name: str,
143
+ stg_schema: str,
144
+ job_proc_name: str,
145
+ stg_proc_name: str | None,
146
+ hub_proc_name: str,
147
+ sat_proc_name: str,
148
+ dim_proc_name: str,
149
+ header: str
150
+ ) -> Tuple[str, str, str]:
151
+ """Generate cleanup/drop procedure for all entity objects.
152
+
153
+ Args:
154
+ entity_name: Entity name
155
+ stg_schema: Staging schema name ('stg' or 'proxy')
156
+ job_proc_name: Name of job orchestration procedure
157
+ stg_proc_name: Name of staging materialization procedure (optional)
158
+ hub_proc_name: Name of hub population procedure
159
+ sat_proc_name: Name of satellite population procedure
160
+ dim_proc_name: Name of dimension recalculation procedure
161
+ header: Auto-generated header comment
162
+
163
+ Returns:
164
+ Tuple of (procedure_code, procedure_name)
165
+ """
166
+ pass
@@ -0,0 +1,148 @@
1
+ # language=sql
2
+ create_entities_table = """
3
+ if object_id('core.entities') is null
4
+ begin
5
+ create table core.entities (
6
+ [entity_id] bigint primary key identity,
7
+ [entity_name] varchar(100) not null,
8
+ [template] varchar(50) not null,
9
+ [created] datetime2(7) not null default sysdatetime(),
10
+ [updated] datetime2(7) not null default sysdatetime(),
11
+ [is_deleted] bit not null default 0,
12
+ [deleted] datetime2(7) default null,
13
+ unique ([entity_name])
14
+ );
15
+ end
16
+ """
17
+
18
+ # language=sql
19
+ create_proc_register_entity = """
20
+ create or alter proc core.register_entity (
21
+ @entity_name varchar(100),
22
+ @template varchar(50)
23
+ ) as
24
+ begin
25
+ set nocount on;
26
+
27
+ if exists (
28
+ select *
29
+ from core.[entities]
30
+ where [entity_name] = @entity_name
31
+ )
32
+ begin
33
+ update core.[entities]
34
+ set [updated] = sysdatetime(), [is_deleted] = 0
35
+ where [entity_name] = @entity_name
36
+ end
37
+ else begin
38
+ insert into core.[entities]
39
+ ([entity_name], [template])
40
+ values (@entity_name, @template)
41
+ end
42
+ end
43
+ """
44
+
45
+ # language=sql
46
+ create_table_ExecutionLog = """
47
+ if object_id('core.ExecutionLog') is null
48
+ begin
49
+ create table [core].[ExecutionLog](
50
+ [executionID] [bigint] identity(1,1) primary key NOT NULL,
51
+ [procid] [int] NOT NULL,
52
+ [begin_timestamp] [datetime2](7) NOT NULL default (getdate()),
53
+ [end_timestamp] [datetime2](7) NULL default (NULL),
54
+ [errorID] [int] NULL,
55
+ [procname] [varchar](200) NULL,
56
+ [parent_executionID] [bigint] NULL
57
+ )
58
+ end
59
+ """
60
+
61
+ # language=sql
62
+ create_table_ErrorLog = """
63
+ if object_id('core.ErrorLog') is null
64
+ begin
65
+ create table [core].[ErrorLog](
66
+ [ErrorID] [int] IDENTITY(1,1) NOT NULL,
67
+ [UserName] [varchar](100) NULL,
68
+ [ErrorNumber] [int] NULL,
69
+ [ErrorState] [int] NULL,
70
+ [ErrorSeverity] [int] NULL,
71
+ [ErrorLine] [int] NULL,
72
+ [ErrorProcedure] [varchar](max) NULL,
73
+ [ErrorMessage] [varchar](max) NULL,
74
+ [ErrorDateTime] [datetime] NULL
75
+ );
76
+ end
77
+ """
78
+
79
+ # language=sql
80
+ create_func_StringToHash = """
81
+ create or alter function [core].[StringToHash1]
82
+ (
83
+ @StrValue1 nvarchar(1000)
84
+ ) returns char(40) as
85
+ begin
86
+ declare @result char(40);
87
+ set @result = upper(convert(char(40), hashbytes('sha1',
88
+ upper(rtrim(ltrim(isnull(@StrValue1, ''))))
89
+ ), 2));
90
+ return @result;
91
+ end
92
+ """
93
+
94
+ # language=sql
95
+ create_schemas = """
96
+ if schema_id('core') is null
97
+ exec ('create schema core')
98
+ if schema_id('stg') is null
99
+ exec ('create schema stg')
100
+ if schema_id('hub') is null
101
+ exec ('create schema hub')
102
+ if schema_id('sat') is null
103
+ exec ('create schema sat')
104
+ if schema_id('dim') is null
105
+ exec ('create schema dim')
106
+ if schema_id('fact') is null
107
+ exec ('create schema fact')
108
+ if schema_id('elt') is null
109
+ exec ('create schema elt')
110
+ if schema_id('job') is null
111
+ exec ('create schema job')
112
+ if schema_id('meta') is null
113
+ exec ('create schema meta')
114
+ if schema_id('proxy') is null
115
+ exec ('create schema proxy')
116
+ """
117
+
118
+ # language=sql
119
+ create_proc_LogExecution = """
120
+ create or alter proc [core].[LogExecution]
121
+ (
122
+ @procid int,
123
+ @executionID_in bigint,
124
+ @executionID_out bigint out,
125
+ @parent_executionID bigint = null
126
+ ) as
127
+ begin
128
+ set nocount on;
129
+
130
+ if @executionID_in is not null
131
+ begin
132
+ update [core].[ExecutionLog]
133
+ set [end_timestamp] = getdate()
134
+ where executionID = @executionID_in;
135
+
136
+ set @executionID_out = @executionID_in;
137
+ end else
138
+ begin
139
+
140
+ declare @out table (executionID int);
141
+ insert into [core].[ExecutionLog] (procid, procname, parent_executionID) output inserted.executionID
142
+ into @out
143
+ values (@procid, object_name(@procid), @parent_executionID);
144
+
145
+ set @executionID_out = (select executionID from @out);
146
+ end
147
+ end
148
+ """
@@ -0,0 +1,132 @@
1
+ # language=sql
2
+ create_extensions = """
3
+ create extension if not exists pgcrypto;
4
+ """
5
+
6
+ # language=sql
7
+ create_entities_table = """
8
+ CREATE TABLE IF NOT EXISTS core.entities (
9
+ entity_id BIGSERIAL PRIMARY KEY,
10
+ entity_name VARCHAR(100) NOT NULL,
11
+ template VARCHAR(50) NOT NULL,
12
+ created TIMESTAMP NOT NULL DEFAULT NOW(),
13
+ updated TIMESTAMP NOT NULL DEFAULT NOW(),
14
+ is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
15
+ deleted TIMESTAMP DEFAULT NULL,
16
+ UNIQUE (entity_name)
17
+ );
18
+ """
19
+
20
+ # language=sql
21
+ create_proc_register_entity = """
22
+ CREATE OR REPLACE PROCEDURE core.register_entity(
23
+ p_entity_name VARCHAR(100),
24
+ p_gen_path VARCHAR(50)
25
+ )
26
+ LANGUAGE plpgsql
27
+ AS $$
28
+ BEGIN
29
+ INSERT INTO core.entities (entity_name, template)
30
+ VALUES (p_entity_name, p_gen_path)
31
+ ON CONFLICT (entity_name) DO UPDATE
32
+ SET updated = NOW(),
33
+ is_deleted = FALSE;
34
+ END;
35
+ $$;
36
+ """
37
+
38
+ # language=sql
39
+ create_func_StringToHash= """
40
+ create or replace function core.string_to_hash1(str_value text)
41
+ returns char(40)
42
+ language plpgsql
43
+ as $$
44
+ declare
45
+ result char(40);
46
+ begin
47
+ if str_value is null
48
+ or str_value in ('(unknown)', 'empty')
49
+ then
50
+ result := repeat('0', 40);
51
+ else
52
+ result :=
53
+ upper(
54
+ encode(
55
+ digest(upper(trim(str_value)), 'sha1'),
56
+ 'hex'
57
+ )
58
+ );
59
+ end if;
60
+
61
+ return cast(result as char(40));
62
+ end;
63
+ $$;
64
+ """
65
+
66
+ # language=sql
67
+ create_table_ExecutionLog = """
68
+ CREATE TABLE IF NOT EXISTS core.ExecutionLog (
69
+ executionID BIGSERIAL PRIMARY KEY,
70
+ procid INT NOT NULL,
71
+ begin_timestamp TIMESTAMP NOT NULL DEFAULT NOW(),
72
+ end_timestamp TIMESTAMP DEFAULT NULL,
73
+ errorID INT DEFAULT NULL,
74
+ procname VARCHAR(200) DEFAULT NULL,
75
+ parent_executionID BIGINT DEFAULT NULL
76
+ );
77
+ """
78
+
79
+ # language=sql
80
+ create_table_ErrorLog = """
81
+ CREATE TABLE IF NOT EXISTS core.ErrorLog (
82
+ ErrorID SERIAL PRIMARY KEY,
83
+ UserName VARCHAR(100) DEFAULT NULL,
84
+ ErrorNumber INT DEFAULT NULL,
85
+ ErrorState INT DEFAULT NULL,
86
+ ErrorSeverity INT DEFAULT NULL,
87
+ ErrorLine INT DEFAULT NULL,
88
+ ErrorProcedure TEXT DEFAULT NULL,
89
+ ErrorMessage TEXT DEFAULT NULL,
90
+ ErrorDateTime TIMESTAMP DEFAULT NULL
91
+ );
92
+ """
93
+
94
+ # language=sql
95
+ create_proc_LogExecution = """
96
+ CREATE OR REPLACE PROCEDURE core.LogExecution(
97
+ p_procid INT,
98
+ p_executionID_in BIGINT,
99
+ INOUT p_executionID_out BIGINT,
100
+ p_parent_executionID BIGINT DEFAULT NULL
101
+ )
102
+ LANGUAGE plpgsql
103
+ AS $$
104
+ BEGIN
105
+ IF p_executionID_in IS NOT NULL THEN
106
+ UPDATE core.ExecutionLog
107
+ SET end_timestamp = NOW()
108
+ WHERE executionID = p_executionID_in;
109
+
110
+ p_executionID_out := p_executionID_in;
111
+ ELSE
112
+ INSERT INTO core.ExecutionLog (procid, procname, parent_executionID)
113
+ VALUES (p_procid, NULL, p_parent_executionID)
114
+ RETURNING executionID INTO p_executionID_out;
115
+ END IF;
116
+ END;
117
+ $$;
118
+ """
119
+
120
+ # language=sql
121
+ create_schemas = f"""
122
+ CREATE SCHEMA IF NOT EXISTS core;
123
+ CREATE SCHEMA IF NOT EXISTS stg;
124
+ CREATE SCHEMA IF NOT EXISTS hub;
125
+ CREATE SCHEMA IF NOT EXISTS sat;
126
+ CREATE SCHEMA IF NOT EXISTS dim;
127
+ CREATE SCHEMA IF NOT EXISTS fact;
128
+ CREATE SCHEMA IF NOT EXISTS elt;
129
+ CREATE SCHEMA IF NOT EXISTS job;
130
+ CREATE SCHEMA IF NOT EXISTS meta;
131
+ CREATE SCHEMA IF NOT EXISTS proxy;
132
+ """
@@ -0,0 +1,27 @@
1
+ from src.sandwich.dialects.base import DialectHandler
2
+ from src.sandwich.dialects.mssql import MssqlDialectHandler
3
+ from src.sandwich.dialects.postgres import PostgresDialectHandler
4
+
5
+
6
+ class DialectHandlerFactory:
7
+ _handlers = {
8
+ "mssql": MssqlDialectHandler,
9
+ "postgres": PostgresDialectHandler,
10
+ }
11
+
12
+ @classmethod
13
+ def register_dialect(cls, dialect_name: str, handler_class):
14
+ cls._handlers[dialect_name] = handler_class
15
+
16
+ @classmethod
17
+ def create_handler(cls, dialect: str) -> DialectHandler:
18
+ if dialect not in cls._handlers:
19
+ available = ", ".join(cls._handlers.keys())
20
+ raise ValueError(f"Unknown dialect '{dialect}'. Available dialects: {available}")
21
+
22
+ handler_class = cls._handlers[dialect]
23
+ return handler_class()
24
+
25
+ @classmethod
26
+ def get_available_dialects(cls) -> list[str]:
27
+ return list(cls._handlers.keys())