tree-sitter-analyzer 1.9.17.1__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.
Files changed (149) hide show
  1. tree_sitter_analyzer/__init__.py +132 -0
  2. tree_sitter_analyzer/__main__.py +11 -0
  3. tree_sitter_analyzer/api.py +853 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +12 -0
  6. tree_sitter_analyzer/cli/argument_validator.py +89 -0
  7. tree_sitter_analyzer/cli/commands/__init__.py +26 -0
  8. tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
  9. tree_sitter_analyzer/cli/commands/base_command.py +181 -0
  10. tree_sitter_analyzer/cli/commands/default_command.py +18 -0
  11. tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
  12. tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
  13. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
  14. tree_sitter_analyzer/cli/commands/query_command.py +109 -0
  15. tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
  16. tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
  17. tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
  18. tree_sitter_analyzer/cli/commands/table_command.py +414 -0
  19. tree_sitter_analyzer/cli/info_commands.py +124 -0
  20. tree_sitter_analyzer/cli_main.py +472 -0
  21. tree_sitter_analyzer/constants.py +85 -0
  22. tree_sitter_analyzer/core/__init__.py +15 -0
  23. tree_sitter_analyzer/core/analysis_engine.py +580 -0
  24. tree_sitter_analyzer/core/cache_service.py +333 -0
  25. tree_sitter_analyzer/core/engine.py +585 -0
  26. tree_sitter_analyzer/core/parser.py +293 -0
  27. tree_sitter_analyzer/core/query.py +605 -0
  28. tree_sitter_analyzer/core/query_filter.py +200 -0
  29. tree_sitter_analyzer/core/query_service.py +340 -0
  30. tree_sitter_analyzer/encoding_utils.py +530 -0
  31. tree_sitter_analyzer/exceptions.py +747 -0
  32. tree_sitter_analyzer/file_handler.py +246 -0
  33. tree_sitter_analyzer/formatters/__init__.py +1 -0
  34. tree_sitter_analyzer/formatters/base_formatter.py +201 -0
  35. tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
  36. tree_sitter_analyzer/formatters/formatter_config.py +197 -0
  37. tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
  38. tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
  39. tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
  40. tree_sitter_analyzer/formatters/go_formatter.py +368 -0
  41. tree_sitter_analyzer/formatters/html_formatter.py +498 -0
  42. tree_sitter_analyzer/formatters/java_formatter.py +423 -0
  43. tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
  44. tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
  45. tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
  46. tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
  47. tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
  48. tree_sitter_analyzer/formatters/php_formatter.py +301 -0
  49. tree_sitter_analyzer/formatters/python_formatter.py +830 -0
  50. tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
  51. tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
  52. tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
  53. tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
  54. tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
  55. tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
  56. tree_sitter_analyzer/interfaces/__init__.py +9 -0
  57. tree_sitter_analyzer/interfaces/cli.py +535 -0
  58. tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
  59. tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
  60. tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
  61. tree_sitter_analyzer/language_detector.py +553 -0
  62. tree_sitter_analyzer/language_loader.py +271 -0
  63. tree_sitter_analyzer/languages/__init__.py +10 -0
  64. tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
  65. tree_sitter_analyzer/languages/css_plugin.py +449 -0
  66. tree_sitter_analyzer/languages/go_plugin.py +836 -0
  67. tree_sitter_analyzer/languages/html_plugin.py +496 -0
  68. tree_sitter_analyzer/languages/java_plugin.py +1299 -0
  69. tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
  70. tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
  71. tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
  72. tree_sitter_analyzer/languages/php_plugin.py +862 -0
  73. tree_sitter_analyzer/languages/python_plugin.py +1636 -0
  74. tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
  75. tree_sitter_analyzer/languages/rust_plugin.py +673 -0
  76. tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
  77. tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
  78. tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
  79. tree_sitter_analyzer/legacy_table_formatter.py +860 -0
  80. tree_sitter_analyzer/mcp/__init__.py +34 -0
  81. tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
  82. tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
  83. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
  84. tree_sitter_analyzer/mcp/server.py +869 -0
  85. tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
  86. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
  87. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
  88. tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
  89. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
  90. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
  91. tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
  92. tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
  93. tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
  94. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
  95. tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
  96. tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
  97. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
  98. tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
  99. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
  100. tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
  101. tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
  102. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
  103. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
  104. tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
  105. tree_sitter_analyzer/models.py +840 -0
  106. tree_sitter_analyzer/mypy_current_errors.txt +2 -0
  107. tree_sitter_analyzer/output_manager.py +255 -0
  108. tree_sitter_analyzer/platform_compat/__init__.py +3 -0
  109. tree_sitter_analyzer/platform_compat/adapter.py +324 -0
  110. tree_sitter_analyzer/platform_compat/compare.py +224 -0
  111. tree_sitter_analyzer/platform_compat/detector.py +67 -0
  112. tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
  113. tree_sitter_analyzer/platform_compat/profiles.py +217 -0
  114. tree_sitter_analyzer/platform_compat/record.py +55 -0
  115. tree_sitter_analyzer/platform_compat/recorder.py +155 -0
  116. tree_sitter_analyzer/platform_compat/report.py +92 -0
  117. tree_sitter_analyzer/plugins/__init__.py +280 -0
  118. tree_sitter_analyzer/plugins/base.py +647 -0
  119. tree_sitter_analyzer/plugins/manager.py +384 -0
  120. tree_sitter_analyzer/project_detector.py +328 -0
  121. tree_sitter_analyzer/queries/__init__.py +27 -0
  122. tree_sitter_analyzer/queries/csharp.py +216 -0
  123. tree_sitter_analyzer/queries/css.py +615 -0
  124. tree_sitter_analyzer/queries/go.py +275 -0
  125. tree_sitter_analyzer/queries/html.py +543 -0
  126. tree_sitter_analyzer/queries/java.py +402 -0
  127. tree_sitter_analyzer/queries/javascript.py +724 -0
  128. tree_sitter_analyzer/queries/kotlin.py +192 -0
  129. tree_sitter_analyzer/queries/markdown.py +258 -0
  130. tree_sitter_analyzer/queries/php.py +95 -0
  131. tree_sitter_analyzer/queries/python.py +859 -0
  132. tree_sitter_analyzer/queries/ruby.py +92 -0
  133. tree_sitter_analyzer/queries/rust.py +223 -0
  134. tree_sitter_analyzer/queries/sql.py +555 -0
  135. tree_sitter_analyzer/queries/typescript.py +871 -0
  136. tree_sitter_analyzer/queries/yaml.py +236 -0
  137. tree_sitter_analyzer/query_loader.py +272 -0
  138. tree_sitter_analyzer/security/__init__.py +22 -0
  139. tree_sitter_analyzer/security/boundary_manager.py +277 -0
  140. tree_sitter_analyzer/security/regex_checker.py +297 -0
  141. tree_sitter_analyzer/security/validator.py +599 -0
  142. tree_sitter_analyzer/table_formatter.py +782 -0
  143. tree_sitter_analyzer/utils/__init__.py +53 -0
  144. tree_sitter_analyzer/utils/logging.py +433 -0
  145. tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
  146. tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
  147. tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
  148. tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
  149. tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
