Skip to content

Commit efa09d4

Browse files
Merge branch 'main' into db2-adapter-pr
Signed-off-by: IBM Db2 Eco System <Hdm-dev-persona-db2-eco-system@ibm.com>
2 parents b555b76 + ee57c61 commit efa09d4

19 files changed

Lines changed: 284 additions & 65 deletions

File tree

.devcontainer/devcontainer-lock.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@
1010
"resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:4fa87399214366e320d489991769c4f3f461e1ffe461f54eea78a41b34945bb5",
1111
"integrity": "sha256:4fa87399214366e320d489991769c4f3f461e1ffe461f54eea78a41b34945bb5"
1212
},
13-
"ghcr.io/devcontainers/features/java:1": {
14-
"version": "1.8.1",
15-
"resolved": "ghcr.io/devcontainers/features/java@sha256:8157bab2d8d71e40b2f3128c162fab763e2b11038fdd33784549290a5d386b48",
16-
"integrity": "sha256:8157bab2d8d71e40b2f3128c162fab763e2b11038fdd33784549290a5d386b48"
17-
},
1813
"ghcr.io/devcontainers/features/python:1": {
1914
"version": "1.8.0",
2015
"resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511",

.devcontainer/devcontainer.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,12 @@
66
"ghcr.io/devcontainers/features/python:1": {
77
"version": "3.12"
88
},
9-
"ghcr.io/devcontainers/features/java:1": {
10-
"version": "21"
11-
},
129
"ghcr.io/devcontainers/features/docker-in-docker:4": {}
1310
},
1411
"postCreateCommand": "bash .devcontainer/post-create-command.sh",
1512
"customizations": {
1613
"vscode": {
17-
"extensions": [
18-
"ms-python.python",
19-
"ms-python.vscode-pylance"
20-
]
14+
"extensions": ["ms-python.python", "ms-python.vscode-pylance"]
2115
}
2216
},
2317
"remoteUser": "vscode"

.github/workflows/pr.yaml

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
outputs:
1717
python: ${{ steps.filter.outputs.python }}
1818
client: ${{ steps.filter.outputs.client }}
19+
vscode: ${{ steps.filter.outputs.vscode }}
1920
ci: ${{ steps.filter.outputs.ci }}
2021
steps:
2122
- uses: actions/checkout@v7
@@ -34,6 +35,8 @@ jobs:
3435
- 'pyproject.toml'
3536
client:
3637
- 'web/client/**'
38+
vscode:
39+
- 'vscode/**'
3740
ci:
3841
- '.github/**'
3942
- 'Makefile'
@@ -188,9 +191,10 @@ jobs:
188191

189192
ui-style:
190193
needs: [changes]
191-
if: false
192-
# needs.changes.outputs.client == 'true' || needs.changes.outputs.ci ==
193-
# 'true' || github.ref == 'refs/heads/main'
194+
if:
195+
needs.changes.outputs.client == 'true' || needs.changes.outputs.vscode ==
196+
'true' || needs.changes.outputs.ci == 'true' || github.ref ==
197+
'refs/heads/main'
194198
runs-on: ubuntu-latest
195199
steps:
196200
- uses: actions/checkout@v7
@@ -252,7 +256,18 @@ jobs:
252256
fail-fast: false
253257
matrix:
254258
engine:
255-
[duckdb, postgres, mysql, mssql, trino, spark, clickhouse, risingwave, starrocks, db2]
259+
[
260+
duckdb,
261+
postgres,
262+
mysql,
263+
mssql,
264+
trino,
265+
spark,
266+
clickhouse,
267+
risingwave,
268+
starrocks,
269+
db2,
270+
]
256271
env:
257272
PYTEST_XDIST_AUTO_NUM_WORKERS: 2
258273
SQLMESH__DISABLE_ANONYMIZED_ANALYTICS: '1'
@@ -393,10 +408,13 @@ jobs:
393408
retention-days: 7
394409

