Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ on:
branches: [ main ]
workflow_call:

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
test:
runs-on: ubuntu-latest
Expand All @@ -32,15 +35,15 @@ jobs:
--health-retries 5

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

- name: Cache pip dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ~/.cache/pip
key: ${{ runner.os }}-py${{ matrix.python-version }}-mongo${{ matrix.mongodb-version }}-sqlalchemy-${{ matrix.sqlalchemy-version }}-pip-${{ hashFiles('**/requirements-test.txt', 'pyproject.toml') }}
Expand Down Expand Up @@ -101,7 +104,7 @@ jobs:
python -m pytest tests/ --cov=pymongosql --cov-report=term-missing --cov-report=xml

- name: Upload coverage reports
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
env_vars: OS,PYTHON
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
19 changes: 10 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@ on:
env:
PYTHON_VERSION: "3.11"
MONGODB_VERSION: "8.0"
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
lint-and-format:
name: Code Quality Checks
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Cache pip dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-lint-${{ hashFiles('**/requirements-test.txt', 'pyproject.toml') }}
Expand Down Expand Up @@ -57,13 +58,13 @@ jobs:
needs: [lint-and-format, test]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
# Fetch full history for setuptools_scm
fetch-depth: 0

- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}

Expand All @@ -85,7 +86,7 @@ jobs:
ls -la dist/

- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: dist
path: dist/
Expand All @@ -100,7 +101,7 @@ jobs:

steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: dist
path: dist/
Expand All @@ -118,12 +119,12 @@ jobs:
contents: write

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Download build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: dist
path: dist/
Expand Down
2 changes: 1 addition & 1 deletion pymongosql/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
if TYPE_CHECKING:
from .connection import Connection

__version__: str = "0.4.5"
__version__: str = "0.4.6"

# Globals https://www.python.org/dev/peps/pep-0249/#globals
apilevel: str = "2.0"
Expand Down
34 changes: 29 additions & 5 deletions pymongosql/sql/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
"NOT IN": "$nin",
}

# Pattern to count all comparison operations in ANTLR getText() output (no spaces).
# Includes standard operators and SQL keywords (IN, LIKE, BETWEEN, IS [NOT] NULL).
_COMPARISON_COUNT_PATTERN = re.compile(
r">=|<=|!=|<>|=|<|>|IN\(|LIKE['\"]|BETWEEN|ISNOTNULL|ISNULL",
re.IGNORECASE,
)


class ContextUtilsMixin:
"""Mixin providing common context utility methods"""
Expand Down Expand Up @@ -194,8 +201,10 @@ def can_handle(self, ctx: Any) -> bool:
text = self.get_context_text(ctx)
text_upper = text.upper()

# Count comparison operators
comparison_count = sum(1 for op in COMPARISON_OPERATORS if op in text)
# Count comparison operator *occurrences* (not distinct types) so that
# "B=trueANDA=1" is correctly seen as having 2 comparisons even though
# both use the same "=" operator. Also counts IN(, LIKE, BETWEEN, etc.
comparison_count = len(_COMPARISON_COUNT_PATTERN.findall(text))

# If there are multiple comparisons and logical operators, it's a logical expression
has_logical_ops = any(op in text_upper for op in LOGICAL_OPERATORS)
Expand Down Expand Up @@ -550,8 +559,23 @@ def _find_operator_positions(self, text: str, operator: str) -> List[int]:
and i + len(operator) < len(text)
and text[i + len(operator)].isalpha()
):
i += len(operator)
continue
# ANTLR getText() concatenates tokens without spaces, so
# "B=true AND A=1" becomes "B=trueANDA=1". We must still
# recognise AND/OR here when the preceding text ends with a
# SQL literal keyword (true/false/null) that is itself
# preceded by a comparison operator (=, <, >, !, etc.).
# This avoids false positives like "category" containing "OR".
before_upper = text[:i].upper()
is_value_boundary = False
for keyword in ("TRUE", "FALSE", "NULL"):
if before_upper.endswith(keyword):
prefix = text[: i - len(keyword)]
if prefix and prefix[-1] in ("=", "<", ">", "!"):
is_value_boundary = True
break
if not is_value_boundary:
i += len(operator)
continue

# Check parentheses and quote depth
if self._is_at_valid_split_position(text, i):
Expand Down Expand Up @@ -583,7 +607,7 @@ def _has_logical_operators(self, ctx: Any) -> bool:
# Count comparison operator occurrences, not just distinct operator types
# so that "a = 1 OR b = 2" counts as 2 comparisons and is treated
# as a logical expression instead of a single comparison.
comparison_count = len(re.findall(r"(>=|<=|!=|<>|=|<|>)", text))
comparison_count = len(_COMPARISON_COUNT_PATTERN.findall(text))
has_logical_ops = any(op in text for op in ["AND", "OR"])
return has_logical_ops and comparison_count >= 2
except Exception:
Expand Down
2 changes: 1 addition & 1 deletion requirements-optional.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# SQLAlchemy support (optional) - supports 1.4+ and 2.x
sqlalchemy>=1.4.0,<3.0.0
sqlalchemy>=1.4.0,<3.0.0
2 changes: 1 addition & 1 deletion requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
pytest>=7.0.0
pytest-cov>=4.0.0
flake8>=6.0.0
flake8-pyproject>=1.2.0
flake8-pyproject>=1.2.0
Loading
Loading