@@ -0,0 +1,224 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any
3
+
4
+ from deepdiff import DeepDiff
5
+
6
+ from .profiles import BehaviorProfile
7
+
8
+
9
+ @dataclass
10
+ class BehaviorDifference:
11
+ """Represents a difference in behavior for a specific construct."""
12
+
13
+ construct_id: str
14
+ diff_type: (
15
+ str # "missing", "attribute_mismatch", "error_mismatch", "count_mismatch"
16
+ )
17
+ details: str
18
+ platform_a_value: Any
19
+ platform_b_value: Any
20
+
21
+
22
+ @dataclass
23
+ class ProfileComparison:
24
+ """Result of comparing two profiles."""
25
+
26
+ platform_a: str
27
+ platform_b: str
28
+ differences: list[BehaviorDifference]
29
+
30
+ @property
31
+ def has_differences(self) -> bool:
32
+ return len(self.differences) > 0
33
+
34
+
35
+ def compare_profiles(
36
+ profile_a: BehaviorProfile, profile_b: BehaviorProfile
37
+ ) -> ProfileComparison:
38
+ """
39
+ Compares two behavior profiles and identifies differences.
40
+
41
+ Args:
42
+ profile_a: First profile.
43
+ profile_b: Second profile.
44
+
45
+ Returns:
46
+ ProfileComparison: The comparison result.
47
+ """
48
+ differences = []
49
+
50
+ # Check for missing constructs
51
+ keys_a = set(profile_a.behaviors.keys())
52
+ keys_b = set(profile_b.behaviors.keys())
53
+
54
+ for key in keys_a - keys_b:
55
+ differences.append(
56
+ BehaviorDifference(
57
+ construct_id=key,
58
+ diff_type="missing",
59
+ details=f"Construct {key} missing in {profile_b.platform_key}",
60
+ platform_a_value="present",
61
+ platform_b_value="missing",
62
+ )
63
+ )
64
+
65
+ for key in keys_b - keys_a:
66
+ differences.append(
67
+ BehaviorDifference(
68
+ construct_id=key,
69
+ diff_type="missing",
70
+ details=f"Construct {key} missing in {profile_a.platform_key}",
71
+ platform_a_value="missing",
72
+ platform_b_value="present",
73
+ )
74
+ )
75
+
76
+ # Compare common constructs
77
+ for key in keys_a.intersection(keys_b):
78
+ beh_a = profile_a.behaviors[key]
79
+ beh_b = profile_b.behaviors[key]
80
+
81
+ # Compare error status
82
+ if beh_a.has_error != beh_b.has_error:
83
+ differences.append(
84
+ BehaviorDifference(
85
+ construct_id=key,
86
+ diff_type="error_mismatch",
87
+ details=f"Error status mismatch for {key}",
88
+ platform_a_value=beh_a.has_error,
89
+ platform_b_value=beh_b.has_error,
90
+ )
91
+ )
92
+
93
+ # Compare element count
94
+ if beh_a.element_count != beh_b.element_count:
95
+ differences.append(
96
+ BehaviorDifference(
97
+ construct_id=key,
98
+ diff_type="count_mismatch",
99
+ details=f"Element count mismatch for {key}",
100
+ platform_a_value=beh_a.element_count,
101
+ platform_b_value=beh_b.element_count,
102
+ )
103
+ )
104
+
105
+ # Compare attributes
106
+ # We use DeepDiff for detailed comparison if needed, or just set comparison
107
+ if beh_a.attributes != beh_b.attributes:
108
+ # Use DeepDiff to get readable diff
109
+ diff = DeepDiff(beh_a.attributes, beh_b.attributes, ignore_order=True)
110
+ if diff:
111
+ differences.append(
112
+ BehaviorDifference(
113
+ construct_id=key,
114
+ diff_type="attribute_mismatch",
115
+ details=f"Attributes mismatch for {key}",
116
+ platform_a_value=beh_a.attributes,
117
+ platform_b_value=beh_b.attributes,
118
+ )
119
+ )
120
+
121
+ return ProfileComparison(
122
+ platform_a=profile_a.platform_key,
123
+ platform_b=profile_b.platform_key,
124
+ differences=differences,
125
+ )
126
+
127
+
128
+ def generate_diff_report(comparison: ProfileComparison) -> str:
129
+ """
130
+ Generates a human-readable report of the differences.
131
+
132
+ Args:
133
+ comparison: The comparison result.
134
+
135
+ Returns:
136
+ str: The report text.
137
+ """
138
+ if not comparison.has_differences:
139
+ return f"No differences found between {comparison.platform_a} and {comparison.platform_b}."
140
+
141
+ lines = [
142
+ f"Comparison Report: {comparison.platform_a} vs {comparison.platform_b}",
143
+ "=" * 60,
144
+ f"Total differences: {len(comparison.differences)}",
145
+ "",
146
+ ]
147
+
148
+ for diff in comparison.differences:
149
+ lines.append(f"Construct: {diff.construct_id}")
150
+ lines.append(f"Type: {diff.diff_type}")
151
+ lines.append(f"Details: {diff.details}")
152
+ lines.append(f" {comparison.platform_a}: {diff.platform_a_value}")
153
+ lines.append(f" {comparison.platform_b}: {diff.platform_b_value}")
154
+ lines.append("-" * 40)
155
+
156
+ return "\n".join(lines)
157
+
158
+
159
+ if __name__ == "__main__":
160
+ import argparse
161
+ import sys
162
+ from pathlib import Path
163
+
164
+ parser = argparse.ArgumentParser(description="Compare two SQL behavior profiles")
165
+ parser.add_argument("profile_a", type=str, help="Path to first profile")
166
+ parser.add_argument("profile_b", type=str, help="Path to second profile")
167
+ parser.add_argument(
168
+ "--fail-on-diff",
169
+ action="store_true",
170
+ help="Exit with error code if differences found",
171
+ )
172
+
173
+ args = parser.parse_args()
174
+
175
+ try:
176
+ path_a = Path(args.profile_a)
177
+ path_b = Path(args.profile_b)
178
+
179
+ if not path_a.exists():
180
+ print(f"Error: Profile not found: {path_a}")
181
+ sys.exit(1)
182
+
183
+ if not path_b.exists():
184
+ print(f"Error: Profile not found: {path_b}")
185
+ sys.exit(1)
186
+
187
+ # Load profiles manually since BehaviorProfile.load expects a platform key
188
+ # We need to load from specific files
189
+ import json
190
+
191
+ from .profiles import BehaviorProfile, ParsingBehavior
192
+
193
+ def load_profile_from_file(path: Path) -> BehaviorProfile:
194
+ with open(path, encoding="utf-8") as f:
195
+ data = json.load(f)
196
+
197
+ # Convert behaviors dict to ParsingBehavior objects
198
+ behaviors = {}
199
+ for key, b_data in data.get("behaviors", {}).items():
200
+ if isinstance(b_data, dict):
201
+ behaviors[key] = ParsingBehavior(**b_data)
202
+ else:
203
+ behaviors[key] = b_data
204
+
205
+ return BehaviorProfile(
206
+ schema_version=data.get("schema_version", "1.0.0"),
207
+ platform_key=data["platform_key"],
208
+ behaviors=behaviors,
209
+ adaptation_rules=data.get("adaptation_rules", []),
210
+ )
211
+
212
+ profile_a = load_profile_from_file(path_a)
213
+ profile_b = load_profile_from_file(path_b)
214
+
215
+ comparison = compare_profiles(profile_a, profile_b)
216
+ report = generate_diff_report(comparison)
217
+ print(report)
218
+
219
+ if args.fail_on_diff and comparison.has_differences:
220
+ sys.exit(1)
221
+
222
+ except Exception as e:
223
+ print(f"Error comparing profiles: {e}")
224
+ sys.exit(1)
@@ -0,0 +1,67 @@
1
+ import platform
2
+ import sys
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+
7
+ @dataclass
8
+ class PlatformInfo:
9
+ """Platform identification information."""
10
+
11
+ os_name: str
12
+ os_version: str
13
+ python_version: str
14
+ platform_key: str
15
+
16
+
17
+ class PlatformDetector:
18
+ """Detects current platform and Python version."""
19
+
20
+ @staticmethod
21
+ def detect() -> PlatformInfo:
22
+ """
23
+ Detects the current platform information.
24
+
25
+ Returns:
26
+ PlatformInfo: The detected platform information.
27
+ """
28
+ os_name = platform.system().lower()
29
+ os_version = platform.release()
30
+ python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
31
+
32
+ # Normalize os_name
33
+ if os_name == "darwin":
34
+ os_name = "macos"
35
+
36
+ platform_key = f"{os_name}-{python_version}"
37
+
38
+ return PlatformInfo(
39
+ os_name=os_name,
40
+ os_version=os_version,
41
+ python_version=python_version,
42
+ platform_key=platform_key,
43
+ )
44
+
45
+ @staticmethod
46
+ def get_profile_path(
47
+ base_path: Path, platform_info: PlatformInfo | None = None
48
+ ) -> Path:
49
+ """
50
+ Resolves the path to the profile file for the given platform.
51
+
52
+ Args:
53
+ base_path: The base directory where profiles are stored.
54
+ platform_info: The platform info to use. If None, detects current platform.
55
+
56
+ Returns:
57
+ Path: The path to the profile file.
58
+ """
59
+ if platform_info is None:
60
+ platform_info = PlatformDetector.detect()
61
+
62
+ return (
63
+ base_path
64
+ / platform_info.os_name
65
+ / platform_info.python_version
66
+ / "profile.json"
67
+ )
@@ -0,0 +1,228 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class SQLTestFixture:
6
+ """A SQL code sample for testing platform compatibility."""
7
+
8
+ id: str
9
+ sql: str
10
+ description: str
11
+ expected_constructs: list[
12
+ str
13
+ ] # List of construct types expected (e.g. "table", "view")
14
+ is_edge_case: bool = False
15
+ known_platform_issues: list[str] | None = None
16
+
17
+
18
+ # Standard SQL constructs
19
+
20
+ FIXTURE_SIMPLE_TABLE = SQLTestFixture(
21
+ id="simple_table",
22
+ sql="""
23
+ CREATE TABLE users (
24
+ id INT PRIMARY KEY,
25
+ username VARCHAR(50) NOT NULL,
26
+ email VARCHAR(100)
27
+ );
28
+ """,
29
+ description="Basic table with columns",
30
+ expected_constructs=["table"],
31
+ )
32
+
33
+ FIXTURE_COMPLEX_TABLE = SQLTestFixture(
34
+ id="complex_table",
35
+ sql="""
36
+ CREATE TABLE orders (
37
+ order_id INT PRIMARY KEY,
38
+ user_id INT,
39
+ order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
40
+ total_amount DECIMAL(10, 2),
41
+ CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id)
42
+ );
43
+ """,
44
+ description="Table with constraints and foreign keys",
45
+ expected_constructs=["table"],
46
+ )
47
+
48
+ FIXTURE_VIEW_WITH_JOIN = SQLTestFixture(
49
+ id="view_with_join",
50
+ sql="""
51
+ CREATE VIEW user_orders AS
52
+ SELECT u.username, o.order_date, o.total_amount
53
+ FROM users u
54
+ JOIN orders o ON u.id = o.user_id;
55
+ """,
56
+ description="View with JOIN operations",
57
+ expected_constructs=["view"],
58
+ )
59
+
60
+ FIXTURE_STORED_PROCEDURE = SQLTestFixture(
61
+ id="stored_procedure",
62
+ sql="""
63
+ CREATE PROCEDURE GetUserOrders(IN userId INT)
64
+ BEGIN
65
+ SELECT * FROM orders WHERE user_id = userId;
66
+ END;
67
+ """,
68
+ description="Procedure with parameters",
69
+ expected_constructs=["procedure"],
70
+ )
71
+
72
+ FIXTURE_FUNCTION_WITH_PARAMS = SQLTestFixture(
73
+ id="function_with_params",
74
+ sql="""
75
+ CREATE FUNCTION CalculateTax(amount DECIMAL(10,2))
76
+ RETURNS DECIMAL(10,2)
77
+ BEGIN
78
+ RETURN amount * 0.15;
79
+ END;
80
+ """,
81
+ description="Function with parameters and return type",
82
+ expected_constructs=["function"],
83
+ )
84
+
85
+ FIXTURE_TRIGGER_BEFORE_INSERT = SQLTestFixture(
86
+ id="trigger_before_insert",
87
+ sql="""
88
+ CREATE TRIGGER before_order_insert
89
+ BEFORE INSERT ON orders
90
+ FOR EACH ROW
91
+ BEGIN
92
+ SET NEW.order_date = NOW();
93
+ END;
94
+ """,
95
+ description="Trigger with timing and event",
96
+ expected_constructs=["trigger"],
97
+ )
98
+
99
+ FIXTURE_INDEX_UNIQUE = SQLTestFixture(
100
+ id="index_unique",
101
+ sql="""
102
+ CREATE UNIQUE INDEX idx_user_email ON users(email);
103
+ """,
104
+ description="Unique index on table",
105
+ expected_constructs=["index"],
106
+ )
107
+
108
+ # Edge case fixtures for platform issues
109
+
110
+ FIXTURE_FUNCTION_WITH_SELECT = SQLTestFixture(
111
+ id="function_with_select",
112
+ sql="""
113
+ CREATE FUNCTION GetTotalSales() RETURNS DECIMAL(10,2)
114
+ BEGIN
115
+ DECLARE total DECIMAL(10,2);
116
+ SELECT SUM(total_amount) INTO total FROM orders;
117
+ RETURN total;
118
+ END;
119
+ """,
120
+ description="Function with SELECT in body (Ubuntu 3.12 issue)",
121
+ expected_constructs=["function"],
122
+ is_edge_case=True,
123
+ known_platform_issues=["ubuntu-3.12"],
124
+ )
125
+
126
+ FIXTURE_TRIGGER_WITH_DESCRIPTION = SQLTestFixture(
127
+ id="trigger_with_description",
128
+ sql="""
129
+ CREATE TRIGGER update_description
130
+ BEFORE UPDATE ON products
131
+ FOR EACH ROW
132
+ BEGIN
133
+ -- Trigger logic here
134
+ END;
135
+ """,
136
+ description="Trigger name extraction (macOS issue where name might be confused with description keyword if present)",
137
+ expected_constructs=["trigger"],
138
+ is_edge_case=True,
139
+ known_platform_issues=["macos"],
140
+ )
141
+
142
+ FIXTURE_FUNCTION_WITH_AUTO_INCREMENT = SQLTestFixture(
143
+ id="function_with_auto_increment",
144
+ sql="""
145
+ CREATE TABLE items (
146
+ id INT AUTO_INCREMENT PRIMARY KEY
147
+ );
148
+
149
+ CREATE FUNCTION GetNextId() RETURNS INT
150
+ BEGIN
151
+ RETURN 1;
152
+ END;
153
+ """,
154
+ description="Function near AUTO_INCREMENT (Windows issue)",
155
+ expected_constructs=["table", "function"],
156
+ is_edge_case=True,
157
+ known_platform_issues=["windows"],
158
+ )
159
+
160
+ FIXTURE_VIEW_IN_ERROR_NODE = SQLTestFixture(
161
+ id="view_in_error_node",
162
+ sql="""
163
+ -- Some complex SQL that might confuse the parser
164
+ CREATE VIEW complex_view AS
165
+ WITH cte AS (SELECT 1)
166
+ SELECT * FROM cte;
167
+ """,
168
+ description="View that appears in ERROR nodes on some platforms",
169
+ expected_constructs=["view"],
170
+ is_edge_case=True,
171
+ )
172
+
173
+ FIXTURE_PHANTOM_TRIGGER = SQLTestFixture(
174
+ id="phantom_trigger",
175
+ sql="""
176
+ -- A comment that looks like a trigger
177
+ -- CREATE TRIGGER phantom_trigger
178
+ CREATE TABLE real_table (id INT);
179
+ """,
180
+ description="Trigger that creates phantom elements on some platforms",
181
+ expected_constructs=["table"], # Should NOT contain trigger
182
+ is_edge_case=True,
183
+ known_platform_issues=["ubuntu-3.12"],
184
+ )
185
+
186
+ FIXTURE_PROCEDURE_WITH_COMMENTS = SQLTestFixture(
187
+ id="procedure_with_comments",
188
+ sql="""
189
+ CREATE PROCEDURE ComplexProc()
190
+ BEGIN
191
+ /*
192
+ Multi-line comment
193
+ that might confuse parser
194
+ */
195
+ SELECT 1;
196
+ END;
197
+ """,
198
+ description="Procedure with complex comments",
199
+ expected_constructs=["procedure"],
200
+ is_edge_case=True,
201
+ )
202
+
203
+ FIXTURE_INDEX_ON_EXPRESSION = SQLTestFixture(
204
+ id="index_on_expression",
205
+ sql="""
206
+ CREATE INDEX idx_lower_email ON users((lower(email)));
207
+ """,
208
+ description="Index on expression",
209
+ expected_constructs=["index"],
210
+ is_edge_case=True,
211
+ )
212
+
213
+ ALL_FIXTURES = [
214
+ FIXTURE_SIMPLE_TABLE,
215
+ FIXTURE_COMPLEX_TABLE,
216
+ FIXTURE_VIEW_WITH_JOIN,
217
+ FIXTURE_STORED_PROCEDURE,
218
+ FIXTURE_FUNCTION_WITH_PARAMS,
219
+ FIXTURE_TRIGGER_BEFORE_INSERT,
220
+ FIXTURE_INDEX_UNIQUE,
221
+ FIXTURE_FUNCTION_WITH_SELECT,
222
+ FIXTURE_TRIGGER_WITH_DESCRIPTION,
223
+ FIXTURE_FUNCTION_WITH_AUTO_INCREMENT,
224
+ FIXTURE_VIEW_IN_ERROR_NODE,
225
+ FIXTURE_PHANTOM_TRIGGER,
226
+ FIXTURE_PROCEDURE_WITH_COMMENTS,
227
+ FIXTURE_INDEX_ON_EXPRESSION,
228
+ ]