395410
test-vscode:
411+
needs: changes
412+
if:
413+
needs.changes.outputs.vscode == 'true' || needs.changes.outputs.ci ==
414+
'true' || github.ref == 'refs/heads/main'
396415
env:
397416
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
398417
runs-on: ubuntu-latest
399-
if: false
400418
steps:
401419
- uses: actions/checkout@v7
402420
- uses: actions/setup-node@v7
@@ -457,7 +475,19 @@ jobs:
457475
strategy:
458476
fail-fast: false
459477
matrix:
460-
dbt-version: ['1.3', '1.4', '1.5', '1.6', '1.7', '1.8', '1.9', '1.10', '1.11', '1.12']
478+
dbt-version:
479+
[
480+
'1.3',
481+
'1.4',
482+
'1.5',
483+
'1.6',
484+
'1.7',
485+
'1.8',
486+
'1.9',
487+
'1.10',
488+
'1.11',
489+
'1.12',
490+
]
461491
steps:
462492
- uses: actions/checkout@v7
463493
- name: Set up Python

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,13 @@ engine-up: engine-clickhouse-up engine-mssql-up engine-mysql-up engine-postgres-
132132
engine-down: engine-clickhouse-down engine-mssql-down engine-mysql-down engine-postgres-down engine-spark-down engine-trino-down
133133

134134
fast-test:
135-
pytest -n auto -m "fast and not cicdonly" --junitxml=test-results/junit-fast-test.xml && pytest -m "isolated" && pytest -m "registry_isolation" && pytest -m "dialect_isolated"
135+
pytest -n auto -m "fast and not cicdonly and not isolated" --junitxml=test-results/junit-fast-test.xml && pytest -m "isolated and not slow" && pytest -m "registry_isolation" && pytest -m "dialect_isolated"
136136

137137
slow-test:
138-
pytest -n auto -m "(fast or slow) and not cicdonly" && pytest -m "isolated" && pytest -m "registry_isolation" && pytest -m "dialect_isolated"
138+
pytest -n auto -m "(fast or slow) and not cicdonly and not isolated" && pytest -m "isolated" && pytest -m "registry_isolation" && pytest -m "dialect_isolated"
139139

140140
cicd-test:
141-
pytest -n auto -m "(fast or slow) and not pyspark" --junitxml=test-results/junit-cicd.xml && pytest -m "pyspark" && pytest -m "isolated" && pytest -m "registry_isolation" && pytest -m "dialect_isolated"
141+
pytest -n auto -m "(fast or slow) and not pyspark and not isolated" --junitxml=test-results/junit-cicd.xml && pytest -m "pyspark" && pytest -m "isolated and not pyspark" && pytest -m "registry_isolation" && pytest -m "dialect_isolated"
142142

143143
core-fast-test:
144144
pytest -n auto -m "fast and not web and not github and not dbt and not jupyter"

