velocity-python 0.0.129__py3-none-any.whl → 0.0.132__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.

Potentially problematic release.


This version of velocity-python might be problematic. Click here for more details.

Files changed (40) hide show
  1. velocity/__init__.py +1 -1
  2. velocity/aws/handlers/mixins/__init__.py +16 -0
  3. velocity/aws/handlers/mixins/activity_tracker.py +142 -0
  4. velocity/aws/handlers/mixins/error_handler.py +192 -0
  5. velocity/aws/handlers/mixins/legacy_mixin.py +53 -0
  6. velocity/aws/handlers/mixins/standard_mixin.py +73 -0
  7. velocity/db/servers/base/__init__.py +9 -0
  8. velocity/db/servers/base/initializer.py +69 -0
  9. velocity/db/servers/base/operators.py +98 -0
  10. velocity/db/servers/base/sql.py +503 -0
  11. velocity/db/servers/base/types.py +135 -0
  12. velocity/db/servers/mysql/__init__.py +64 -0
  13. velocity/db/servers/mysql/operators.py +54 -0
  14. velocity/db/servers/{mysql_reserved.py → mysql/reserved.py} +2 -14
  15. velocity/db/servers/mysql/sql.py +569 -0
  16. velocity/db/servers/mysql/types.py +107 -0
  17. velocity/db/servers/postgres/__init__.py +40 -0
  18. velocity/db/servers/postgres/operators.py +34 -0
  19. velocity/db/servers/postgres/sql.py +4 -3
  20. velocity/db/servers/postgres/types.py +88 -2
  21. velocity/db/servers/sqlite/__init__.py +52 -0
  22. velocity/db/servers/sqlite/operators.py +52 -0
  23. velocity/db/servers/sqlite/reserved.py +20 -0
  24. velocity/db/servers/sqlite/sql.py +530 -0
  25. velocity/db/servers/sqlite/types.py +92 -0
  26. velocity/db/servers/sqlserver/__init__.py +64 -0
  27. velocity/db/servers/sqlserver/operators.py +47 -0
  28. velocity/db/servers/sqlserver/reserved.py +32 -0
  29. velocity/db/servers/sqlserver/sql.py +625 -0
  30. velocity/db/servers/sqlserver/types.py +114 -0
  31. {velocity_python-0.0.129.dist-info → velocity_python-0.0.132.dist-info}/METADATA +1 -1
  32. {velocity_python-0.0.129.dist-info → velocity_python-0.0.132.dist-info}/RECORD +35 -16
  33. velocity/db/servers/mysql.py +0 -640
  34. velocity/db/servers/sqlite.py +0 -968
  35. velocity/db/servers/sqlite_reserved.py +0 -208
  36. velocity/db/servers/sqlserver.py +0 -921
  37. velocity/db/servers/sqlserver_reserved.py +0 -314
  38. {velocity_python-0.0.129.dist-info → velocity_python-0.0.132.dist-info}/WHEEL +0 -0
  39. {velocity_python-0.0.129.dist-info → velocity_python-0.0.132.dist-info}/licenses/LICENSE +0 -0
  40. {velocity_python-0.0.129.dist-info → velocity_python-0.0.132.dist-info}/top_level.txt +0 -0