sqlmesh/core/node.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
class IntervalUnit(str, Enum):
2929
"""IntervalUnit is the inferred granularity of an incremental node.
3030
31-
IntervalUnit can be one of 5 types, YEAR, MONTH, DAY, HOUR, MINUTE. The unit is inferred
31+
IntervalUnit can be one of 7 types: YEAR, MONTH, DAY, HOUR, HALF_HOUR, QUARTER_HOUR,
32+
FIVE_MINUTE. The unit is inferred
3233
based on the cron schedule of a node. The minimum time delta between a sample set of dates
3334
is used to determine which unit a node's schedule is.
3435

sqlmesh/core/state_sync/db/interval.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def remove_intervals(
108108
for snapshot in all_snapshots
109109
]
110110

111+
if not intervals_to_remove:
112+
return
113+
111114
if logger.isEnabledFor(logging.INFO):
112115
snapshot_ids = ", ".join(str(s.snapshot_id) for s, _ in intervals_to_remove)
113116
logger.info("Removing interval for snapshots: %s", snapshot_ids)

sqlmesh/core/test/definition.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,12 +263,9 @@ def assert_equal(
263263
for col, value in object_sentinel_values.items():
264264
try:
265265
# can't use `isinstance()` here - https://stackoverflow.com/a/68743663/1707525
266-
if type(value) is datetime.date:
267-
expected[col] = pd.to_datetime(expected[col]).dt.date
268-
elif type(value) is datetime.time:
269-
expected[col] = pd.to_datetime(expected[col]).dt.time
270-
elif type(value) is datetime.datetime:
271-
expected[col] = pd.to_datetime(expected[col]).dt.to_pydatetime()
266+
value_type = type(value)
267+
if value_type in (datetime.date, datetime.time, datetime.datetime):
268+
expected[col] = _parse_expected_datetime_column(expected[col], value_type)
272269
except Exception as e:
273270
from sqlmesh.core.console import get_console
274271

@@ -1014,6 +1011,34 @@ def _raise_error(msg: str, path: Path | None = None) -> None:
10141011
raise TestError(f"Failed to run test:\n{msg}")
10151012

10161013

1014+
def _parse_expected_datetime_column(series: pd.Series, target_type: type) -> pd.Series:
1015+
"""Convert a series of expected values to python ``date``/``time``/``datetime``.
1016+
1017+
Falls back to microsecond resolution when pandas' default nanosecond
1018+
parsing overflows. SQL ``TIMESTAMP`` columns can carry values outside
1019+
pandas' default ``datetime64[ns]`` range (1677-09-21..2262-04-11), so
1020+
unit tests may compare against values like ``0001-01-01`` which are
1021+
valid in the database but overflow the default resolution.
1022+
"""
1023+
import pandas as pd
1024+
from pandas.errors import OutOfBoundsDatetime
1025+
1026+
try:
1027+
parsed = pd.to_datetime(series)
1028+
except OutOfBoundsDatetime:
1029+
parsed = series.astype("datetime64[us]")
1030+
1031+
if target_type is datetime.date:
1032+
return parsed.dt.date
1033+
if target_type is datetime.time:
1034+
return parsed.dt.time
1035+
# `Series.dt.to_pydatetime()` returns an `ndarray` in pandas 2.x. Wrap it in a
1036+
# Series with ``dtype=object`` so pandas does not coerce the values back to
1037+
# ``pd.Timestamp`` (which would reintroduce the nanosecond overflow this
1038+
# function exists to avoid).
1039+
return pd.Series(parsed.dt.to_pydatetime(), index=parsed.index, dtype="object")
1040+
1041+
10171042
def _normalize_df_value(value: t.Any) -> t.Any:
10181043
"""Normalize data in a pandas dataframe so ruamel and sqlglot can deal with it."""
10191044
import numpy as np

sqlmesh/core/test/runner.py

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -125,25 +125,7 @@ def run_tests(
125125
# Ensure workers are not greater than the number of tests
126126
num_workers = min(len(model_test_metadata) or 1, default_test_connection.concurrent_tasks)
127127

128-
def _run_single_test(
129-
metadata: ModelTestMetadata, engine_adapter: EngineAdapter
130-
) -> t.Optional[ModelTextTestResult]:
131-
test = ModelTest.create_test(
132-
body=metadata.body,
133-
test_name=metadata.test_name,
134-
models=models,
135-
engine_adapter=engine_adapter,
136-
dialect=dialect,
137-
path=metadata.path,
138-
default_catalog=default_catalog,
139-
preserve_fixtures=preserve_fixtures,
140-
concurrency=num_workers > 1,
141-
verbosity=verbosity,
142-
)
143-
144-
if not test:
145-
return None
146-
128+
def _run_single_test(test: ModelTest) -> ModelTextTestResult:
147129
result = t.cast(
148130
ModelTextTestResult,
149131
ModelTextTestRunner().run(t.cast(unittest.TestCase, test)),
@@ -158,11 +140,30 @@ def _run_single_test(
158140

159141
start_time = time.perf_counter()
160142
try:
143+
# Build ModelTest instances on the calling thread before workers start. create_test()
144+
# can call to_datetime() / ttl_cache (time.time()), which races with another worker's
145+
# time_machine freeze when execution_time is set under concurrent_tasks > 1.
146+
# NOTE: We can run create_tests in a separate parallel stage for a future optimization.
147+
# We just can't overlap runs/creations.
148+
tests: list[ModelTest] = []
149+
for metadata, engine_adapter in metadata_to_adapter.items():
150+
test = ModelTest.create_test(
151+
body=metadata.body,
152+
test_name=metadata.test_name,
153+
models=models,
154+
engine_adapter=engine_adapter,
155+
dialect=dialect,
156+
path=metadata.path,
157+
default_catalog=default_catalog,
158+
preserve_fixtures=preserve_fixtures,
159+
concurrency=num_workers > 1,
160+
verbosity=verbosity,
161+
)
162+
if test:
163+
tests.append(test)
164+
161165
with ThreadPoolExecutor(max_workers=num_workers) as pool:
162-
futures = [
163-
pool.submit(_run_single_test, metadata=metadata, engine_adapter=engine_adapter)
164-
for metadata, engine_adapter in metadata_to_adapter.items()
165-
]
166+
futures = [pool.submit(_run_single_test, test) for test in tests]
166167

167168
for future in concurrent.futures.as_completed(futures):
168169
test_results.append(future.result())

tests/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,13 @@ def pytest_collection_modifyitems(items, *args, **kwargs):
212212
"dialect_isolated",
213213
}
214214
for item in items:
215+
if "capsys" in item.fixturenames:
216+
# capsys is not threadsafe, so the test must be isolated.
217+
item.add_marker("isolated")
215218
for marker in item.iter_markers():
216219
if marker.name in test_type_markers:
217220
break
218221
else:
219-
# if no test type marker is found, assume fast test
220222
item.add_marker("fast")
221223

222224

tests/core/engine_adapter/integration/docker/_common-hive.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,22 @@ services:
1212

1313
# S3-style object storage
1414
minio:
15-
image: 'minio/minio:RELEASE.2022-05-26T05-48-41Z'
15+
image: 'cgr.dev/chainguard/minio:latest@sha256:039800e64ec7247d2fde7cff3697e964f6fe20b6d7d2c46aa7d82cc63355d512'
1616
ports:
1717
- '9000:9000'
1818
- '9001:9001'
1919
environment:
20-
MINIO_ACCESS_KEY: minio
21-
MINIO_SECRET_KEY: minio123
20+
MINIO_ROOT_USER: minio
21+
MINIO_ROOT_PASSWORD: minio123
2222
command: server /data --console-address ":9001"
2323

2424
# Set up minio with default buckets / paths
2525
mc-job:
26-
image: 'minio/mc:RELEASE.2022-05-09T04-08-26Z'
26+
image: 'cgr.dev/chainguard/minio-client:latest-dev@sha256:fb635b967f5f32424391150dac4985cc1aea78fb0a1ce031c01952cf661a13ec'
2727
entrypoint: |
2828
/bin/bash -c "
2929
sleep 5;
30-
/usr/bin/mc config --quiet host add myminio http://minio:9000 minio minio123;
30+
/usr/bin/mc alias --quiet set myminio http://minio:9000 minio minio123;
3131
/usr/bin/mc mb --quiet myminio/trino/datalake;
3232
/usr/bin/mc mb --quiet myminio/trino/datalake_iceberg;
3333
/usr/bin/mc mb --quiet myminio/trino/datalake_delta;
@@ -39,4 +39,4 @@ services:
3939
/usr/bin/mc mb --quiet myminio/nessie/warehouse;
4040
"
4141
depends_on:
42-
- minio
42+
- minio

0 commit comments

Comments
 (0)