velocity/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.129"
1
+ __version__ = version = "0.0.132"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -0,0 +1,16 @@
1
+ """
2
+ Mixins for AWS Lambda handlers.
3
+
4
+ This package provides reusable mixins for common Lambda handler functionality:
5
+ - ActivityTracker: Standardized activity logging and tracking
6
+ - ErrorHandler: Standardized error handling and notifications
7
+ - StandardMixin: Combined mixin for most common use cases
8
+ - LegacyMixin: Backward-compatible enhanced tracking for existing handlers
9
+ """
10
+
11
+ from .activity_tracker import ActivityTracker
12
+ from .error_handler import ErrorHandler
13
+ from .standard_mixin import StandardMixin
14
+ from .legacy_mixin import LegacyMixin
15
+
16
+ __all__ = ['ActivityTracker', 'ErrorHandler', 'StandardMixin', 'LegacyMixin']
@@ -0,0 +1,142 @@
1
+ """
2
+ Activity Tracker Mixin for Lambda Handlers.
3
+
4
+ Provides standardized activity tracking and logging functionality
5
+ for Lambda handlers using the aws_api_activity table.
6
+ """
7
+
8
+ import copy
9
+ import json
10
+ import os
11
+ import time
12
+ from abc import ABC, abstractmethod
13
+ from typing import Dict, Any, Optional
14
+
15
+
16
+ class ActivityTracker(ABC):
17
+ """
18
+ Mixin class providing standardized activity tracking for Lambda handlers.
19
+
20
+ Tracks API calls to the aws_api_activity table with consistent data structure
21
+ and automatic duration calculation.
22
+ """
23
+
24
+ def __init__(self, *args, **kwargs):
25
+ super().__init__(*args, **kwargs)
26
+ self.activity_log_key = None
27
+ self.start_time = None
28
+ self.end_time = None
29
+ self.activity_data = {}
30
+
31
+ def track_activity_start(self, tx, context):
32
+ """Start tracking activity for the current request"""
33
+ self.start_time = time.time()
34
+
35
+ # Gather common activity data
36
+ postdata = context.postdata()
37
+ payload = context.payload()
38
+
39
+ self.activity_data = {
40
+ "action": context.action(),
41
+ "args": json.dumps(context.args()),
42
+ "postdata": self._sanitize_postdata(postdata),
43
+ "handler_name": self.__class__.__name__,
44
+ "function_name": os.environ.get("AWS_LAMBDA_FUNCTION_NAME", "Unknown"),
45
+ "user_branch": os.environ.get("USER_BRANCH", "Unknown"),
46
+ "start_timestamp": self.start_time,
47
+ }
48
+
49
+ # Add user information if available
50
+ user_info = self._extract_user_info(payload)
51
+ if user_info:
52
+ self.activity_data.update(user_info)
53
+
54
+ # Add session information
55
+ session_data = context.session()
56
+ if session_data:
57
+ self.activity_data.update({k: v for k, v in session_data.items()
58
+ if k not in ['cognito_user']})
59
+
60
+ # Create the activity record
61
+ self.activity_log_key = tx.table("aws_api_activity").new(self.activity_data).pk
62
+
63
+ return self.activity_log_key
64
+
65
+ def track_activity_success(self, tx, context):
66
+ """Update activity record with success information"""
67
+ if not self.activity_log_key:
68
+ return
69
+
70
+ self.end_time = time.time()
71
+ update_data = {
72
+ "end_timestamp": self.end_time,
73
+ "duration": self.end_time - self.start_time,
74
+ "status": "success"
75
+ }
76
+
77
+ tx.table("aws_api_activity").update(update_data, self.activity_log_key)
78
+
79
+ def track_activity_error(self, tx, context, exception, tb_string):
80
+ """Update activity record with error information"""
81
+ if not self.activity_log_key:
82
+ return
83
+
84
+ self.end_time = time.time()
85
+ update_data = {
86
+ "end_timestamp": self.end_time,
87
+ "duration": self.end_time - self.start_time if self.start_time else 0,
88
+ "status": "error",
89
+ "exception_type": exception.__class__.__name__,
90
+ "exception_message": str(exception),
91
+ "traceback": tb_string,
92
+ }
93
+
94
+ # Handle legacy field names for backward compatibility
95
+ update_data["exception"] = exception.__class__.__name__
96
+
97
+ tx.table("aws_api_activity").update(update_data, self.activity_log_key)
98
+
99
+ def _sanitize_postdata(self, postdata: Dict) -> str:
100
+ """Remove sensitive information from postdata before logging"""
101
+ if not postdata:
102
+ return "{}"
103
+
104
+ sanitized = copy.deepcopy(postdata)
105
+
106
+ # Remove cognito user data from payload if present
107
+ if "payload" in sanitized and isinstance(sanitized["payload"], dict):
108
+ sanitized["payload"].pop("cognito_user", None)
109
+
110
+ # Remove other sensitive fields as needed
111
+ sensitive_fields = ["password", "token", "secret", "key", "auth", "cognito_user"]
112
+ self._recursive_sanitize(sanitized, sensitive_fields)
113
+
114
+ return json.dumps(sanitized)
115
+
116
+ def _recursive_sanitize(self, data: Any, sensitive_fields: list):
117
+ """Recursively remove sensitive fields from nested data structures"""
118
+ if isinstance(data, dict):
119
+ for key in list(data.keys()):
120
+ if any(field in key.lower() for field in sensitive_fields):
121
+ data[key] = "[REDACTED]"
122
+ else:
123
+ self._recursive_sanitize(data[key], sensitive_fields)
124
+ elif isinstance(data, list):
125
+ for item in data:
126
+ self._recursive_sanitize(item, sensitive_fields)
127
+
128
+ def _extract_user_info(self, payload: Dict) -> Dict:
129
+ """Extract user information from payload"""
130
+ user_info = {}
131
+
132
+ if payload and "cognito_user" in payload:
133
+ try:
134
+ attrs = payload["cognito_user"]["attributes"]
135
+ if "email" in attrs:
136
+ user_info["email_address"] = attrs["email"].lower()
137
+ if "sub" in attrs:
138
+ user_info["user_id"] = attrs["sub"]
139
+ except (KeyError, TypeError):
140
+ pass
141
+
142
+ return user_info
@@ -0,0 +1,192 @@
1
+ """
2
+ Error Handler Mixin for Lambda Handlers.
3
+
4
+ Provides standardized error handling, logging, and notification functionality
5
+ for Lambda handlers.
6
+ """
7
+
8
+ import copy
9
+ import os
10
+ import pprint
11
+ import time
12
+ from abc import ABC, abstractmethod
13
+ from typing import Dict, Any, Optional
14
+
15
+
16
+ class ErrorHandler(ABC):
17
+ """
18
+ Mixin class providing standardized error handling for Lambda handlers.
19
+
20
+ Handles error logging to sys_log table, email notifications to administrators,
21
+ and error metrics collection.
22
+ """
23
+
24
+ def handle_standard_error(self, tx, context, exception: Exception, tb_string: str):
25
+ """Handle errors with consistent logging and notification patterns"""
26
+
27
+ # Log to sys_log for centralized logging
28
+ self.log_error_to_system(tx, context, exception, tb_string)
29
+
30
+ # Determine if this error requires notification
31
+ if self._should_notify_error(exception):
32
+ self.send_error_notification(tx, context, exception, tb_string)
33
+
34
+ # Log error metrics for monitoring
35
+ self.log_error_metrics(tx, context, exception)
36
+
37
+ def log_error_to_system(self, tx, context, exception: Exception, tb_string: str):
38
+ """Log error to sys_log table"""
39
+ error_data = {
40
+ "level": "ERROR",
41
+ "message": str(exception),
42
+ "function": f"{self.__class__.__name__}.{context.action()}",
43
+ "traceback": tb_string,
44
+ "exception_type": exception.__class__.__name__,
45
+ "handler_name": self.__class__.__name__,
46
+ "action": context.action(),
47
+ "user_branch": os.environ.get("USER_BRANCH", "Unknown"),
48
+ "function_name": os.environ.get("AWS_LAMBDA_FUNCTION_NAME", "Unknown"),
49
+ "app_name": os.environ.get("ProjectName", "Unknown"),
50
+ "user_agent": "AWS Lambda",
51
+ "device_type": "Lambda",
52
+ "sys_modified_by": "Lambda",
53
+ }
54
+
55
+ # Add user context if available
56
+ try:
57
+ if hasattr(self, 'current_user') and self.current_user:
58
+ error_data["user_email"] = self.current_user.get("email_address")
59
+ except:
60
+ pass
61
+
62
+ tx.table("sys_log").insert(error_data)
63
+
64
+ def send_error_notification(self, tx, context, exception: Exception, tb_string: str):
65
+ """Send error notification email to administrators"""
66
+ try:
67
+ # Import here to avoid circular dependency
68
+ from support.app import helpers
69
+
70
+ environment = os.environ.get('USER_BRANCH', 'Unknown').title()
71
+ function_name = os.environ.get('AWS_LAMBDA_FUNCTION_NAME', 'Unknown')
72
+
73
+ subject = f"{environment} Lambda Error - {function_name}"
74
+
75
+ body = f"""
76
+ Error Details:
77
+ - Handler: {self.__class__.__name__}
78
+ - Action: {context.action()}
79
+ - Exception: {exception.__class__.__name__}
80
+ - Message: {str(exception)}
81
+ - Environment: {environment}
82
+ - Function: {function_name}
83
+
84
+ Full Traceback:
85
+ {tb_string}
86
+
87
+ Request Details:
88
+ {self._get_error_context(context)}
89
+ """
90
+
91
+ sender = self._get_error_notification_sender()
92
+ recipients = self._get_error_notification_recipients()
93
+
94
+ helpers.sendmail(
95
+ tx,
96
+ subject=subject,
97
+ body=body,
98
+ html=None,
99
+ sender=sender,
100
+ recipient=recipients[0],
101
+ cc=recipients[1:] if len(recipients) > 1 else None,
102
+ bcc=None,
103
+ email_settings_id=1001,
104
+ )
105
+ except Exception as email_error:
106
+ print(f"Failed to send error notification email: {email_error}")
107
+
108
+ def _should_notify_error(self, exception: Exception) -> bool:
109
+ """Determine if an error should trigger email notifications"""
110
+ # Don't notify for user authentication errors or validation errors
111
+ non_notification_types = [
112
+ "AuthenticationError",
113
+ "ValidationError",
114
+ "ValueError",
115
+ "AlertError"
116
+ ]
117
+
118
+ exception_name = exception.__class__.__name__
119
+
120
+ # Check for authentication-related exceptions
121
+ if "Authentication" in exception_name or "Auth" in exception_name:
122
+ return False
123
+
124
+ return exception_name not in non_notification_types
125
+
126
+ @abstractmethod
127
+ def _get_error_notification_recipients(self) -> list:
128
+ """
129
+ Get list of email recipients for error notifications.
130
+
131
+ Must be implemented by the handler class.
132
+
133
+ Returns:
134
+ List of email addresses to notify when errors occur
135
+
136
+ Example:
137
+ return ["admin@company.com", "devops@company.com"]
138
+ """
139
+ pass
140
+
141
+ @abstractmethod
142
+ def _get_error_notification_sender(self) -> str:
143
+ """
144
+ Get email sender for error notifications.
145
+
146
+ Must be implemented by the handler class.
147
+
148
+ Returns:
149
+ Email address to use as sender for error notifications
150
+
151
+ Example:
152
+ return "no-reply@company.com"
153
+ """
154
+ pass
155
+
156
+ def _get_error_context(self, context) -> str:
157
+ """Get sanitized request context for error reporting"""
158
+ try:
159
+ postdata = context.postdata()
160
+ sanitized = copy.deepcopy(postdata)
161
+
162
+ # Remove sensitive data
163
+ if "payload" in sanitized and isinstance(sanitized["payload"], dict):
164
+ sanitized["payload"].pop("cognito_user", None)
165
+
166
+ return pprint.pformat(sanitized)
167
+ except:
168
+ return "Unable to retrieve request context"
169
+
170
+ def log_error_metrics(self, tx, context, exception: Exception):
171
+ """Log error metrics for monitoring and alerting"""
172
+ try:
173
+ metrics_data = {
174
+ "metric_type": "error_count",
175
+ "handler_name": self.__class__.__name__,
176
+ "action": context.action(),
177
+ "exception_type": exception.__class__.__name__,
178
+ "environment": os.environ.get("USER_BRANCH", "Unknown"),
179
+ "function_name": os.environ.get("AWS_LAMBDA_FUNCTION_NAME", "Unknown"),
180
+ "timestamp": time.time(),
181
+ "sys_modified_by": "Lambda"
182
+ }
183
+
184
+ # Try to insert into metrics table if it exists
185
+ try:
186
+ tx.table("lambda_metrics").insert(metrics_data)
187
+ except:
188
+ # Metrics table might not exist yet, don't fail error handler
189
+ pass
190
+ except:
191
+ # Don't fail the error handler if metrics logging fails
192
+ pass
@@ -0,0 +1,53 @@
1
+ """
2
+ Legacy Mixin for backward compatibility.
3
+
4
+ Provides enhanced activity tracking while maintaining existing
5
+ beforeAction/afterAction/onError implementations in handlers.
6
+ """
7
+
8
+ import os
9
+ from .activity_tracker import ActivityTracker
10
+ from .error_handler import ErrorHandler
11
+
12
+
13
+ class LegacyMixin(ActivityTracker, ErrorHandler):
14
+ """
15
+ Legacy-compatible mixin that enhances existing handlers without breaking them.
16
+
17
+ This mixin adds standardized activity tracking and error handling
18
+ while preserving existing beforeAction/afterAction/onError implementations.
19
+
20
+ Use this when migrating existing handlers that have complex custom logic
21
+ in their action methods.
22
+ """
23
+
24
+ def _enhanced_before_action(self, tx, context):
25
+ """Enhanced beforeAction that adds activity tracking"""
26
+ # Start activity tracking
27
+ self.track_activity_start(tx, context)
28
+
29
+ def _enhanced_after_action(self, tx, context):
30
+ """Enhanced afterAction that adds activity tracking"""
31
+ # Update activity tracking with success
32
+ self.track_activity_success(tx, context)
33
+
34
+ def _enhanced_error_handler(self, tx, context, exc, tb):
35
+ """Enhanced onError that adds standardized error handling"""
36
+ # Convert exc to exception object if it's a string
37
+ if isinstance(exc, str):
38
+ exception = Exception(exc)
39
+ else:
40
+ exception = exc
41
+
42
+ # Update activity tracking with error
43
+ self.track_activity_error(tx, context, exception, tb)
44
+
45
+ # Handle error with standard patterns (but don't send duplicate emails)
46
+ self.log_error_to_system(tx, context, exception, tb)
47
+ self.log_error_metrics(tx, context, exception)
48
+
49
+ # Only send notification if handler doesn't already handle it
50
+ # Check for a flag that handlers can set to indicate they handle their own notifications
51
+ if not getattr(self, '_handles_own_error_notifications', False):
52
+ if self._should_notify_error(exception):
53
+ self.send_error_notification(tx, context, exception, tb)
@@ -0,0 +1,73 @@
1
+ """
2
+ Standard Mixin combining ActivityTracker and ErrorHandler.
3
+
4
+ Provides a single mixin that includes both activity tracking and error handling
5
+ with standardized beforeAction, afterAction, and onError implementations.
6
+ """
7
+
8
+ import copy
9
+ import pprint
10
+ from abc import ABC, abstractmethod
11
+
12
+ from .activity_tracker import ActivityTracker
13
+ from .error_handler import ErrorHandler
14
+
15
+
16
+ class StandardMixin(ActivityTracker, ErrorHandler):
17
+ """
18
+ Combined mixin providing both activity tracking and error handling.
19
+ Use this as the primary mixin for Lambda handlers.
20
+
21
+ Provides standard implementations of:
22
+ - beforeAction: Starts activity tracking + custom logic
23
+ - afterAction: Records success + custom logic
24
+ - onError: Records error + handles notifications + custom logic
25
+ """
26
+
27
+ def beforeAction(self, tx, context):
28
+ """Standard beforeAction implementation"""
29
+ # Start activity tracking
30
+ self.track_activity_start(tx, context)
31
+
32
+ # Call any custom beforeAction logic
33
+ self._custom_before_action(tx, context)
34
+
35
+ def afterAction(self, tx, context):
36
+ """Standard afterAction implementation"""
37
+ # Update activity tracking with success
38
+ self.track_activity_success(tx, context)
39
+
40
+ # Call any custom afterAction logic
41
+ self._custom_after_action(tx, context)
42
+
43
+ def onError(self, tx, context, exc, tb):
44
+ """Standard onError implementation"""
45
+ # Convert exc to exception object if it's a string
46
+ if isinstance(exc, str):
47
+ exception = Exception(exc)
48
+ else:
49
+ exception = exc
50
+
51
+ # Update activity tracking with error
52
+ self.track_activity_error(tx, context, exception, tb)
53
+
54
+ # Handle error with standard patterns
55
+ self.handle_standard_error(tx, context, exception, tb)
56
+
57
+ # Call any custom error handling
58
+ self._custom_error_handler(tx, context, exception, tb)
59
+
60
+ @abstractmethod
61
+ def _custom_before_action(self, tx, context):
62
+ """Override this method for handler-specific beforeAction logic"""
63
+ pass
64
+
65
+ @abstractmethod
66
+ def _custom_after_action(self, tx, context):
67
+ """Override this method for handler-specific afterAction logic"""
68
+ pass
69
+
70
+ @abstractmethod
71
+ def _custom_error_handler(self, tx, context, exception, tb):
72
+ """Override this method for handler-specific error handling"""
73
+ pass
@@ -0,0 +1,9 @@
1
+ """
2
+ Base abstract classes for database server implementations.
3
+ """
4
+ from .sql import BaseSQLDialect
5
+ from .types import BaseTypes
6
+ from .operators import BaseOperators
7
+ from .initializer import BaseInitializer
8
+
9
+ __all__ = ["BaseSQLDialect", "BaseTypes", "BaseOperators", "BaseInitializer"]
@@ -0,0 +1,69 @@
1
+ """
2
+ Abstract base class for database initialization.
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import Any, Dict, Optional
6
+ from velocity.db.core import engine
7
+
8
+
9
+ class BaseInitializer(ABC):
10
+ """
11
+ Abstract base class for database connection initialization.
12
+
13
+ Each database implementation should provide a concrete implementation
14
+ of the initialize method to set up database connections properly.
15
+ """
16
+
17
+ @staticmethod
18
+ @abstractmethod
19
+ def initialize(config: Optional[Dict[str, Any]] = None, **kwargs) -> engine.Engine:
20
+ """
21
+ Initialize a database engine with the appropriate driver and configuration.
22
+
23
+ Args:
24
+ config: Configuration dictionary (can be None)
25
+ **kwargs: Additional configuration parameters
26
+
27
+ Returns:
28
+ Configured Engine instance
29
+
30
+ Raises:
31
+ ImportError: If required database driver is not available
32
+ ValueError: If configuration is invalid
33
+ """
34
+ pass
35
+
36
+ @staticmethod
37
+ def _merge_config(base_config: Dict[str, Any], config: Optional[Dict[str, Any]], **kwargs) -> Dict[str, Any]:
38
+ """
39
+ Helper method to merge configuration from multiple sources.
40
+
41
+ Args:
42
+ base_config: Base configuration (e.g., from environment)
43
+ config: User-provided configuration
44
+ **kwargs: Additional keyword arguments
45
+
46
+ Returns:
47
+ Merged configuration dictionary
48
+ """
49
+ final_config = base_config.copy()
50
+ if config:
51
+ final_config.update(config)
52
+ final_config.update(kwargs)
53
+ return final_config
54
+
55
+ @staticmethod
56
+ def _validate_required_config(config: Dict[str, Any], required_keys: list[str]) -> None:
57
+ """
58
+ Validate that required configuration keys are present.
59
+
60
+ Args:
61
+ config: Configuration to validate
62
+ required_keys: List of required configuration keys
63
+
64
+ Raises:
65
+ ValueError: If required keys are missing
66
+ """
67
+ missing_keys = [key for key in required_keys if key not in config]
68
+ if missing_keys:
69
+ raise ValueError(f"Missing required configuration keys: {missing_keys}")
@@ -0,0 +1,98 @@
1
+ """
2
+ Abstract base class for database operator mapping implementations.
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import Dict
6
+
7
+
8
+ class BaseOperators(ABC):
9
+ """
10
+ Abstract base class that defines the interface for database operator mappings.
11
+
12
+ Each database implementation should provide concrete implementations of operator
13
+ mappings to handle conversion between Velocity.DB operators and SQL operators.
14
+ """
15
+
16
+ @classmethod
17
+ @abstractmethod
18
+ def get_operators(cls) -> Dict[str, str]:
19
+ """
20
+ Returns a dictionary mapping Velocity.DB operators to SQL operators.
21
+
22
+ This method should return a complete mapping of all operators supported
23
+ by this database implementation.
24
+
25
+ Returns:
26
+ Dictionary mapping operator symbols to SQL operators
27
+
28
+ Examples:
29
+ {
30
+ "=": "=",
31
+ "!=": "<>",
32
+ "<>": "<>",
33
+ "%": "LIKE",
34
+ "!%": "NOT LIKE",
35
+ "%%": "ILIKE", # PostgreSQL case-insensitive
36
+ "!%%": "NOT ILIKE",
37
+ "><": "BETWEEN",
38
+ "!><": "NOT BETWEEN",
39
+ # ... etc
40
+ }
41
+ """
42
+ pass
43
+
44
+ @classmethod
45
+ def get_base_operators(cls) -> Dict[str, str]:
46
+ """
47
+ Returns common operators supported by most databases.
48
+ Subclasses can use this as a starting point and override specific operators.
49
+
50
+ Returns:
51
+ Dictionary of common SQL operators
52
+ """
53
+ return {
54
+ "=": "=",
55
+ "==": "=",
56
+ "!=": "<>",
57
+ "<>": "<>",
58
+ "!": "<>",
59
+ "<": "<",
60
+ ">": ">",
61
+ "<=": "<=",
62
+ ">=": ">=",
63
+ "%": "LIKE",
64
+ "!%": "NOT LIKE",
65
+ "><": "BETWEEN",
66
+ "!><": "NOT BETWEEN",
67
+ ">!<": "NOT BETWEEN",
68
+ }
69
+
70
+ @classmethod
71
+ def supports_case_insensitive_like(cls) -> bool:
72
+ """
73
+ Returns True if this database supports case-insensitive LIKE operations.
74
+
75
+ Returns:
76
+ True if database supports ILIKE or similar
77
+ """
78
+ return False
79
+
80
+ @classmethod
81
+ def supports_regex(cls) -> bool:
82
+ """
83
+ Returns True if this database supports regular expressions.
84
+
85
+ Returns:
86
+ True if database supports regex operators
87
+ """
88
+ return False
89
+
90
+ @classmethod
91
+ def get_regex_operators(cls) -> Dict[str, str]:
92
+ """
93
+ Returns regex operators if supported by this database.
94
+
95
+ Returns:
96
+ Dictionary of regex operators or empty dict if not supported
97
+ """
98
+ return {}