Compare commits

...

304 Commits

Author SHA1 Message Date
Github Build Bot
62ea994a33 Bumping version to 1.7.17 and generate changelog 2024-06-20 20:29:20 +00:00
Kshitij Aranke
0593c0baa6 [Backport 1.7.latest] Add most recent dbt-docs changes (#10335) 2024-06-20 15:07:31 -04:00
FishtownBuildBot
d96a9271ff [Automated] Merged prep-release/1.7.16_9389782380 into target 1.7.latest during release process 2024-06-05 15:44:19 -04:00
Github Build Bot
9722af38bd Bumping version to 1.7.16 and generate changelog 2024-06-05 19:05:44 +00:00
github-actions[bot]
533e2a265e [Backport 1.7.latest] add --host flag to docs serve (#10232)
* add --host flag to docs serve (#10228)

(cherry picked from commit c2d4643f9d)

* fix test_serve unit test

---------

Co-authored-by: Michelle Ark <MichelleArk@users.noreply.github.com>
Co-authored-by: Michelle Ark <michelle.ark@dbtlabs.com>
Co-authored-by: Quigley Malcolm <QMalcolm@users.noreply.github.com>
2024-06-05 11:48:38 -07:00
Kshitij Aranke
befca7945f [Backport 1.7.latest] update dbt_utils version to 1.2.0 (#10262)
* [Backport 1.7.latest] update dbt_utils version to 1.2.0
2024-06-05 11:18:24 -07:00
FishtownBuildBot
2b6176704f [Automated] Merged prep-release/1.7.15_9197893826 into target 1.7.latest during release process 2024-05-22 17:03:50 -04:00
Github Build Bot
72b0f86fa6 Bumping version to 1.7.15 and generate changelog 2024-05-22 20:25:07 +00:00
github-actions[bot]
dab1aa73cc [Backport 1.7.latest] change port bind and add a unittest (#10212)
* change port bind and add a unittest (#10208)

(cherry picked from commit 0c08d7a19a)

* fix unit tests

* remove ssh-key since schemas repo is public

* fix compare schemas diff

---------

Co-authored-by: Michelle Ark <MichelleArk@users.noreply.github.com>
Co-authored-by: Michelle Ark <michelle.ark@dbtlabs.com>
2024-05-22 16:19:12 -04:00
Kshitij Aranke
6cff222293 [Backport 1.7.latest] [Tidy First] Remove wheel and twine from dev-requirements.txt (#10207) 2024-05-21 23:56:55 +01:00
Mike Alfare
879ae14e8d [Backport 1.7.latest] Fix the semicolon semantics for indexes while respecting other bug fix (#10194)
* backport postgres#97
* point to the correct reference for run_dbt

---------

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2024-05-21 18:25:27 -04:00
github-actions[bot]
d0ddeea257 [Backport 1.7.latest] Fix #9907: Add retry to tox to reduce flaky tests due to network failures (#10143)
(cherry picked from commit 751139d8c1)

Co-authored-by: Kshitij Aranke <kshitij.aranke@dbtlabs.com>
2024-05-21 21:03:16 +01:00
FishtownBuildBot
db4bb6c65c [Automated] Merged prep-release/1.7.14_8931458196 into target 1.7.latest during release process 2024-05-02 19:25:20 -04:00
Github Build Bot
19ad148bfe Bumping version to 1.7.14 and generate changelog 2024-05-02 22:46:55 +00:00
Michelle Ark
87ac4deb00 [Backport] deprecate materialization overrides from imported packages (#9998) 2024-04-29 09:59:43 -04:00
Emily Rockman
6a54a4c04b [Backport 1.7.latest] convert SECRET_ENV_PREFIX from DBT_ENV_SECRET_ to DBT_ENV_SECRET (#10052)
* convert SECRET_ENV_PREFIX from DBT_ENV_SECRET_ to DBT_ENV_SECRET

* changelog
2024-04-26 13:25:15 -05:00
Mike Alfare
0322a4b9b7 [Backport 1.7.latest] [Bug] Use a list instead of a set for index changes to perserve order (dbt-postgres#73) (#10044)
* backport dbt-postgres#73
2024-04-25 19:04:39 -04:00
github-actions[bot]
40a54ff2ec pin macos test runners to macos-12 (#10031) (#10036)
(cherry picked from commit bcbde3ac42)

Co-authored-by: Mike Alfare <13974384+mikealfare@users.noreply.github.com>
2024-04-25 08:45:12 -07:00
Matthew McKnight
ad0df69fb9 Materialized Views not updating cache (#9959)
* initial push of adding cache functions for materialized views

* update

* add cache_renamed

* remove current attempt of add cache_renamed

* add checks for add, and drop to see if relation exists in dispatch macro

* move calls from distpach macro into the default call/postgres specific versions incase we are not catching the dispatch as a layer of call or in drop case of it being a explicit return call

* split up relation into parts to build up to_relation

* move cache_dropped back to the dispatch original dispatch macro

* move cache_dropped back to the dispatch original dispatch macro

* readd removed space between macros

* move cache_added back to distpach macro

* remove extra curly brace

* revert get_relation call back get cache_renamed

* move of cache_renamed to get_rename_sql

* remove updated cache_changes and remove Relation.MaterializedView from renabmeable (takes us back to previous functionality)

* add changelog

* add simple test case, and doc string
2024-04-24 11:33:17 -07:00
Emily Rockman
7122b31326 Fix changlogs, remove PR reference (#9887) (#9996)
* stop referencing PRs

* convert PR to Issue in older yaml
# Conflicts:
#	.changes/1.8.0/Dependencies-20231005-151848.yaml
#	.changes/1.8.0/Dependencies-20231031-131954.yaml
#	.changes/1.8.0/Dependencies-20231106-130051.yaml
#	.changes/1.8.0/Dependencies-20231122-001840.yaml
#	.changes/1.8.0/Dependencies-20231204-000945.yaml
#	.changes/1.8.0/Dependencies-20231211-005651.yaml
#	.changes/1.8.0/Dependencies-20240115-012030.yaml
#	.changes/1.8.0/Dependencies-20240123-105843.yaml
#	.changes/1.8.0/Dependencies-20240129-005734.yaml
#	.changes/1.8.0/Dependencies-20240129-005743.yaml
#	.changes/1.8.0/Dependencies-20240212-011324.yaml
#	.changes/1.8.0/Dependencies-20240222-102947.yaml
#	.changes/1.8.0/Dependencies-20240226-004412.yaml
#	.changes/1.8.0/Dependencies-20240226-123502.yaml
#	.changes/1.8.0/Dependencies-20240227-151115.yaml
#	.changes/1.8.0/Security-20240222-152445.yaml
#	.changes/unreleased/Dependencies-20240117-100818.yaml
2024-04-22 14:17:58 -05:00
Emily Rockman
d70dc4e823 pin proto-types to match proto pin (#9999) (#10005)
# Conflicts:
#	dev-requirements.txt
2024-04-22 13:55:17 -05:00
FishtownBuildBot
aaa3ddae4a [Automated] Merged prep-release/1.7.13_8742019105 into target 1.7.latest during release process 2024-04-18 11:53:14 -06:00
Github Build Bot
6095b023f6 Bumping version to 1.7.13 and generate changelog 2024-04-18 17:15:19 +00:00
Emily Rockman
483a4e8deb [BACKPORT 1.7] bump sqlparse (#9965)
* bump sqlparse to 0.5 (#9951)

* bump sqlparse

* changelog
# Conflicts:
#	core/setup.py

* fix change kind (#9964)

* Update .changes/unreleased/Security-20240417-141316.yaml
2024-04-18 13:04:59 -04:00
FishtownBuildBot
f9cff92ba8 [Automated] Merged prep-release/1.7.12_8708637173 into target 1.7.latest during release process 2024-04-16 10:30:47 -06:00
Github Build Bot
ef37e629b1 Bumping version to 1.7.12 and generate changelog 2024-04-16 15:51:02 +00:00
github-actions[bot]
227877e1ae be less explicit (#9936) (#9937)
(cherry picked from commit 7e72cace2b)

Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
2024-04-15 12:26:48 -05:00
Emily Rockman
ffa1a384d0 update to wrk for all versions (#9916) (#9919)
# Conflicts:
#	.github/workflows/main.yml
2024-04-15 08:00:22 -05:00
Emily Rockman
2c24aa79f0 [1.7] Fix Workflow Deprecations (#9799)
* update to current node version

* remember codecov

* ensure uniquness in name

---------

Co-authored-by: Kshitij Aranke <kshitij.aranke@dbtlabs.com>
2024-04-12 09:40:47 -05:00
Chenyu Li
947f397f63 [BACKPORT 1.7] Exclude password-like fields for considering reparse (#9844) (#9857) 2024-04-08 15:56:11 -07:00
Quigley Malcolm
b8681a3c86 [Backport to 1.7.latest] Fix assorted source freshness edgecases so check is run or actionable information (#9825) (#9826)
* Fix assorted source freshness edgecases so check is run or actionable information (#9825)

* Ensure BaseRunner handles nodes without `build_path`

Some nodes, like SourceDefinition nodes, don't have a `build_path` property.
This is problematic because we take in nodes with no type checking, and
assume they have properties sometimes, like `build_path`. This was just
the case in BaseRunner's `_handle_generic_exception` and
`_handle_interal_exception` methods. Thus to stop dbt from crashing when
trying to handle an exception related to a node without a `build_path`,
we added an private method to the BaseRunner class for safely trying
to get `build_path`.

* Use keyword arguments when instantiating `Note` events in freshness.py

Previously we were passing arguments during the `Note` event instantiations
in freshness.py as positional arguments. This would cause not the desired
`Note` event to be emitted, but instead get the message
```
[Note] Don't use positional arguments when constructing logging events
```
which was our fault, not the users'. Additionally, we were passing the
level for the event in the `Note` instantiation when we needed to be
passing it to the `fire_event` method.

* Raise error when `loaded_at_field` is `None` and metadata check isn't possible

Previously if a source freshness check didn't have a `loaded_at_field` and
metadata source freshness wasn't supported by the adapter, then we'd log
a warning message and let the source freshness check continue. This was problematic
because the source freshness check couldn't actually continue and the process
would raise an error in the form
```
type object argument after ** must be a mapping, not NoneType
```
because the `freshness` variable was never getting set. This error wasn't particularly
helpful for any person running into it. So instead of letting that error
happen we now deliberately raise an error with helpful information.

* Add test which ensures bad source freshness checks raise appropriate error

This test directly tests that when a source freshness check doesn't have a
`loaded_at_field` and the adapter in use doesn't support metadata checks,
then the appropriate error message gets raised. That is, it directly tests
the change made in a162d53a8. This test indirectly tests the changes in both
7ec2f82a9 and 7b0ff3198 as the appropriate error can only be raised because
we've fixed other upstream issues via those commits.

* Add changelog entry for source freshness edgecase fixes

* Update imports of dbt.artifacts to pre-dbt.artifacts locations

Moving things to dbt.artifacts started to happen AFTER the release of
dbt-core 1.7, thus 1.7 has no concept of dbt.articacts. The changes brought
in via the previous commit, were cherry-picked from main (1.8.0 beta) and
thus reference dbt.artifacts. This caused everything to blow up, because
dbt.artifacts doesn't exist. Here we've updated the dbt.artifacts imports
to their pre-dbt.artifacts paths.
2024-03-29 09:09:16 -07:00
FishtownBuildBot
dd070b94b9 [Automated] Merged prep-release/1.7.11_8461692987 into target 1.7.latest during release process 2024-03-27 22:27:15 -05:00
Github Build Bot
bddff89a6e Bumping version to 1.7.11 and generate changelog 2024-03-28 02:48:42 +00:00
Mila Page
27e17a788a Add factory wrappers to renamed_relations (#9682)
* Add factory wrappers to renamed_relations
* add test and postgres semantics

---------

Co-authored-by: Mila Page <versusfacit@users.noreply.github.com>
Co-authored-by: Mike Alfare <13974384+mikealfare@users.noreply.github.com>
2024-03-21 15:10:57 -04:00
Gerda Shank
a0372fc078 Handle exceptions during node execution more elegantly. (#9585) (#9778)
* Handle exceptions during node execution more elegantly.

* Add changelog entry.

* Fix import

* Add task documentation.

* Change event type for noting thread exceptions.

Co-authored-by: Peter Webb <peter.webb@dbtlabs.com>
2024-03-20 16:06:14 -04:00
FishtownBuildBot
34a97c0267 [Automated] Merged prep-release/1.7.10_8273708859 into target 1.7.latest during release process 2024-03-13 18:31:24 -07:00
Github Build Bot
3b524cad91 Bumping version to 1.7.10 and generate changelog 2024-03-14 00:51:54 +00:00
Gerda Shank
5d0dd2a7aa Do not add duplicate input_measures when parsing metrics (#9677) (#9760) 2024-03-13 20:36:39 -04:00
Quigley Malcolm
88bb7a3537 [Backport 1.7.latest][MANUAL] Fix listing saved queries (#9748)
* Add tests to check that saved queries show in `dbt list`

* Update `list` task to support saved queries

This is built off of @jtcohen6 work in d6e7cda on jerco/fix-9532.
I didn't directly cherry pick because there was more work to do as
well as merge conflicts. That is to say @jtcohen6 should be credited
with some of the work.

* Update error message when iterating over nodes during list command errors

This was originally suggested by @jtcohen6 in d6e7cda of jerco/fix-9532.
This commit just makes sure the change gets included because I didn't
cherry-pick that commit into this work.

* Add changie log for saved query list support
2024-03-11 15:14:54 -07:00
github-actions[bot]
53d28eed1e [Backport 1.7.latest] Stop trying to parse deleted schema files (#9732)
* Stop trying to parse deleted schema files (#9722)

* Add test around deleting a YAML file containing semantic models and metrics

It was raised in https://github.com/dbt-labs/dbt-core/issues/8860 that an
error is being raised during partial parsing when files containing
metrics/semantic models are deleted. In further testing it looks like this
error specifically happens when a file containing both semantic models and
metrics is deleted. If the deleted file contains just semantic models or
metrics there seems to be no issue. The next commit should contain the fix.

* Skip deleted schema files when scheduling files during partial parsing

Waaaay back (in 7563b99) deleted schema files started being separated out
from deleted non-schema files. However ever since, when it came to scheduling
files for reparsing, we've only done so for deleted non-schema files. We even
missed this when we refactored the scheduling code in b37e5b5. This change
updates `_schedule_for_parsing` which is used by `schedule_nodes_for_parsing`
to begin skipping deleted schema files in addition to deleted non schema files.

* Update `add_to_pp_files` to ignore `deleted_schema_files`

As noted in the previous commit, we started separating out deleted
schema files from deleted non-schema files a looong time ago. However,
this whole time we've been adding `deleted_schema_files` to the list
of files to be parsed. This change corrects for that.

* Add changie doc for partial parsing KeyError fix

(cherry picked from commit deedeeb9ce)

* Empty commit to trigger github actions

---------

Co-authored-by: Quigley Malcolm <QMalcolm@users.noreply.github.com>
Co-authored-by: Quigley Malcolm <quigley.malcolm@dbtlabs.com>
2024-03-07 13:40:18 -05:00
github-actions[bot]
6919c501d3 [Backport 1.7.latest] Restrict protobuf to 4.* versions (#9707)
* Restrict protobuf to 4.* versions (#9630)

Protobuf v5 has breaking changes. Here we are limiting the protobuf
dependency to one major version, 4, so that we don't have to patch
over handling 2 different major versions of protobuf.

(cherry picked from commit e4fe839e45)

---------

Co-authored-by: Quigley Malcolm <QMalcolm@users.noreply.github.com>
Co-authored-by: Quigley Malcolm <quigley.malcolm@dbtlabs.com>
2024-02-29 14:21:34 -08:00
FishtownBuildBot
66c2a11746 [Automated] Merged prep-release/1.7.9_8087077147 into target 1.7.latest during release process 2024-02-28 13:47:27 -08:00
Github Build Bot
d01d33c28a Bumping version to 1.7.9 and generate changelog 2024-02-28 21:08:53 +00:00
Quigley Malcolm
1e064efcbf [Backport 1.7.latest] Restrict to protobuf 4 (#9674)
* Restrict protobuf to version 4.

* Restrict protobuf to major version 4.

---------

Co-authored-by: Peter Allen Webb <peter.webb@dbtlabs.com>
2024-02-26 16:14:29 -08:00
Kshitij Aranke
ffcd9e7f51 [Backport 1.7.latest] Make dbt-core compatible with Python 3.12 (#9673) 2024-02-26 17:29:11 -06:00
github-actions[bot]
0a6d0c158e Upgrade Jinja2 dependency version specification to address CVE-2024-22195 (#9638) (#9655)
CVE-2024-22195 identified an issue in Jinja2 versions <= 3.1.2. As such
we've gone and changed our dependency requirement specification to be
3.1.3 or greater (but less than 4).

Note: Preivously we were using the `~=` version specifier. However due
to some issues with the `~=` we've moved to using `>=` in combination
with `<`. This gives us the same range that `~=` gave us, but avoids
a pip resolution issue when multiple packages in an environment use `~=`
for the same dependency.

(cherry picked from commit 7ea4670832)

Co-authored-by: Quigley Malcolm <QMalcolm@users.noreply.github.com>
2024-02-26 14:13:45 -08:00
Kshitij Aranke
708e07ed53 [Backport 1.7.latest] Add target-path to retry (#9664) 2024-02-26 14:02:51 -06:00
Gerda Shank
8afbe10e24 [Backport 1.7.latest] Do not allow hook node_info to persist past hook run (#9645)
* Use log_contextvars context manager in run_hooks

* Changie

* Add test

* Fix unset_contextvars function
2024-02-26 11:25:31 -05:00
FishtownBuildBot
64b98b03c4 [Automated] Merged prep-release/1.7.8_7906944271 into target 1.7.latest during release process 2024-02-14 15:02:40 -06:00
Github Build Bot
c723cb1f7a Bumping version to 1.7.8 and generate changelog 2024-02-14 20:24:42 +00:00
Gerda Shank
6517c75e7e [Backport 1.7.latest] Store node_info in GenericExceptionOnRun logging event (#9568)
* Add node_info to GenericExceptionOnRun, InternalErrorOnRun &
SQLRunnerException

* Changie

* Formatting
2024-02-14 11:06:36 -05:00
Gerda Shank
f95cf55d33 [Backport 1.7.latest] Set query headers when manifest is passed in to dbtRunner (#9569) 2024-02-14 09:51:56 -05:00
github-actions[bot]
ad37fc9534 Preserve model constraints when versioning models (#9539) (#9541)
(cherry picked from commit 2b6e2e18df)

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
2024-02-12 10:17:18 -05:00
FishtownBuildBot
169972f480 [Automated] Merged prep-release/1.7.7_7745564565 into target 1.7.latest during release process 2024-02-01 12:38:17 -06:00
Github Build Bot
7b96509409 Bumping version to 1.7.7 and generate changelog 2024-02-01 18:00:42 +00:00
Michelle Ark
cfbba81938 TestGenerateCatalogWithExternalNodes (#9456) (#9483)
* TestGenerateCatalogWithExternalNodes (#9456)

(cherry picked from commit 06e55bb93e)

* Flip logic in `packages_for_node` to remove error case

By flipping the logic from `not in` to `in` we can drop the exception
and instead default to the model runtime config when the package isn't
found. We're still trying to grok if there will be any fallout from this.
The tests all pass, but that doesn't guarantee nothing bad will happen.

---------

Co-authored-by: Quigley Malcolm <quigley.malcolm@dbtlabs.com>
2024-01-31 13:32:50 -05:00
Michelle Ark
1391363d53 Fix source seed selection docs generate (#9454) (#9493)
(cherry picked from commit 719a50cc91)
2024-01-30 18:23:10 -05:00
github-actions[bot]
0d8e4af2f0 [Backport 1.7.latest] Pin pytest in dev-requirements.txt (#9475)
Co-authored-by: Kshitij Aranke <kshitij.aranke@dbtlabs.com>
2024-01-30 16:33:26 +00:00
FishtownBuildBot
e9ee761194 [Automated] Merged prep-release/1.7.6_7658416467 into target 1.7.latest during release process 2024-01-25 12:26:54 -06:00
Github Build Bot
5ec34cd4cc Bumping version to 1.7.6 and generate changelog 2024-01-25 17:48:30 +00:00
github-actions[bot]
bc494bbe80 [Backport 1.7.latest] fix retry as CLI (#9448)
(cherry picked from commit 50b85a0b01)

Co-authored-by: Chenyu Li <chenyu.li@dbtlabs.com>
2024-01-24 16:29:01 -08:00
github-actions[bot]
3f3dda48f7 Handle unknown type_code for model contracts (#8887) (#9409) 2024-01-19 15:08:10 -06:00
FishtownBuildBot
1f98991376 [Automated] Merged prep-release/1.7.5_7572664948 into target 1.7.latest during release process 2024-01-18 17:42:32 +01:00
Github Build Bot
5f5ddd27ac Bumping version to 1.7.5 and generate changelog 2024-01-18 16:04:29 +00:00
Chenyu Li
6e331833b0 Backport 9328 to 1.7.latest (#9391)
* Fix full-refresh and vars for retry (#9328)

Co-authored-by: Peter Allen Webb <peter.webb@dbtlabs.com>
(cherry picked from commit 1e4286a62d)

* pr feedback

* Update requires.py
2024-01-17 13:27:46 -08:00
FishtownBuildBot
d338b3e065 [Automated] Merged prep-release/1.7.4_7213813158 into target 1.7.latest during release process 2023-12-14 15:28:18 -05:00
Github Build Bot
9f0bcf5666 Bumping version to 1.7.4 and generate changelog 2023-12-14 19:50:11 +00:00
Quigley Malcolm
d65179b24e [Backport 1.7.latest] Update parser to support conversion metrics #9173 (#9255)
* Move minimum DSI version to 0.4.2

We're backporting a feature "conversion metrics" to 1.7. Conversion
metrics don't exist in DSI < 0.4.2 which is problematic if we allow
for those versions. This ensures that those who are on a version of
1.7 that supports conversion metrics will also have the requisit version
of DSI.

* added ConversionTypeParams classes

* updated parser for ConversionTypeParams

* added step to populate input_measure for conversion metrics

* added tests

* added changelog

* Regenerate v11 manifest jsonschema to include conversion metrics definition

* Regenerate v11 manifest test artifact for testing version compatability

---------

Co-authored-by: Will Deng <william.deng14@gmail.com>
2023-12-11 10:37:06 -08:00
Quigley Malcolm
0ef8638ab4 [Backport 1.7.latest] Fix ensuring we produce valid jsonschema artifacts for manifest, catalog, sources, and run-results (#9229)
* Drop `all_refs=True` from jsonschema-ization build process

Passing `all_refs=True` makes it so that Everything is a ref, even
the top level schema. In jsonschema land, this essentially makes the
produced artifact not a full schema, but a fractal object to be included
in a schema. Thus when `$id` is passed in, jsonschema tools blow up
because `$id` is for identifying a schema, which we explicitly weren't
creating. The alternative was to drop the inclusion of `$id`. Howver, we're
intending to create a schema, and having an `$id` is recommended best
practice. Additionally since we were intending to create a schema,
not a fractal, it seemed best to create to full schema.

* Explicity produce jsonschemas using DRAFT_2020_12 dialect

Previously were were implicitly using the `DRAFT_2020_12` dialect through
mashumaro. It felt wise to begin explicitly specifying this. First, it
is closest in available mashumaro provided dialects to what we produced
pre 1.7. Secondly, if mashumaro changes its default for whatever reason
(say a new dialect is added, and mashumaro moves to that), we don't want
to automatically inherit that.

* Begin including schema dialect specification in produced jsonschema

In jsonschema's documentation they state
> It's not always easy to tell which draft a JSON Schema is using.
> You can use the $schema keyword to declare which version of the JSON Schema specification the schema is written to.
> It's generally good practice to include it, though it is not required.

and

> For brevity, the $schema keyword isn't included in most of the examples in this book, but it should always be used in the real world.

Basically, to know how to parse a schema, it's important to include what
schema dialect is being used for the schema specification. The change in
this commit ensures we include that information.

* Add change documentation for jsonschema schema production fix

* Regenerate dbt jsonschemas with fixed mashumaro jsonschema production process

Specifically we regenerated
* catalog v1
* manifest v11
* run-results v5
* sources v3
using the command `scripts/collect-artifact-schema.py --path schemas`
2023-12-07 12:28:34 -08:00
FishtownBuildBot
fc2d16fd34 [Automated] Merged prep-release/1.7.3_7034454600 into target 1.7.latest during release process 2023-11-29 10:42:33 -05:00
Github Build Bot
de35686532 Bumping version to 1.7.3 and generate changelog 2023-11-29 15:04:49 +00:00
github-actions[bot]
cf3714d2c1 [Backport 1.7.latest] Fix #9119: Get sources working again in dbt docs generate (#9163)
Co-authored-by: Kshitij Aranke <kshitij.aranke@dbtlabs.com>
2023-11-29 14:45:40 +00:00
Jeremy Cohen
09f5bb3dcf Backport #9147 to 1.7.latest (#9156)
* Fixups for deps lock file (#9147)

* Update git revision with commit SHA

* Use PackageRenderer for lock file

* add unit tests for git and tarball packages

* deepcopy unrendered_packages_data before iteration, fix remaining tests

* Add functional tests

* Add changelog entries

* Assert one more

---------

Co-authored-by: Michelle Ark <michelle.ark@dbtlabs.com>

* Restore warning on unpinned git packages

---------

Co-authored-by: Michelle Ark <michelle.ark@dbtlabs.com>
2023-11-29 08:40:54 +01:00
FishtownBuildBot
f94bf2bba4 [Automated] Merged prep-release/1.7.2_6894976289 into target 1.7.latest during release process 2023-11-16 14:11:56 -05:00
Github Build Bot
ae75cc3a62 Bumping version to 1.7.2 and generate changelog 2023-11-16 18:34:17 +00:00
Emily Rockman
1e45a751a5 roll back agate pin (#9106) 2023-11-16 12:29:54 -06:00
github-actions[bot]
b5885da54e [Backport 1.7.latest] [IDE] also treat SystemExit exception as an interrupt. (#9042)
* During node execution, also treat SystemExit as an interrupt. (#8994)

IDE worker process raises SystemExit in multiple scenarios, including user abort of a command.

(cherry picked from commit 931b2dbe40)

* Add test asserting GraphRunnableTasks attempt to cancel connections on SystemExit (#9101)

* Add test asserting GraphRunnableTasks attempt to cancel connections on SystemExit

* Add test asserting GraphRunnableTasks attempt to cancel connections on KeyboardInterrupt

* Add test asserting GraphRunnableNode doesn't try to cancel connections on generic Exception

---------

Co-authored-by: Ben Mosher <ben.mosher@dbtlabs.com>
Co-authored-by: Quigley Malcolm <QMalcolm@users.noreply.github.com>
2023-11-16 08:02:08 -08:00
github-actions[bot]
73ebe53273 Support hierarchical config setting for SavedQueryExport configs (#9065) (#9074)
* Add test asserting `SavedQuery` configs can be set from `dbt_project.yml`

* Allow extraneous properties in Export configs

This brings the Export config object more in line with how other config
objects are specified in the unparsed definition. It allows for specifying
of extra configs, although they won't get propagate to the final config.

* Add `ExportConfig` options to `SavedQueryConfig` options

This allows for specifying `ExportConfig` options at the `SavedQueryConfig` level.
This also therefore allows these options to be specified in the dbt_project.yml
config. The plan in the follow up commit is to merge the `SavedQueryConfig` options
into all configs of `Exports` belonging to the saved query.

There are a couple caveots to call out:
1. We've used `schema` instead of `schema_name` on the `SavedQueryConfig` despite
it being called `schema_name` on the `ExportConfig`. This is because need `schema_name`
to be the name of the property on the `ExportConfig`, but `schema` is the user facing
specification.
2. We didn't add the `ExportConfig` `alias` property to the `SavedQueryConfig` This
is because `alias` will always be specific to a single export, and thus it doesn't
make sense to allow defining it on the `SavedQueryConfig` to then apply to all
`Exports` belonging to the `SavedQuery`

* Begin inheriting configs from saved query config, and transitively from project config

Export configs will now inherit from saved query configs, with a preference
for export config specifications. That is to say an export config will inherity
a config attr from the saved query config only if a value hasn't been supplied
on the export config directly. Additionally because the saved query config has
a similar relationship with the project config, exports configs can inherit
from the project config (again with a preference for export config specifications).

* Correct conditional in export config building for map schema to schema_name

I somehow wrote a really weird, but also valid, conditional statement. Previously
the conditional was
```
if combined.get("schema") is not combined.get("schema_name") is None:
```
which basically checked whether `schema` was a boolean that didn't match
the boolean of whether `schema_name` was None. This would pretty much
always evaluate to True because `schema` should be a string or none, not
a bool, and thus would never match the right hand side. Crazy. It has now
been fixed to do the thing we want to it to do. If `schema` isn't `None`,
and `schema_name` is `None`, then set `schema_name` to have the value of
`schema`.

* Update parameter names in `_get_export_config` to be more verbose

(cherry picked from commit c2f7d75e9e)

Co-authored-by: Quigley Malcolm <QMalcolm@users.noreply.github.com>
2023-11-15 15:34:36 -08:00
github-actions[bot]
2a9c3689a4 test pinning ddtrace (#9090) (#9092)
(cherry picked from commit 3902137dfc)

Co-authored-by: Michelle Ark <MichelleArk@users.noreply.github.com>
2023-11-15 16:49:14 -06:00
github-actions[bot]
67d8ce398f [Backport 1.7.latest] Fix dbt deps failing on tarballs (#9075) 2023-11-15 12:07:13 -08:00
FishtownBuildBot
7eb6cdbbfb [Automated] Merged prep-release/1.7.1_6789802369 into target 1.7.latest during release process 2023-11-07 15:52:03 -05:00
Github Build Bot
6ba3dc211c Bumping version to 1.7.1 and generate changelog 2023-11-07 20:15:09 +00:00
github-actions[bot]
ce86238389 Support new agate Integer data_type in adapter code (#9004) (#9027) 2023-11-07 14:56:48 -05:00
github-actions[bot]
7d60d6361e Fix lock for git subduer (#9019) (#9028)
* wip

* add tests

* changelog

* nits

* pr feedback

* nits

(cherry picked from commit 01d481bc8d)

Co-authored-by: Chenyu Li <chenyu.li@dbtlabs.com>
2023-11-07 14:30:50 -05:00
github-actions[bot]
bebd6ca0e1 Use MANIFEST.in to identify package data, allows recursive include (#9021) (#9026)
* changelog

* use MANIFEST.in to identify package data

(cherry picked from commit 839c720e91)

Co-authored-by: Mike Alfare <13974384+mikealfare@users.noreply.github.com>
2023-11-07 14:05:43 -05:00
Peter Webb
66cb5a0e7c Fix back compat for run_results pre-v5 (#9009) (#9017)
* Fix back compat for run_results pre-v5

* Add type annotations

* Add functional testing

* Add inline annotations

* Add changelog entry.

* Consolidate upgrade_schema_version + upgrade_run_results_json

* Restore accidentally reverted test cases

* Pre-commit fixups

---------

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
2023-11-06 19:26:53 -05:00
FishtownBuildBot
2401600e57 [Automated] Merged prep-release/1.7.0_6734243388 into target 1.7.latest during release process 2023-11-02 11:43:59 -04:00
Github Build Bot
a8fcf77831 Bumping version to 1.7.0 and generate changelog 2023-11-02 14:53:42 +00:00
Peter Webb
0995825793 Make relation filtering None-tolerant for maximal flexibility across adapters. (#8975) (#8978) 2023-11-01 18:14:23 -04:00
Quigley Malcolm
eeaaec4de9 DSI 0.4.0 and Saved Query Exports (#8950) (#8973) 2023-11-01 12:04:34 -07:00
github-actions[bot]
1cbab8079a ADAP-974: Fix issue where materialized views were not showing up in catalog queries (#8945) (#8963)
* changelog
* write test case demonstrating the issue
* update catalog query to reflect materialized views

(cherry picked from commit bb21403c9e)

Co-authored-by: Mike Alfare <13974384+mikealfare@users.noreply.github.com>
2023-11-01 14:29:53 -04:00
github-actions[bot]
333d793cf0 add a no-op runner for saved_query (#8937) (#8958)
(cherry picked from commit 211392c4a4)

Co-authored-by: Chenyu Li <chenyu.li@dbtlabs.com>
2023-11-01 09:06:07 -07:00
Peter Webb
b6f0eac2cd Backport Catalog Fix to 1.7.latest (#8953)
* Fix issues around new get_catalog_by_relations macro (#8856)

* Fix issues around new get_catalog_by_relations macro

* Add changelog entry

* Fix unit test.

* Additional unit testing

* Fix cased comparison in catalog-retrieval function (#8940)

* Fix cased comparison in catalog-retrieval function.

* Fix cased comparison in catalog-retrieval function.
2023-10-31 16:24:27 -04:00
github-actions[bot]
5e13698b2b [Backport 1.7.latest] Fix #8836: Add version to fqn when version==0 (#8922)
Co-authored-by: Kshitij Aranke <kshitij.aranke@dbtlabs.com>
2023-10-26 13:38:44 -05:00
github-actions[bot]
12c1cbbc64 Contract enforcement on temporary tables (#8889) (#8902)
* add test

* fix test

* first pass with constraint error

* add back column checks for temp tables

* changelog

* Update .changes/unreleased/Fixes-20231024-145504.yaml

(cherry picked from commit 98310b6612)

Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
2023-10-26 09:26:57 -05:00
github-actions[bot]
ced70d55c3 Fix partial parsing issue not working for changing semantic model name (#8865) (#8878)
* fix

* test

* changelog

(cherry picked from commit 35f46dac8c)

Co-authored-by: Chenyu Li <chenyu.li@dbtlabs.com>
2023-10-25 11:23:48 -07:00
FishtownBuildBot
1baebb423c [Automated] Merged prep-release/1.7.0rc1_6495873283 into target main during release process 2023-10-12 09:50:47 -04:00
Github Build Bot
462df8395e Bumping version to 1.7.0rc1 and generate changelog 2023-10-12 12:53:26 +00:00
Quigley Malcolm
35f214d9db Support dbt-semantic-interfaces 0.3.0 (#8820)
* changie doc for DSI 0.3.0 upgrade

* Gracefully handle v10 metric filters

* Fix iteration over metrics in `upgrade_v10_metric_filters`

* Update previous manifest version test fixtures to have more expressive metrics

* Regenerate the test v10 manifest artifact using the more expressive metrics from 904cc1ef

To do this I cherry-picked 904cc1ef onto my local 1.6.latest branch,
had the test regenerate the test v10 manifest artifact, and then over
wrote the test v10 manifest artifact on this branch (cherry-picking it
across the branches didn't work, had to copy paste :grimmace:)

* Regenerate test v11 manifest artifact using the fixture changes in 904cc1ef

* Update `upgrade_v10_metric_filters` to handled disabled metrics

Regenerating the v10 and v11 test manifest artifacts uncovered an
issue wherein we weren't handling disabled metrics that need to
get upgraded. This commit fixes that. Additionally, the
`upgrade_v10_metric_filters` was getting a bit unwieldy, so I broke
extracted the abstracted sub functions.

* Fix `test_backwards_compatible_versions` test

When we regenerated the v10 test manifest artifact, it started having
the `metricflow_time_sine` model, and it didn't previously. This caused
`test_backwards_compatible_versions` to start failing because it was
no longer identified as having modified state for v10. The test has
been altered accordingly
2023-10-11 16:46:26 -07:00
Quigley Malcolm
af0cbcb6a5 Add SavedQuery nodes (#8798)
* Bump to dbt-semantic-interfaces 0.3.0b1

* Update import path of `WhereFilterParser` from `dbt-semantic-interfaces`

In 0.3.x of `dbt-semantic-intefaces` the location of the WhereFilterParser
moved to be grouped in with a bunch of new adjacent code. As such,
we needed to correct our import path of it.

* Create basic `SavedQuery` node type based on `SavedQuery` protocol from DSI

* Add ability to add SavedQueries to the manifest

* Define unparsed SavedQuery node

* Begin parsing saved_query objects to manifest

* Skip jinja rendering of `SavedQuery.where` property

* Begin propagating `SavedQueries` on the manifest to the semantic manifest

* Add tests for basic saved query parsing

* Add custom pluralization handling of SavedQuery node type

* Add a config subclass to SavedQuery node

* Move the SavedQuery node to nodes.py

Unfortunately things are a bit too intertwined currently for SavedQuery
to be in it's own file. We need to add the SavedQuery node to the
GraphMemberNode, unfortunately with SavedQuery in it's own file,
importing it would have caused a circular dependency. We'll need
to separately come in and split things up as a cleanup portion of
work.

* Add basic plumbing of saved query configs to projects

* Add basic lookup utility for saved queries, SavedQueryLookup

* Handle disabled SavedQuery nodes in parsing and lookups

* Add SavedQuery nodes to grouping process

Our grouping logic seems to be in a weird spot. It seems liek we're
moving to setting the `group` for a node in the node's `config` however,
all of the logic around grouping is still focused on the top level `group`
property on a nodes. To get group stuff plumbed I've thus added `group`
as a top level property of the `SavedQuery` node, and populated it from
the config group value.

* Plumb through saved query in a lot more places

I don't like making scatter shot commits like this. However, a lot
of this commit was written ~4am, soooo yea. Things were broken, I wanted
things to be unbroken. I mostly searched for `semantic_models` and added
the equivalent necessary `saved_queries`. Some stuff is in support of
writing out the manifest, some stuff helps with node selection, it's a
lot of miscelaneous stuff that I don't fully understand.

* Add `depends_on` to `SavedQuery` nodes and populate from `metrics` property

* Add partial parsing support to SavedQuery nodes

* Add `docs` support for SavedQuery descriptions

* Support selctor methods for SavedQuery nodes

* Add `refs` property to SavedQuery node

We don't actually append anything to `refs` for SavedQuery nodes currently.
I'm not sure if anything needs to be appended to them. Regardless, we
access the `refs` property throughout the codebase while iterating over
nodes. It seems wise to support this attribute as to not accidently blow
something up with it not existing.

* Support `saved_queries` when upgrading from manifests <= v10 (and regenerate v11)

* Add changie doc for saved query node support

* Pin to dbt-semantic-interfaces 0.3.0b1 for saved query work

We're gonna release DSI 0.3.0, and if this PR automatically pulls that
in things will break. But the things that need fixing should be handled
separately from this PR. After releasing DSI 0.3.0 I'm going to create
a branch off/ontop of this one, and open a stacked PR with the associated
changes.

* Bump supported DSI version to 0.3.x

* Switch metric filters and saved query where to use ne WhereFilterIntersection

* Update schema yaml readers to create WhereFilterInterfaces

* Expand metric filters and saved query where property to handle both str and list of strs

* Update tests which were broken by where filter changes

* Regeneate v11 manifest

* Fixup: Update `SavedQueryLookup.perform_lookup` to operate on saved queries

I missed this when I was copy and pasting 🤦
2023-10-11 15:54:11 -07:00
Peter Webb
2e35426d11 Add support for getting freshness from DBMS metadata (#8795)
* Add support for getting freshness from DBMS metadata

* Add changelog entry

* Add simple test case

* Change parsing error to warning and add new event type for warning

* Code review simplification of capability dict.

* Revisions to the capability mechanism per review

* Move utility function.

* Reduce try/except scope

* Clean up imports.

* Simplify typing per review

* Unit test fix
2023-10-11 15:55:57 -04:00
Emily Rockman
bf10a29f06 update v10 manifest on main (#8834)
* update manifest

* add changelog
2023-10-11 14:52:01 -05:00
Gerda Shank
a7e2d9bc40 Partial parsing issue when adding groups and updating models at the same time (#8817) 2023-10-11 15:01:49 -04:00
Michelle Ark
a3777496b5 fix changelog (#8833) 2023-10-11 13:22:05 -04:00
dependabot[bot]
edf6aedc51 Bump docker/build-push-action from 4 to 5 (#8783)
* Bump docker/build-push-action from 4 to 5

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add automated changelog yaml from template for bot PR

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
2023-10-11 09:51:51 -07:00
Jeremy Cohen
53845d0277 Add warning_tag to UnversionedBreakingChange (#8828) 2023-10-11 17:54:16 +02:00
Mike Alfare
3d27483658 ADAP-850: Support test results as a view (#8653)
* add `store_failures_as` parameter to TestConfig, catch strategy parameter in test materialization
* create test results as views
* updated test expected values for new config option
* break up tests into reusable tests and adapter specific configuration, update test to check for relation type and confirm views update
* move test configuration into base test class
* allow `store_failures_as` to drive whether failures are stored
* update expected test config dicts to include the new default value for store_failures_as
* Add `store_failures_as` config for generic tests
* cover --store-failures on CLI gap
* add generic tests test case for store_failures_as
* update object names for generic test case tests for store_failures_as
* remove unique generic test, it was not testing `store_failures_as`
* pull generic run and assertion into base test class to turn tests into quasi-parameterized tests
* add ephemeral option for store_failures_as, as a way to easily turn off store_failures at the model level
* add compilation error for invalid setting of store_failures_as

---------

Co-authored-by: Doug Beatty <doug.beatty@dbtlabs.com>
2023-10-10 17:26:01 -04:00
Emily Rockman
4f9bd0cb38 Fix uncaught exception for group updates (#8792)
* add test

* write test

* fix test

* updating test

* add clean

* cleanup

* more tests, fix comment

* add new test, move fixtures
2023-10-10 15:37:47 -05:00
Kshitij Aranke
3f7f7de179 Fix #8682: Override path-like args in dbt retry (#8803) 2023-10-10 19:31:54 +01:00
Kshitij Aranke
6461f5aacf Fix #8022: Foreign key constraint on incremental model results in Database Error (#8768) 2023-10-10 18:32:51 +01:00
Doug Beatty
339957b42c Explanation of Parsing vs. Compilation vs. Runtime (#8744)
* Explanation of Parsing vs. Compilation vs. Runtime

* Update core/dbt/parser/parsing-vs-compilation-vs-runtime.md

* Update core/dbt/parser/parsing-vs-compilation-vs-runtime.md

* Update core/dbt/parser/parsing-vs-compilation-vs-runtime.md

* Update core/dbt/parser/parsing-vs-compilation-vs-runtime.md

* Update core/dbt/parser/parsing-vs-compilation-vs-runtime.md

* Update core/dbt/parser/parsing-vs-compilation-vs-runtime.md

* Apply suggestions from code review

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>

* Fix a couple markdown rendering issues

* Move to the "explain it like im 64" folder

When ELI5 just isnt detailed enough.

* Disambiguate Python references

Disambiguate Python references and delineate SQL models ("Jinja-SQL") from Python models ("dbt-py")

---------

Co-authored-by: Jeremy Cohen <jeremy@dbtlabs.com>
2023-10-10 10:51:54 -06:00
Gerda Shank
4391dc1a63 Type aliasing for model contract column data_type (#8592) 2023-10-10 11:43:26 -04:00
Michelle Ark
964e0e4e8a [Fix] respect project root when loading seeds (#8762) 2023-10-10 11:17:41 -04:00
Chenyu Li
549dbf3390 Deps lock by justbldwn (#8408)
*  adding installed_packages.json functionality

*  update test_simple_dependency_deps test

* 📝 adding changelog for deps feature via changie

*  restructure deps command, include lock/add

*  add new deps event types to sample_values

*  fix test_simple_dependency_deps test

* 🐛 attempting to fix cli commands

* 🐛 convert dbt deps to dbt deps install

also leave dbt deps as just a new click group

*  update test_command_mutually_exclusive_option

change deps command to deps install

*  update functional tests from deps > deps install

*  change missing deps to deps install

*  convert adapter tests to deps install from deps

* move back to deps and merge more with main

* fix-unittest

* add hash

* foramt yml and update command structure

* nits

* add new param

* nits

* nits

* nits

* fix_tests

* pr_feedback

* nits

* nits

* move_check

* Update Features-20230125-165933.yaml

---------

Co-authored-by: Justin Baldwin <91483530+justbldwn@users.noreply.github.com>
2023-10-09 21:05:00 -07:00
Quigley Malcolm
70b2e15a25 Add semantic model test to test_contracts_graph_parsed.py (#8654)
* Add semantic model test to `test_contracts_graph_parsed.py`

The tests in `test_contracts_graph_parsed.py` are meant to ensure
that we can go from objects to dictionaries and back without any
changes. We've had a desire to simplify these tests. Most tests in
this file have three to four fixtures, this test only has one. What
a test of this format ensures is that parsing a SemanticModel from
a dictionary doesn't add/drop any keys from the dictionary and that
when going back to the dictionary no keys are dropped. This style of
test will still break whenever the semantic model (or sub objects)
change. However now when that happens, only one fixture will have to
be updated (whereas previously we had to update 3-4 fixtures).

* Begin using hypothesis package for symmetry testing

Hypothesis is a python package for doing property testing. The `@given`
parameterizes a test, with it generating the arguements it has following
`strategies`. The main strategies we use is `builds` this takes in a callable
passes any sub strategies for named arguements, and will try to infer any
other arguments if the callable is typed. I found that even though the
test was run many many times, some of the `SemanticModel` properties
weren't being changed. For instance `dimensions`, `entities`, and `measures`
were always empty lists. Because of this I defined sub strategies for
some attributes of `SemanticModel`s.

* Update unittest readme to have details on test_contracts_graph_parsed methodology
2023-10-09 14:55:26 -07:00
Mark Scannell
bb249d612c Generate static index html documentation (#8615)
* Include option to generate static index.html

* Added changie

* Using DBT's system load / write file methods for better cross platform
support

* Updated docs tests with dbt.client.systems calls for file reading

* Writing out static_index.html as binary file to prevent line-ending
conversions on Windows. (similar behaviour as index.html)
2023-10-06 10:37:13 -07:00
Emily Rockman
17773bdb94 pin types-requests in dev-requirements (#8788)
* pin types-requests

* changelog
2023-10-06 09:53:08 -05:00
Gerda Shank
f30293359c Selectors in docs generate limits catalog generation (#8772) 2023-10-05 16:00:12 -04:00
Emily Rockman
0c85e6149f remove guild as codeowner (#8778) 2023-10-05 10:16:32 -05:00
Emily Rockman
ec57d7af94 add gha for dependabot checks (#8777) 2023-10-05 10:16:20 -05:00
Emily Rockman
df791f729c support doc blocks (#8771) 2023-10-05 08:12:28 -05:00
Emily Rockman
c6ff3abecd remove top level meta attribute (#8766) 2023-10-04 13:23:09 -05:00
Emily Rockman
eac13e3bd3 Add meta to SemanticModels (#8754)
* WIP

* changelog
2023-10-03 13:08:37 -05:00
Emily Rockman
46ee3f3d9c rebuild manifest missed fields (#8755)
* rebuild manifest missed fields

* changelogs
2023-10-02 09:38:50 -05:00
Peter Webb
5e1f0c5fbc Report Resource Usage Statistics When a dbt Command Finishes (#8671)
* Add performance metrics to the CommandCompleted event.

* Add changelog entry.

* Add flag for controling the log level of ResourceReport.

* Update changelog entry to reflect changes

* Remove outdated attributes

* Work around missing resource module on windows

* Fix corner case where flags are not set
2023-09-29 18:37:37 -04:00
Peter Webb
c4f09b160a Add new get_catalog_relations macro, Supporting Changes (#8648)
* Add new get_catalog_relations macro, allowing dbt to specify which relations in a schema the adapter should return data about

* Implement postgres adapter support for relation filtering on catalog queries

* Code review changes adding feature flag for catalog-by-relation-list support

* Use profile specified in --profile with dbt init (#7450)

* Use profile specified in --profile with dbt init

* Update .changes/unreleased/Fixes-20230424-161642.yaml

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>

* Refactor run() method into functions, replace exit() calls with exceptions

* Update help text for profile option

---------

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>

* add TestLargeEphemeralCompilation (#8376)

* Fix a couple of issues in the postgres implementation of get_catalog_relations

* Add relation count limit at which to fall back to batch retrieval

* Better feature detection mechanism for adapters.

* Code review changes to get_catalog_relations and adapter feature checking

* Add changelog entry

---------

Co-authored-by: ezraerb <ezraerb@alum.mit.edu>
Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>
Co-authored-by: Michelle Ark <MichelleArk@users.noreply.github.com>
2023-09-29 16:13:23 -04:00
Jeremy Cohen
48c97e86dd Fix tag: selection for projects with semantic models (#8750)
* Add unit test to repro regression

* Add defensive code for tag: selection

* Add changelog entry
2023-09-29 09:49:32 -07:00
Emily Rockman
416bc845ad fix duplication of docs issues (#8747)
* fix duplication of docs issues

* update conditional to only run on merged PRs
2023-09-29 09:40:34 -05:00
Michelle Ark
408a78985a Fix: avoid double-rendering sql_header in dbt show (#8740) 2023-09-28 19:35:14 +01:00
Emily Rockman
0c965c8115 update changelog kind (#8737) 2023-09-28 08:20:57 -07:00
Philippe Boyd
f65e4b6940 feat: resolve packages with same git repo and unique subdirectory (#8322) 2023-09-27 15:31:37 -04:00
Michelle Ark
a2d4424f92 Inline limit in SQL sent from dbt show (#8641) 2023-09-27 15:56:34 +01:00
Kshitij Aranke
997f839cd6 Disallow cleaning paths outside current working directory (#8469) 2023-09-27 15:47:00 +01:00
Emily Rockman
556fad50df Model contracts: raise warning for numeric types without specified scale (#8721)
* add warning when contracting fields don't have precision

* rename files

* changelog

* move tests out of adapter zone

* Update core/dbt/include/global_project/macros/adapters/columns.sql

Co-authored-by: colin-rogers-dbt <111200756+colin-rogers-dbt@users.noreply.github.com>

* Apply suggestions from code review

---------

Co-authored-by: colin-rogers-dbt <111200756+colin-rogers-dbt@users.noreply.github.com>
2023-09-27 08:00:12 -05:00
dave-connors-3
bb4214b5c2 Dc/8546 semantic models in graph selection (#8589) 2023-09-26 23:23:03 +01:00
Kshitij Aranke
f17c1f3fe7 Fix #6497: Support global flags passed in after subcommands (#8670) 2023-09-26 16:16:36 +01:00
Kshitij Aranke
d4fe9a8ad4 Fix #8509: Support doc blocks in nested semantic model YAML (#8709) 2023-09-26 16:02:21 +01:00
Emily Rockman
2910aa29e4 Automated Repo Cleanup (#8686)
* add workflow

* cleanup and rename workflow

* rename workflow in actions

* add more context
2023-09-26 09:35:29 -05:00
Renan Leme
89cc073ea8 CT-3144 Fix test edges type filter on Graph (#8696)
* CT-3144 Fix test edges filter

* CT-3144 Add changelog

* CT-3144 Remove duplicated line

* CT-3144 Remove duplicated line

* CT-3144 Rename vars

* CT-3144 Update filter to use get_edge_data

* Trigger cla
2023-09-26 09:55:45 -04:00
Quigley Malcolm
aa86fdfe71 Add date spine macros to core (#8616)
* Add `date_spine` macro (and macros it depends on) from dbt-utils to core

The macros added are
- date_spine
- get_intervals_between
- generate_series
- get_powers_of_two

We're adding these to core because they are becoming more prevalently used
with the increase usage in the semantic layer. Basically if you are
using the semantic layer currently, then it is almost a requirement
to use dbt-utils, which is undesireable given the SL is supported directly
in core. The primary focus of this was to just add `date_spine`. However,
because `date_spine` depends on other macros, these other macros were
also moved.

* Add adapter tests for `get_powers_of_two` macro

* Add adapter tests for `generate_series` macro

* Add adapter tests for `get_intervals_between` macro

* Add adapter tests for `date_spine` macro

* Improve test fixture for `date_spine` macro to work with multiple adapters

* Cast to types to date in fixture_date_spine when targeting redshift

* Improve test fixture for `get_intervals_between` macro to work with multiple adapters

* changie doc for adding date_spine macro
2023-09-25 12:20:05 -07:00
Quigley Malcolm
48e9ced781 Support null coalescing properties for metric nodes (#8700)
* Include 'join_to_timespine` and `fill_nulls_with` in metric fixture

* Support `join_to_timespine` and `fill_nulls_with` properties on measure inputs to metrics

* Assert new `fill_nulls_with` and `join_to_timespine` properties don't break associated DSI protocol

* Add doc for metric null coalescing improvements

* Fix unit test for unparsed metric objects

The `assert_symmetric` function asserts that dictionaries are mostly
equivalent. I say mostly equivalent because it drops keys that are
`None`. The issue is that that `join_to_timespine` gets defaulted
to `False`, so we have to specify it in the `get_ok_dict` so that
they match.
2023-09-25 11:02:47 -07:00
Doug Beatty
7b02bd1f02 Lower bound of 8.0.2 for click (#8684)
* Lower bound of `8.0.2` for `click`

* Changelog entry
2023-09-24 13:46:54 -06:00
Emily Rockman
417fc2a735 Support quoted parameter list for MultiOption cli options (#8665)
* allow multioption to be quoted

* changelog

* fix test

* remove list format

* fix tests

* fix list object

* review arg change

* fix quotes

* Update .changes/unreleased/Features-20230918-150855.yaml

* add types

* convert list to set in test

* make mypy happy

* mroe mypy happiness

* more mypy happiness

* last mypy change

* add node to test
2023-09-22 14:34:36 -05:00
Quigley Malcolm
317128f790 Update pull request template to include type annotations as part of checklist (#8687)
* Update pull request template to include type annotations as part of checklist

* Add to checklist item a link to python typing information
2023-09-22 11:30:28 -07:00
Emily Rockman
e3dfb09b10 Support labels for semantic_models, dimensions, measures and entities (#8646)
* first pass

* changelog

* changelog

* Delete .changes/unreleased/Features-20230913-155802.yaml

* Update .changes/unreleased/Features-20230914-074429.yaml
2023-09-22 10:56:57 -05:00
colin-rogers-dbt
d912654110 Allow adapters to include python package logging in dbt logs (#8643)
* add set_package_log_level functionality

* set package handler

* set package handler

* add logging about stting up logging

* test event log handler

* add event log handler

* add event log level

* rename package and add unit tests

* revert logfile config change

* cleanup and add code comments

* add changie

* swap function for dict

* add additional unit tests

* fix unit test
2023-09-20 09:27:30 -07:00
Peter Webb
34ab4cf9be More Type Annotations (#8536)
* Extend use of type annotations in the events module.

* Add return type of None to more __init__ definitions.

* Still more type annotations adding -> None to __init__

* Tweak per review
2023-09-20 11:35:22 -04:00
Michelle Ark
d597b80486 add TestLargeEphemeralCompilation (#8376) 2023-09-18 15:00:10 +01:00
ezraerb
3f5ebe81b9 Use profile specified in --profile with dbt init (#7450)
* Use profile specified in --profile with dbt init

* Update .changes/unreleased/Fixes-20230424-161642.yaml

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>

* Refactor run() method into functions, replace exit() calls with exceptions

* Update help text for profile option

---------

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>
2023-09-15 10:14:53 -05:00
Kshitij Aranke
f52bd9287b Fix #8160: Warn when --state == --target (#8638) 2023-09-14 17:04:32 +01:00
Gerda Shank
f5baeeea1c Allow setting access in config in addition to properties (#8635) 2023-09-14 11:09:41 -04:00
Mike Alfare
3cc7044fb3 Loosen type on replaceable_relation and renameable_relation and provide guidance in docstrings (#8647)
* loosen type on replaceable_relation and renameable_relation and provide guidance in docstrings
2023-09-13 20:17:33 -04:00
Gerda Shank
26c7675c28 Fix test_numeric_values of the show test (#8644) 2023-09-13 15:38:18 -04:00
colin-rogers-dbt
8aaed0e29f replace is_replaceable function with can_be_replaced call (#8637) 2023-09-13 10:19:09 -04:00
Emily Rockman
5182e3c40c split up test class (#8610) 2023-09-12 10:59:56 -05:00
Kshitij Aranke
1e252c7664 move codecov failure threshold to 0.1% (#8625) 2023-09-12 16:45:36 +01:00
Kshitij Aranke
05ef3b6e44 Audit potential circular dependencies (#8489) 2023-09-12 14:00:16 +01:00
leahwicz
ad04012b63 Revert "Merge branch 'main' into main" (#8622)
This reverts commit c93cba4603, reversing
changes made to 971669016f.
2023-09-11 22:18:12 -04:00
leahwicz
c93cba4603 Merge branch 'main' into main 2023-09-11 21:40:10 -04:00
Mike Alfare
971669016f ADAP-869: Support atomic replace in replace macro (#8539)
* move config changes into alter.sql in alignment with other adapters
* move shared relations macros to relations root
* move single models files to models root
* add table to replace
* move create file into relation directory
* implement replace for postgres
* move column specific macros into column directory
* add unit test for can_be_replaced
* update renameable_relations and replaceable_relations to frozensets to set defaults
* fixed tests for new defaults
2023-09-11 16:23:25 -04:00
Emily Rockman
6c6f245914 update PR template (#8613) 2023-09-11 13:48:25 -05:00
Quigley Malcolm
b39eeb328c Unskip and rename test_expression_metric (#8578)
* Add docstrings to `contracts/graph/metrics.py` functions to document what they do

Used [dbt-labs/dbt-core#5607](https://github.com/dbt-labs/dbt-core/pull/5607)
for context on what the functions should do.

* Add typing to `reverse_dag_parsing` and update function to work on 1.6+ metrics

* Add typing to `parent_metrics` and `parent_metrics_names`

* Add typing to `base_metric_dependency` and `derived_metric_dependency` and update functions to work on 1.6+ metrics

* Simplify implementations of `basic_metric_dependency` and `derived_metric_dependnecy`

* Add typing to `ResolvedMetricReference` initialization

* Add typing to `derived_metric_dependency_graph`

* Simplify conditional controls in `ResolvedMetricReference` functions

The functions in `ResolvedMetricReference` use `manifest.metric.get(...)`
which will only return either a `Metric` or `None`, never a different
node type. Thus we don't need to check that the returned metric is
a metric.

* Don't recurse on over `depends_on` for non-derived metrics in `reverse_dag_parsing`

The function `reverse_dag_parsing` only cares about derived metrics,
that is metrics that depend on other metrics. Metrics only depend on
other metrics if they are one of the `DERIVED_METRICS` types. Thus
doing a recursive call to `reverse_dag_parsing` for non `DERIVED_METRICS`
types is unnecessary. Previously we were iterating over a metric's
`depends_on` property regardless of whether the metric was a `DERIVED_METRICS`
type. Now we only do this work if the metric is of a `DERIVED_METRICS`
type.

* Simplify `parent_metrics_names` by having it call `parent_metrics`

* Unskip `TestMetricHelperFunctions.test_derived_metric` and update fixture setup

* Add changie doc for metric helper function updates

* Get manifest in `test_derived_metric` from the parse dbt_run invocation

* Remove `Relation` a intiatlization attribute for `ResolvedMetricReference`

* Add return typing to class `__` functions of `ResolvedMetricReference`

* Move from `manifest.metrics.get` to `manifest.expect` in metric helpers

Previously with `manifest.metrics.get` we were just skipping when `None`
was returned. Getting `None` back was expected in that `parent_unique_id`s
that didn't belong to metrics should return `None` when calling
`manifest.metrics.get`, and these are fine to skip. However, there's
an edgecase where a `parent_unique_id` is supposed to be a metric, but
isn't found, thus returning `None`. How likely this edge case could
get hit, I'm not sure, but it's a possible edge case. Using `manifest.metrics.get`
it we can't actually tell if we're in the edge case or not. By moving
to `manifest.expect` we get the error handling built in, and the only
trade off is that we need to change our conditional to skip returned
nodes that aren't metrics.
2023-09-07 15:28:36 -07:00
Emily Rockman
be94bf1f3c Preserve decimal places for dbt show (#8561)
* update `Number` class to handle integer values (#8306)

* add show test for json data

* oh changie my changie

* revert unecessary cahnge to fixture

* keep decimal class for precision methods, but return __int__ value

* jerco updates

* update integer type

* update other tests

* Update .changes/unreleased/Fixes-20230803-093502.yaml

---------

Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>

* account for integer vs number on table merges

* add tests for combining number with integer.

* add unit test when nulls are added

* cant none as an Integer

* fix null tests

---------

Co-authored-by: dave-connors-3 <73915542+dave-connors-3@users.noreply.github.com>
Co-authored-by: Dave Connors <dave.connors@fishtownanalytics.com>
2023-09-07 09:15:11 -05:00
Ben Mosher
e24a952e98 compile --no-inject-ephemeral-ctes flag (#8482) 2023-09-07 10:13:40 -04:00
Michelle Ark
89f20d12cf make UnparsedVersion.__lt__ order-agnostic (#8559) 2023-09-06 17:46:59 -04:00
Michelle Ark
ebeb0f1154 test advanced ref override (#8552) 2023-09-06 17:45:16 -04:00
Kshitij Aranke
d66fe214d9 Fix #8544: Parse the correct schema version from manifest (#8551)
* Fix #8544: Parse the correct schema version from manifest

* add changie

* add comment
2023-09-06 20:38:37 +01:00
Michelle Ark
75781503b8 add typing to partially typed methods in runnable.py (#8569) 2023-09-06 13:57:55 -04:00
Kshitij Aranke
9aff3ca274 Fix #8398: Add typing to __init__ in base.py (#8568) 2023-09-06 18:13:05 +01:00
Emily Rockman
7e2a08f3a5 remove label (#8557) 2023-09-05 19:12:16 -05:00
Emily Rockman
a0e13561b1 Support dbt-cloud config dict in dbt_project.yml (#8527)
* first pass at adding dbt-cloud config

* changelog

* fix test, add direct validation
2023-09-05 09:48:37 -05:00
FishtownBuildBot
7eedfcd274 [Automated] Merged prep-release/1.7.0b2_6049623160 into target main during release process 2023-09-01 08:48:37 -05:00
Github Build Bot
da779ac77c Bumping version to 1.7.0b2 and generate changelog 2023-09-01 12:51:37 +00:00
Mike Alfare
adfa3226e3 ADAP-814: Add support for replacing materialized views with tables/views and vice versa (#8449)
* first draft of adding in table - materialized view swap
* table/view/materialized view can all replace each other
* update renameable relations to a config
* migrate relations macros from `macros/adapters/relations` to `macros/relations` so that generics are close to the relation specific macros that they reference; also aligns with adapter macro files structure, to look more familiar
* move drop macro to drop macro file
* align the behavior of get_drop_sql and drop_relation, adopt existing default from drop_relation
* add explicit ddl for drop statements instead of inheriting the default from dbt-core
* update replace macro dependent macros to align with naming standards
* update type for mashumaro, update related test
2023-08-31 20:41:45 -04:00
Quigley Malcolm
e5e1a272ff Fix untyped functions in core/dbt/context/base.py (#8525)
* Improve typing of `ContextMember` functions

* Improve typing of `Var` functions

* Improve typing of `ContextMeta.__new__`

* Improve typing `BaseContext` and functions

In addition to just adding parameter typing and return typing to
`BaseContext` functions. We also declared `_context_members_` and
`_context_attrs_` as properites of `BaseContext` this was necessary
because they're being accessed in the classes functions. However,
because they were being indirectly instantiated by the metaclass
`ContextMeta`, the properties weren't actually known to exist. By
adding declaring the properties on the `BaseContext`, we let mypy
know they exist.

* Remove bare `invocations` of `@contextmember` and `@contextproperty`, and add typing to them

Previously `contextmember` and `contextproperty` were 2-in-1 decorators.
This meant they could be invoked either as `@contextmember` or
`@contextmember('some_string')`. This was fine until we wanted to return
typing to the functions. In the instance where the bare decorator was used
(i.e. no `(...)` were present) an object was expected to be returned. However
in the instance where parameters were passed on the invocation, a callable
was expected to be returned. Putting a union of both in the return type
made the invocations complain about each others' return type. To get around this
we've dropped the bare invocation as acceptable. The parenthesis are now always
required, but passing a string in them is optional.
2023-08-31 13:34:57 -07:00
Gerda Shank
d8e8a78368 Fix snapshot success message to display "INSERT 0 1" (for example) instead of success (#8524) 2023-08-31 13:09:15 -04:00
Emily Rockman
7ae3de1fa0 Semantic model configs - enable/disable + groups (#8502)
* WIP

* WIP

* get group and enabled added

* changelog

* cleanup

* getting measure lookup working

* missed file

* get project level working

* fix last test

* add groups to config tests

* more group tests

* fix path

* clean up manifest.py

* update error message

* fix test assert

* remove extra check

* resolve conflicts in manaifest

* update manifest

* resolve conflict

* add alias
2023-08-30 20:18:22 -05:00
Gerda Shank
72898c7211 Add return value to AdapterContainer.__init__ and AdapterMeta.__new__ (#8523) 2023-08-30 17:37:32 -04:00
Peter Webb
fc1a14a0e3 Include Compiled Node Attributes in run_results.json (#8492)
* Add compiled node properties to run_results.json

* Include compiled-node attributes in run_results.json

* Fix typo

* Bump schema version of run_results

* Fix test assertions

* Update expected run_results to reflect new attributes

* Code review changes

* Fix mypy warnings for ManifestLoader.load() (#8443)

* revert python version for docker images (#8445)

* revert python version for docker images

* add comment to not update python version, update changelog

* Bumping version to 1.7.0b1 and generate changelog

* [CT-3013]  Fix parsing of `window_groupings` (#8454)

* Update semantic model parsing tests to check measure non_additive_dimension spec

* Make `window_groupings` default to empty list if not specified on `non_additive_dimension`

* Add changie doc for `window_groupings`  parsing fix

* update `Number` class to handle integer values (#8306)

* add show test for json data

* oh changie my changie

* revert unecessary cahnge to fixture

* keep decimal class for precision methods, but return __int__ value

* jerco updates

* update integer type

* update other tests

* Update .changes/unreleased/Fixes-20230803-093502.yaml

---------

Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>

* Improve docker image README (#8212)

* Improve docker image README

- Fix unnecessary/missing newline escapes
- Remove double whitespace between parameters
- 2-space indent for extra lines in image build commands

* Add changelog entry for #8212

* ADAP-814: Refactor prep for MV updates (#8459)

* apply reformatting changes only for #8449
* add logging back to get_create_materialized_view_as_sql
* changie

* swap trigger (#8463)

* update the implementation template (#8466)

* update the implementation template

* add colon

* Split tests into classes (#8474)

* add flaky decorator

* split up tests into classes

* revert update agate for int (#8478)

* updated typing and methods to meet mypy standards (#8485)

* Convert error to conditional warning for unversioned contracted model, fix msg format (#8451)

* first pass, tests need updates

* update proto defn

* fixing tests

* more test fixes

* finish fixing test file

* reformat the message

* formatting messages

* changelog

* add event to unit test

* feedback on message structure

* WIP

* fix up event to take in all fields

* fix test

* Fix ambiguous reference error for duplicate model names across packages with tests (#8488)

* Safely remove external nodes from manifest (#8495)

* [CT-2840] Improved semantic layer protocol satisfaction tests (#8456)

* Test `SemanticModel` satisfies protocol when none of it's `Optionals` are specified

* Add tests ensuring SourceFileMetadata and FileSlice satisfiy DSI protocols

* Add test asserting Defaults obj satisfies protocol

* Add test asserting SemanticModel with optionals specified satisfies protocol

* Split dimension protocol satisfaction tests into with and without optionals

* Simplify DSI Protocol import strategy in protocol satisfaction tests

* Add test asserting DimensionValidtyParams satisfies protocol

* Add test asserting DimensionTypeParams satisfies protocol

* Split entity protocol satisfaction tests into with and without optionals

* Split measure protocol satisfication tests and add measure aggregation params satisficaition test

* Split metric protocol satisfaction test into optional specified an unspecified

Additionally, create where_filter pytest fixture

* Improve protocol satisfaction tests for MetricTypeParams and sub protocols

Specifically we added/improved protocol satisfaction tests for
- MetricTypeParams
- MetricInput
- MetricInputMeasure
- MetricTimeWindow

* Convert to using mashumaro jsonschema with acceptable performance (#8437)

* Regenerate run_results schema after merging in changes from main.

---------

Co-authored-by: Gerda Shank <gerda@dbtlabs.com>
Co-authored-by: Matthew McKnight <91097623+McKnight-42@users.noreply.github.com>
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
Co-authored-by: Quigley Malcolm <QMalcolm@users.noreply.github.com>
Co-authored-by: dave-connors-3 <73915542+dave-connors-3@users.noreply.github.com>
Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
Co-authored-by: Jaime Martínez Rincón <jaime@jamezrin.name>
Co-authored-by: Mike Alfare <13974384+mikealfare@users.noreply.github.com>
Co-authored-by: Michelle Ark <MichelleArk@users.noreply.github.com>
2023-08-30 17:28:49 -04:00
Gerda Shank
f063e4e01c Convert to using mashumaro jsonschema with acceptable performance (#8437) 2023-08-30 14:06:59 -04:00
Quigley Malcolm
07372db906 [CT-2840] Improved semantic layer protocol satisfaction tests (#8456)
* Test `SemanticModel` satisfies protocol when none of it's `Optionals` are specified

* Add tests ensuring SourceFileMetadata and FileSlice satisfiy DSI protocols

* Add test asserting Defaults obj satisfies protocol

* Add test asserting SemanticModel with optionals specified satisfies protocol

* Split dimension protocol satisfaction tests into with and without optionals

* Simplify DSI Protocol import strategy in protocol satisfaction tests

* Add test asserting DimensionValidtyParams satisfies protocol

* Add test asserting DimensionTypeParams satisfies protocol

* Split entity protocol satisfaction tests into with and without optionals

* Split measure protocol satisfication tests and add measure aggregation params satisficaition test

* Split metric protocol satisfaction test into optional specified an unspecified

Additionally, create where_filter pytest fixture

* Improve protocol satisfaction tests for MetricTypeParams and sub protocols

Specifically we added/improved protocol satisfaction tests for
- MetricTypeParams
- MetricInput
- MetricInputMeasure
- MetricTimeWindow
2023-08-29 09:40:30 -07:00
Michelle Ark
48d04e8141 Safely remove external nodes from manifest (#8495) 2023-08-28 14:10:34 -04:00
Michelle Ark
6234267242 Fix ambiguous reference error for duplicate model names across packages with tests (#8488) 2023-08-25 09:15:07 -04:00
Emily Rockman
1afbb87e99 Convert error to conditional warning for unversioned contracted model, fix msg format (#8451)
* first pass, tests need updates

* update proto defn

* fixing tests

* more test fixes

* finish fixing test file

* reformat the message

* formatting messages

* changelog

* add event to unit test

* feedback on message structure

* WIP

* fix up event to take in all fields

* fix test
2023-08-24 19:29:31 -05:00
Mike Alfare
d18a74ddb7 updated typing and methods to meet mypy standards (#8485) 2023-08-24 16:25:42 -04:00
Michelle Ark
4d3c6d9c7c revert update agate for int (#8478) 2023-08-23 14:06:52 -04:00
Emily Rockman
10f9724827 Split tests into classes (#8474)
* add flaky decorator

* split up tests into classes
2023-08-23 11:27:34 -05:00
Emily Rockman
582faa129e update the implementation template (#8466)
* update the implementation template

* add colon
2023-08-22 13:58:25 -05:00
Emily Rockman
4ec87a01e0 swap trigger (#8463) 2023-08-21 15:48:37 -05:00
Mike Alfare
ff98685dd6 ADAP-814: Refactor prep for MV updates (#8459)
* apply reformatting changes only for #8449
* add logging back to get_create_materialized_view_as_sql
* changie
2023-08-21 14:44:47 -04:00
Jaime Martínez Rincón
424f3d218a Improve docker image README (#8212)
* Improve docker image README

- Fix unnecessary/missing newline escapes
- Remove double whitespace between parameters
- 2-space indent for extra lines in image build commands

* Add changelog entry for #8212
2023-08-18 14:14:59 -05:00
dave-connors-3
661623f9f7 update Number class to handle integer values (#8306)
* add show test for json data

* oh changie my changie

* revert unecessary cahnge to fixture

* keep decimal class for precision methods, but return __int__ value

* jerco updates

* update integer type

* update other tests

* Update .changes/unreleased/Fixes-20230803-093502.yaml

---------

Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
2023-08-18 14:01:08 -05:00
Quigley Malcolm
49397b4d7b [CT-3013] Fix parsing of window_groupings (#8454)
* Update semantic model parsing tests to check measure non_additive_dimension spec

* Make `window_groupings` default to empty list if not specified on `non_additive_dimension`

* Add changie doc for `window_groupings`  parsing fix
2023-08-18 10:57:29 -07:00
FishtownBuildBot
0553fd817c [Automated] Merged prep-release/1.7.0b1_5895067219 into target main during release process 2023-08-17 15:04:21 -05:00
Github Build Bot
7ad971f720 Bumping version to 1.7.0b1 and generate changelog 2023-08-17 19:25:44 +00:00
Matthew McKnight
f485c13035 revert python version for docker images (#8445)
* revert python version for docker images

* add comment to not update python version, update changelog
2023-08-17 14:18:28 -05:00
Gerda Shank
c30b691164 Fix mypy warnings for ManifestLoader.load() (#8443) 2023-08-17 14:34:22 -04:00
Quigley Malcolm
d088d4493e Add doc string context to Identifier validion regex rule (#8440) 2023-08-17 10:11:03 -07:00
Emily Rockman
770f804325 Fix test failures (#8432)
* fail job when anything fails in previous matrix

* tweak wording

* PR feedback
2023-08-17 07:20:07 -05:00
Emily Rockman
37a29073de change trigger (#8418) 2023-08-16 19:49:59 -05:00
Peter Webb
17cd145f09 Temporarily disable test. (#8434) 2023-08-16 16:42:48 -04:00
Kshitij Aranke
ac539fd5cf Ignore .github and .changes directories for code coverage (#8424)
Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
2023-08-16 16:19:12 +01:00
Gerda Shank
048553ddc3 Fix using project-dir with list command and path selector (#8388) 2023-08-16 10:39:22 -04:00
Gerda Shank
dfe6b71fd9 Add return values to functions to fix mypy warnings (#8416) 2023-08-16 10:37:46 -04:00
Peter Webb
18ee93ca3a Fix run_results.json Performance Regression (#8413)
* Remedy performance regression by only writing run_results.json once.

* Write results before cleaning up connections.
2023-08-15 16:41:14 -04:00
Emily Rockman
cb4bc2d6e9 Automate opening docs issues (#8373)
* first pass

* WIP

* update issue body

* fix triggering label

* fix docs

* add better run name

* reduce complexity

* update description

* fix PR title

* point at workflow on main

* fix wording

* add label
2023-08-15 14:20:46 -05:00
Quigley Malcolm
b0451806ef [CT-2526] Add ability to automatically create metrics from semantic model measures (#8310)
* Update semantic model parsing test to check `create_metric = true` functionality

* Add `create_metric` boolean property to unparsed measure objects

* Begin creating metrics from measures with `create_metric = True`

* Add test ensuring partial parsing handles metrics generated from measures

* Ensure partial parsing appropriately deletes metrics generated from semantic models

* Add changie doc for  addition

* Separate generated metrics from parsed metrics for partial parsing

I was doing a demo earlier today of this branch (minus this commit)
and noticed something odd. When I changes a semantic model, metrics
that should have been technically uneffected would get dropped. Basically
if I made a change to a semantic model which had metrics in the same
file, and then ran parse, those metrics defined in the same file
would get dropped. Then with no other changes, if I ran parse again
they would come back. What was happening was that parsed metrics
and generated metrics were getting tracked the same way on the file
objects for partial parsing. In 0787a7c7b6
we began dropping all metrics tracked in a file objects when changes
to semantic models were detected. Since parsed metrics and generated
metrics were being tracked together on the file object, the parsed
metrics were getting dropped as well. In this commit we begin separating
out the tracking of generated metrics and parsed metrics on the
file object, and now only drop the generated metrics when semantic
models have a detected change.

* Assert in test that  semantic model partial parsing doesn't clobber regular metrics
2023-08-14 12:42:11 -07:00
Kshitij Aranke
b514e4c249 Fix #8350: add connection status into list of statuses for dbt debug (#8351) 2023-08-14 18:13:10 +01:00
Michelle Ark
8350dfead3 add --no-partial-parse-file-diff flag (#8361)
* add --no-partial-parse-file-diff flag

* changelog entry
2023-08-14 13:24:16 +02:00
Michelle Ark
34e6edbb13 Fix: deleting models that depend on external nodes (#8330) 2023-08-14 10:31:58 +02:00
FishtownBuildBot
27be92903e Add new index.html and changelog yaml files from dbt-docs (#8346) 2023-08-14 13:12:37 +08:00
Michelle Ark
9388030182 fix ModelNodeArgs.fqn (#8364) 2023-08-11 23:21:44 -04:00
Emily Rockman
b7aee3f5a4 add env vars to tox.ini (#8365)
* add env vars to tox.ini

* revert test
2023-08-11 10:49:41 -05:00
Michelle Ark
83ff38ab24 track plugin.get_nodes (#8336) 2023-08-10 11:33:11 -04:00
Michelle Ark
6603a44151 Detect changes to model access, deprecation_date, and latest_version in state:modified (#8264) 2023-08-10 11:29:02 -04:00
Kshitij Aranke
e69d4e7f14 Fix #8245: Add flag to codecov report (#8341) 2023-08-09 18:14:52 +01:00
d-kaneshiro
506f65e880 fixed comment util.py (#8222)
* fixed comments util.py

* add CHANGELOG entries
2023-08-09 09:24:38 -04:00
Gerda Shank
41bb52762b Bump manifest jsonschema to v11, update v10 schema (#8335) 2023-08-08 22:55:47 -04:00
Anju
8c98ef3e70 Copy dir if symlink fails (#7447) 2023-08-08 21:37:36 -04:00
Emily Rockman
44d1e73b4f Fix missing quote in fixtures.py (#8324) 2023-08-07 15:46:44 -05:00
Emily Rockman
53794fbaba Update implementation-ticket.yml (#8332) 2023-08-07 14:09:37 -06:00
Emily Rockman
556b4043e9 Update implementation-ticket.yml to reference adapters (#8329) 2023-08-07 12:39:29 -05:00
Grant Murray
424c636533 [CT-2776] [Feature] Enable-post-parse-population-of-dbt-custom-env (#7998)
* patch(events/functions): enable-repopulation-of-metadata-vars

* changie new
2023-08-04 12:59:47 -07:00
Emily Rockman
f63709260e add formatting events into json logs (#8308)
* add formatting events into json logs

* changelog

* Delete Under the Hood-20230803-100811.yaml
2023-08-03 15:56:01 -05:00
Michelle Ark
991618dfc1 capitalize integration-report name (#8265) 2023-08-02 18:06:05 -04:00
Chenyu Li
1af489b1cd fix constructing param with 0 value (#8298)
* fix constructing param with 0 value

* Update core/dbt/cli/flags.py

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>

---------

Co-authored-by: Doug Beatty <44704949+dbeatty10@users.noreply.github.com>
2023-08-02 14:57:55 -07:00
Kshitij Aranke
a433c31d6e Fix #7179 (#8279) 2023-08-02 16:56:16 +01:00
Peter Webb
5814928e38 Issue One Event Per Node Failure (#8210)
* Replaced the FirstRunResultError and AfterFirstRunResultError events with RunResultError.

* Attempts at reasonable unit tests.

* Restore event manager after unit test.
2023-08-02 10:24:05 -04:00
Ramon Vermeulen
6130a6e1d0 Support configurable delimiter for seed files, default to comma (#3990) (#7186)
* Support configurable delimiter for seed files, default to comma (#3990)

* Update Features-20230317-144957.yaml

* Moved "delimiter" to seed config instead of node config

* Update core/dbt/clients/agate_helper.py

Co-authored-by: Cor <jczuurmond@protonmail.com>

* Update test_contracts_graph_parsed.py

* fixed integration tests

* Added functional tests for seed files with a unique delimiter

* Added docstrings

* Added a test for an empty string configured delimiter value

* whitespace

* ran black

* updated changie entry

* Update Features-20230317-144957.yaml

---------

Co-authored-by: Cor <jczuurmond@protonmail.com>
2023-08-01 09:15:43 -07:00
Quigley Malcolm
7872f6a670 Add tests for specifcally checking the population of SemanticModel.depends_on (#8226) 2023-07-31 14:15:09 -07:00
Emily Rockman
f230e418aa hard code test splits (#8258)
* change trigger

* add logic for different targets

* fix comment

* hard code test splits
2023-07-31 13:26:17 -05:00
Quigley Malcolm
518eb73f88 [CT-2888] Support dbt-semantic-interfaces 0.2.0 (#8250)
* Upgrade DSI dependency to ~=0.2.0

* Allow users to specify `primary_entity` on semantic models

* Add `primary_entity` and `primary_entity_reference` to SemanticModel node

* Plumb primary_entity from unparsed to parsed semantic nodes

* Fix metric filter specifications in existing tests

* Add changie doc about supporting DSI 0.2.0
2023-07-28 14:36:51 -07:00
Emily Rockman
5b6d21d7da loosen the click pin (#8232)
* loosen the click pin

* changelog

* separate out sqlparse pin

* remove changelog

* add ignores
2023-07-28 15:53:27 -05:00
Michelle Ark
410506f448 [Fix] raise execution errors for runnable tasks (#8237) 2023-07-28 13:18:51 -04:00
dependabot[bot]
3cb44d37c0 Bump mypy from 1.4.0 to 1.4.1 (#8219)
* Bump mypy from 1.4.0 to 1.4.1

Bumps [mypy](https://github.com/python/mypy) from 1.4.0 to 1.4.1.
- [Commits](https://github.com/python/mypy/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add automated changelog yaml from template for bot PR

* update pre-commit

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
2023-07-28 07:33:16 -05:00
Quigley Malcolm
f977ed7471 [CT-2879] Fix unbound variable error in checked_agg_time_dimension_for_measure (#8235)
* Fix unbound variable error in `checked_agg_time_dimension_for_measure`

* Improve assertion error message in `checked_agg_time_dimension_for_measure`

* Add changie doc for checked_agg_time_dimension_for_measure unbound variable fix

* Add unit tests for checking functionality of `checked_agg_time_dimension_for_measure`
2023-07-27 14:58:59 -07:00
Emily Rockman
3f5617b569 pin upper bound for sqlparse (#8236)
* pin upper bound for sqlparse

* changelog
2023-07-27 16:29:42 -05:00
Gerda Shank
fe9c875d32 Ensure that target_schema from snapshot config is promoted to node level (#8117) 2023-07-27 13:40:52 -04:00
Michelle Ark
23b16ad6d2 Split integration tests into parallel groups / jobs (#6346) 2023-07-27 11:27:34 -04:00
Gerda Shank
fdeccfaf24 Initialize sqlparse lexer and tweak order of setting compilation fields (#8215) 2023-07-26 17:29:51 -04:00
dependabot[bot]
fecde23da5 Bump mypy from 1.3.0 to 1.4.0 (#7912)
* Bump mypy from 1.3.0 to 1.4.0

Bumps [mypy](https://github.com/python/mypy) from 1.3.0 to 1.4.0.
- [Commits](https://github.com/python/mypy/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add automated changelog yaml from template for bot PR

* add to pre-commit config

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Github Build Bot <buildbot@fishtownanalytics.com>
Co-authored-by: Emily Rockman <emily.rockman@dbtlabs.com>
2023-07-26 15:15:40 -05:00
Emily Rockman
b1d931337e up timeout (#8218) 2023-07-26 13:50:03 -05:00
Michelle Ark
39542336b8 Update implementation-ticket.yml (#8208) 2023-07-25 18:50:13 -04:00
Peter Webb
799588cada Update Core for Latest dbt-extractor with Version Parsing (#8206)
* Update dbt-extractor requirement, and adjust ref handling accordingly

* Add changelog entry.
2023-07-25 15:47:29 -04:00
Chenyu Li
f392add4b8 add param to control maxBytes for single dbt.log file (#8200)
* add param to control maxBytes for single dbt.log file

* nits

* nits

* Update core/dbt/cli/params.py

Co-authored-by: Peter Webb <peter.webb@dbtlabs.com>

---------

Co-authored-by: Peter Webb <peter.webb@dbtlabs.com>
2023-07-25 12:30:55 -07:00
Gerda Shank
49560bf2a2 Check for existing_relation immediately prior to renaming (#8193) 2023-07-25 12:59:18 -04:00
Quigley Malcolm
44b3ed5ae9 [CT-2594] Fix serialization of warn_error_options on Contexts (#8180)
* Add test ensuring `warn_error_options` is dictified in `invocation_args_dict` of contexts

* Add dictification specific to `warn_error_options` in `args_to_dict`

* Changie doc for serialization changes of warn_error_options
2023-07-24 13:35:27 -07:00
Quigley Malcolm
6235145641 [CT-1483] Let macro names include word materialization (#8181)
* Add test asserting that a macro with the work materializtion doesn't cause issues

* Let macro names include the word `materialization`

Previously we were checking if a macro included a materialization
based on if the macro name included the word `materialization`. However,
a macro name included the word `materialization` isn't guarnteed to
actually have a materialization, and a macro that doesn't have
`materialization` in the name isn't guaranteed to not have a materialization.
This change is to detect macros with materializations based on the
detected block type of the macro.

* Add changie doc materialization in macro detection
2023-07-24 13:10:42 -07:00
lllong33
ff5cb7ba51 Fixed double underline (#7944)
* Fixed double-underline
* backward compatibility postgres_get_relations
* Remove invalid comments
* compatibility adapter and get_relation
* fix generic for call
* fix adapter dispatch grammar issue
2023-07-21 17:28:27 -04:00
Quigley Malcolm
1e2b9ae962 [CT-1849] _connection_exception_retry handles EOFError exceptions (#8182)
* Add test for checking that `_connection_exception_retry` handles `EOFError`s

* Update `_connection_exception_retry` to handle `EOFError` exceptions

* Add changie docs for `_connection_exception_retry` handling `EOFError` exceptions
2023-07-21 11:34:06 -07:00
Michelle Ark
8cab58d248 Add implementation issue template (#8176) 2023-07-21 13:50:53 -04:00
Chenyu Li
0d645c227f add a node status (#8174) 2023-07-20 14:38:01 -07:00
Pádraic Slattery
fb6c349677 Correcting spelling of partition (#8101)
* Correcting spelling of partition

* Changie entry
2023-07-20 11:05:11 -07:00
Kshitij Aranke
eeb057085c Hotfix for 372: Use JSONEncoder in json.dumps (#8151) 2023-07-19 18:32:47 -05:00
Michelle Ark
121371f4a4 format exception from dbtPlugin.initialize (#8143) 2023-07-19 17:39:26 -04:00
Gerda Shank
a32713198b Rearrange pp_versioned_models test (#8150) 2023-07-19 17:05:09 -04:00
Jeremy Cohen
a1b067c683 Rm docs changelog entry (#8147) 2023-07-19 12:53:14 +02:00
leahwicz
22c40a4766 Update release-docker.yml 2023-05-03 09:33:31 -04:00
leahwicz
bcf140b3c1 Update release-docker.yml 2023-05-02 23:21:08 -04:00
leahwicz
e3692a6a3d Update release.yml 2023-05-02 23:11:40 -04:00
leahwicz
e7489383a2 Update release-docker.yml 2023-05-02 23:11:27 -04:00
leahwicz
70246c3f86 Update release.yml 2023-05-02 22:50:35 -04:00
leahwicz
0796c84da5 Update release-docker.yml 2023-05-02 22:50:18 -04:00
leahwicz
718482fb02 Update release.yml 2023-05-02 22:46:13 -04:00
leahwicz
a3fb66daa4 Update release-docker.yml 2023-05-02 22:45:41 -04:00
leahwicz
da34b80c26 Update release-docker.yml 2023-05-02 22:43:59 -04:00
leahwicz
ba5ab21140 Update release-docker.yml 2023-05-02 22:27:57 -04:00
leahwicz
65f41a1e36 Update testing.yml 2023-05-02 22:12:39 -04:00
leahwicz
0930c9c059 Update release.yml 2023-05-02 22:08:11 -04:00
leahwicz
1d193a9ab9 Update release-docker.yml 2023-05-02 22:07:53 -04:00
leahwicz
3adc6dca61 Update release-docker.yml 2023-05-02 22:03:15 -04:00
leahwicz
36d9f841d6 Update release-docker.yml 2023-05-02 22:01:27 -04:00
leahwicz
48ad13de00 Update release.yml 2023-05-02 21:57:19 -04:00
leahwicz
42935cce05 Update release.yml 2023-05-02 21:52:56 -04:00
leahwicz
e77f1c3b0f Update release.yml 2023-05-02 19:24:59 -04:00
leahwicz
388838aa99 Update testing.yml 2023-05-02 19:20:06 -04:00
leahwicz
d4d0990072 Update release.yml 2023-05-02 19:04:40 -04:00
leahwicz
4210d17f14 Update testing.yml 2023-05-02 19:03:15 -04:00
leahwicz
fbd12e78c9 Update testing.yml 2023-05-02 18:40:41 -04:00
leahwicz
83d3421e72 Update testing.yml 2023-05-02 17:55:56 -04:00
leahwicz
8bcbf73aaa Update release-docker.yml 2023-05-02 17:31:20 -04:00
leahwicz
cc5f15885d Update release.yml 2023-05-02 17:29:33 -04:00
leahwicz
20fdf55bf6 Update testing.yml 2023-05-02 17:15:07 -04:00
leahwicz
955dcec68b Update testing.yml 2023-05-02 17:03:23 -04:00
leahwicz
2b8564b16f Update testing.yml 2023-05-02 16:55:21 -04:00
leahwicz
57da3e51cd Update testing.yml 2023-05-02 13:14:50 -04:00
leahwicz
dede0e9747 Update testing.yml 2023-05-02 13:11:57 -04:00
leahwicz
35d2fc1158 Update testing.yml 2023-05-02 13:10:04 -04:00
leahwicz
c5267335a3 Update testing.yml 2023-05-02 13:08:00 -04:00
leahwicz
15c7b589c2 Update testing.yml 2023-05-02 13:06:35 -04:00
leahwicz
0ada5e8bf7 Create testing.yml 2023-05-02 12:17:26 -04:00
leahwicz
412ac8d1b9 Update release.yml 2023-04-19 09:08:39 -04:00
leahwicz
5df501a281 Update release.yml 2023-04-18 21:48:49 -04:00
leahwicz
3e4c61d020 Update release.yml 2023-04-18 21:46:26 -04:00
leahwicz
cc39fe51b3 Update release-docker.yml 2023-04-18 21:44:08 -04:00
leahwicz
89cd24388d Update release.yml 2023-04-18 21:40:55 -04:00
leahwicz
d5da0a8093 Update release.yml 2023-04-18 21:36:29 -04:00
leahwicz
88ae1f8871 Update release-docker.yml 2023-04-18 09:15:19 -04:00
leahwicz
50b3d1deaa Update release-docker.yml 2023-04-17 21:22:55 -04:00
leahwicz
3b3def5b8a Update release-docker.yml 2023-04-17 21:11:34 -04:00
leahwicz
4f068a45ff Update release-docker.yml 2023-04-17 21:06:40 -04:00
leahwicz
23a9504a51 Update release-docker.yml 2023-04-17 21:03:29 -04:00
leahwicz
d0d4eba477 Update release-docker.yml 2023-04-17 21:02:10 -04:00
leahwicz
a3fab0b5a9 Update release-docker.yml 2023-04-17 20:57:20 -04:00
443 changed files with 38629 additions and 5800 deletions

View File

@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.7.0a1
current_version = 1.7.17
parse = (?P<major>[\d]+) # major version number
\.(?P<minor>[\d]+) # minor version number
\.(?P<patch>[\d]+) # patch version number

157
.changes/1.7.0.md Normal file
View File

@@ -0,0 +1,157 @@
## dbt-core 1.7.0 - November 02, 2023
### Breaking Changes
- Removed the FirstRunResultError and AfterFirstRunResultError event types, using the existing RunResultError in their place. ([#7963](https://github.com/dbt-labs/dbt-core/issues/7963))
### Features
- add log file of installed packages via dbt deps ([#6643](https://github.com/dbt-labs/dbt-core/issues/6643))
- Enable re-population of metadata vars post-environment change during programmatic invocation ([#8010](https://github.com/dbt-labs/dbt-core/issues/8010))
- Added support to configure a delimiter for a seed file, defaults to comma ([#3990](https://github.com/dbt-labs/dbt-core/issues/3990))
- Allow specification of `create_metric: true` on measures ([#8125](https://github.com/dbt-labs/dbt-core/issues/8125))
- Add node attributes related to compilation to run_results.json ([#7519](https://github.com/dbt-labs/dbt-core/issues/7519))
- Add --no-inject-ephemeral-ctes flag for `compile` command, for usage by linting. ([#8480](https://github.com/dbt-labs/dbt-core/issues/8480))
- Support configuration of semantic models with the addition of enable/disable and group enablement. ([#7968](https://github.com/dbt-labs/dbt-core/issues/7968))
- Accept a `dbt-cloud` config in dbt_project.yml ([#8438](https://github.com/dbt-labs/dbt-core/issues/8438))
- Support atomic replace in the global replace macro ([#8539](https://github.com/dbt-labs/dbt-core/issues/8539))
- Use translate_type on data_type in model.columns in templates by default, remove no op `TYPE_LABELS` ([#8007](https://github.com/dbt-labs/dbt-core/issues/8007))
- Add an option to generate static documentation ([#8614](https://github.com/dbt-labs/dbt-core/issues/8614))
- Allow setting "access" as a config in addition to as a property ([#8383](https://github.com/dbt-labs/dbt-core/issues/8383))
- Loosen typing requirement on renameable/replaceable relations to Iterable to allow adapters more flexibility in registering relation types, include docstrings as suggestions ([#8647](https://github.com/dbt-labs/dbt-core/issues/8647))
- Add support for optional label in semantic_models, measures, dimensions and entities. ([#8595](https://github.com/dbt-labs/dbt-core/issues/8595), [#8755](https://github.com/dbt-labs/dbt-core/issues/8755))
- Allow adapters to include package logs in dbt standard logging ([#7859](https://github.com/dbt-labs/dbt-core/issues/7859))
- Support storing test failures as views ([#6914](https://github.com/dbt-labs/dbt-core/issues/6914))
- resolve packages with same git repo and unique subdirectory ([#5374](https://github.com/dbt-labs/dbt-core/issues/5374))
- Add new ResourceReport event to record memory/cpu/io metrics ([#8342](https://github.com/dbt-labs/dbt-core/issues/8342))
- Adding `date_spine` macro (and supporting macros) from dbt-utils to dbt-core ([#8172](https://github.com/dbt-labs/dbt-core/issues/8172))
- Support `fill_nulls_with` and `join_to_timespine` for metric nodes ([#8593](https://github.com/dbt-labs/dbt-core/issues/8593), [#8755](https://github.com/dbt-labs/dbt-core/issues/8755))
- Raise a warning when a contracted model has a numeric field without scale defined ([#8183](https://github.com/dbt-labs/dbt-core/issues/8183))
- Added support for retrieving partial catalog information from a schema ([#8521](https://github.com/dbt-labs/dbt-core/issues/8521))
- Add meta attribute to SemanticModels config ([#8511](https://github.com/dbt-labs/dbt-core/issues/8511))
- Selectors with docs generate limits catalog generation ([#6014](https://github.com/dbt-labs/dbt-core/issues/6014))
- Allow freshness to be determined via DBMS metadata for supported adapters ([#8704](https://github.com/dbt-labs/dbt-core/issues/8704))
- Add support semantic layer SavedQuery node type ([#8594](https://github.com/dbt-labs/dbt-core/issues/8594))
- Add exports to SavedQuery spec ([#8892](https://github.com/dbt-labs/dbt-core/issues/8892))
### Fixes
- Copy dir during `dbt deps` if symlink fails ([#7428](https://github.com/dbt-labs/dbt-core/issues/7428), [#8223](https://github.com/dbt-labs/dbt-core/issues/8223))
- If --profile specified with dbt-init, create the project with the specified profile ([#6154](https://github.com/dbt-labs/dbt-core/issues/6154))
- Fixed double-underline ([#5301](https://github.com/dbt-labs/dbt-core/issues/5301))
- Copy target_schema from config into snapshot node ([#6745](https://github.com/dbt-labs/dbt-core/issues/6745))
- Enable converting deprecation warnings to errors ([#8130](https://github.com/dbt-labs/dbt-core/issues/8130))
- Add status to Parse Inline Error ([#8173](https://github.com/dbt-labs/dbt-core/issues/8173))
- Ensure `warn_error_options` get serialized in `invocation_args_dict` ([#7694](https://github.com/dbt-labs/dbt-core/issues/7694))
- Stop detecting materialization macros based on macro name ([#6231](https://github.com/dbt-labs/dbt-core/issues/6231))
- Update `dbt deps` download retry logic to handle `EOFError` exceptions ([#6653](https://github.com/dbt-labs/dbt-core/issues/6653))
- Improve handling of CTE injection with ephemeral models ([#8213](https://github.com/dbt-labs/dbt-core/issues/8213))
- Fix unbound local variable error in `checked_agg_time_dimension_for_measure` ([#8230](https://github.com/dbt-labs/dbt-core/issues/8230))
- Ensure runtime errors are raised for graph runnable tasks (compile, show, run, etc) ([#8166](https://github.com/dbt-labs/dbt-core/issues/8166))
- Fix retry not working with log-file-max-bytes ([#8297](https://github.com/dbt-labs/dbt-core/issues/8297))
- Add explicit support for integers for the show command ([#8153](https://github.com/dbt-labs/dbt-core/issues/8153))
- Detect changes to model access, version, or latest_version in state:modified ([#8189](https://github.com/dbt-labs/dbt-core/issues/8189))
- Add connection status into list of statuses for dbt debug ([#8350](https://github.com/dbt-labs/dbt-core/issues/8350))
- fix fqn-selection for external versioned models ([#8374](https://github.com/dbt-labs/dbt-core/issues/8374))
- Fix: DbtInternalError after model that previously ref'd external model is deleted ([#8375](https://github.com/dbt-labs/dbt-core/issues/8375))
- Fix using list command with path selector and project-dir ([#8385](https://github.com/dbt-labs/dbt-core/issues/8385))
- Remedy performance regression by only writing run_results.json once. ([#8360](https://github.com/dbt-labs/dbt-core/issues/8360))
- Add support for swapping materialized views with tables/views and vice versa ([#8449](https://github.com/dbt-labs/dbt-core/issues/8449))
- Turn breaking changes to contracted models into warnings for unversioned models ([#8384](https://github.com/dbt-labs/dbt-core/issues/8384), [#8282](https://github.com/dbt-labs/dbt-core/issues/8282))
- Ensure parsing does not break when `window_groupings` is not specified for `non_additive_dimension` ([#8453](https://github.com/dbt-labs/dbt-core/issues/8453))
- fix ambiguous reference error for tests and versions when model name is duplicated across packages ([#8327](https://github.com/dbt-labs/dbt-core/issues/8327), [#8493](https://github.com/dbt-labs/dbt-core/issues/8493))
- Fix "Internal Error: Expected node <unique-id> not found in manifest" when depends_on set on ModelNodeArgs ([#8506](https://github.com/dbt-labs/dbt-core/issues/8506))
- Fix snapshot success message ([#7583](https://github.com/dbt-labs/dbt-core/issues/7583))
- Parse the correct schema version from manifest ([#8544](https://github.com/dbt-labs/dbt-core/issues/8544))
- make version comparison insensitive to order ([#8571](https://github.com/dbt-labs/dbt-core/issues/8571))
- Update metric helper functions to work with new semantic layer metrics ([#8134](https://github.com/dbt-labs/dbt-core/issues/8134))
- Disallow cleaning paths outside current working directory ([#8318](https://github.com/dbt-labs/dbt-core/issues/8318))
- Warn when --state == --target ([#8160](https://github.com/dbt-labs/dbt-core/issues/8160))
- update dbt show to include limit in DWH query ([#8496,](https://github.com/dbt-labs/dbt-core/issues/8496,), [#8417](https://github.com/dbt-labs/dbt-core/issues/8417))
- Support quoted parameter list for MultiOption CLI options. ([#8598](https://github.com/dbt-labs/dbt-core/issues/8598))
- Support global flags passed in after subcommands ([#6497](https://github.com/dbt-labs/dbt-core/issues/6497))
- Lower bound of `8.0.2` for `click` ([#8683](https://github.com/dbt-labs/dbt-core/issues/8683))
- Fixes test type edges filter ([#8692](https://github.com/dbt-labs/dbt-core/issues/8692))
- semantic models in graph selection ([#8589](https://github.com/dbt-labs/dbt-core/issues/8589))
- Support doc blocks in nested semantic model YAML ([#8509](https://github.com/dbt-labs/dbt-core/issues/8509))
- avoid double-rendering sql_header in dbt show ([#8739](https://github.com/dbt-labs/dbt-core/issues/8739))
- Fix tag selection for projects with semantic models ([#8749](https://github.com/dbt-labs/dbt-core/issues/8749))
- Foreign key constraint on incremental model results in Database Error ([#8022](https://github.com/dbt-labs/dbt-core/issues/8022))
- Support docs blocks on versioned model column descriptions ([#8540](https://github.com/dbt-labs/dbt-core/issues/8540))
- Enable seeds to be handled from stored manifest data ([#6875](https://github.com/dbt-labs/dbt-core/issues/6875))
- Override path-like args in dbt retry ([#8682](https://github.com/dbt-labs/dbt-core/issues/8682))
- Group updates on unmodified nodes are handled gracefully for state:modified ([#8371](https://github.com/dbt-labs/dbt-core/issues/8371))
- Partial parsing fix for adding groups and updating models at the same time ([#8697](https://github.com/dbt-labs/dbt-core/issues/8697))
- Fix partial parsing not working for semantic model change ([#8859](https://github.com/dbt-labs/dbt-core/issues/8859))
- Rework get_catalog implementation to retain previous adapter interface semantics ([#8846](https://github.com/dbt-labs/dbt-core/issues/8846))
- Add back contract enforcement for temporary tables on postgres ([#8857](https://github.com/dbt-labs/dbt-core/issues/8857))
- Add version to fqn when version==0 ([#8836](https://github.com/dbt-labs/dbt-core/issues/8836))
- Fix cased comparison in catalog-retrieval function. ([#8939](https://github.com/dbt-labs/dbt-core/issues/8939))
- Catalog queries now assign the correct type to materialized views ([#8864](https://github.com/dbt-labs/dbt-core/issues/8864))
- Make relation filtering None-tolerant for maximal flexibility across adapters. ([#8974](https://github.com/dbt-labs/dbt-core/issues/8974))
### Docs
- Corrected spelling of "Partiton" ([dbt-docs/#8100](https://github.com/dbt-labs/dbt-docs/issues/8100))
- Remove static SQL codeblock for metrics ([dbt-docs/#436](https://github.com/dbt-labs/dbt-docs/issues/436))
- fixed comment util.py ([dbt-docs/#None](https://github.com/dbt-labs/dbt-docs/issues/None))
- Fix newline escapes and improve formatting in docker README ([dbt-docs/#8211](https://github.com/dbt-labs/dbt-docs/issues/8211))
- Display contract and column constraints on the model page ([dbt-docs/#433](https://github.com/dbt-labs/dbt-docs/issues/433))
- Display semantic model details in docs ([dbt-docs/#431](https://github.com/dbt-labs/dbt-docs/issues/431))
### Under the Hood
- Switch from hologram to mashumaro jsonschema ([#8426](https://github.com/dbt-labs/dbt-core/issues/8426))
- Refactor flaky test pp_versioned_models ([#7781](https://github.com/dbt-labs/dbt-core/issues/7781))
- format exception from dbtPlugin.initialize ([#8152](https://github.com/dbt-labs/dbt-core/issues/8152))
- A way to control maxBytes for a single dbt.log file ([#8199](https://github.com/dbt-labs/dbt-core/issues/8199))
- Ref expressions with version can now be processed by the latest version of the high-performance dbt-extractor library. ([#7688](https://github.com/dbt-labs/dbt-core/issues/7688))
- Bump manifest schema version to v11, freeze manifest v10 ([#8333](https://github.com/dbt-labs/dbt-core/issues/8333))
- add tracking for plugin.get_nodes calls ([#8344](https://github.com/dbt-labs/dbt-core/issues/8344))
- add internal flag: --no-partial-parse-file-diff to inform whether to compute a file diff during partial parsing ([#8363](https://github.com/dbt-labs/dbt-core/issues/8363))
- Add return values to a number of functions for mypy ([#8389](https://github.com/dbt-labs/dbt-core/issues/8389))
- Fix mypy warnings for ManifestLoader.load() ([#8401](https://github.com/dbt-labs/dbt-core/issues/8401))
- Use python version 3.10.7 in Docker image. ([#8444](https://github.com/dbt-labs/dbt-core/issues/8444))
- Re-organize jinja macros: relation-specific in /macros/adapters/relations/<relation>, relation agnostic in /macros/relations ([#8449](https://github.com/dbt-labs/dbt-core/issues/8449))
- Update typing to meet mypy standards ([#8396](https://github.com/dbt-labs/dbt-core/issues/8396))
- Mypy errors - adapters/factory.py ([#8387](https://github.com/dbt-labs/dbt-core/issues/8387))
- Added more type annotations. ([#8537](https://github.com/dbt-labs/dbt-core/issues/8537))
- Audit potential circular dependencies ([#8349](https://github.com/dbt-labs/dbt-core/issues/8349))
- Add functional test for advanced ref override ([#8566](https://github.com/dbt-labs/dbt-core/issues/8566))
- Add typing to __init__ in base.py ([#8398](https://github.com/dbt-labs/dbt-core/issues/8398))
- Fix untyped functions in task/runnable.py (mypy warning) ([#8402](https://github.com/dbt-labs/dbt-core/issues/8402))
- add a test for ephemeral cte injection ([#8225](https://github.com/dbt-labs/dbt-core/issues/8225))
- Fix test_numeric_values to look for more specific strings ([#8470](https://github.com/dbt-labs/dbt-core/issues/8470))
- Pin types-requests<2.31.0 in `dev-requirements.txt` ([#8789](https://github.com/dbt-labs/dbt-core/issues/8789))
- Add warning_tag to UnversionedBreakingChange ([#8827](https://github.com/dbt-labs/dbt-core/issues/8827))
- Update v10 manifest schema to match 1.6 for testing schema compatibility ([#8835](https://github.com/dbt-labs/dbt-core/issues/8835))
- Add a no-op runner for Saved Qeury ([#8893](https://github.com/dbt-labs/dbt-core/issues/8893))
### Dependencies
- Bump mypy from 1.3.0 to 1.4.0 ([#7912](https://github.com/dbt-labs/dbt-core/pull/7912))
- Bump mypy from 1.4.0 to 1.4.1 ([#8219](https://github.com/dbt-labs/dbt-core/pull/8219))
- Update pin for click<9 ([#8232](https://github.com/dbt-labs/dbt-core/pull/8232))
- Add upper bound to sqlparse pin of <0.5 ([#8236](https://github.com/dbt-labs/dbt-core/pull/8236))
- Support dbt-semantic-interfaces 0.2.0 ([#8250](https://github.com/dbt-labs/dbt-core/pull/8250))
- Bump docker/build-push-action from 4 to 5 ([#8783](https://github.com/dbt-labs/dbt-core/pull/8783))
- Upgrade dbt-semantic-interfaces dep to 0.3.0 ([#8819](https://github.com/dbt-labs/dbt-core/pull/8819))
- Begin using DSI 0.4.x ([#8892](https://github.com/dbt-labs/dbt-core/pull/8892))
### Contributors
- [@anjutiwari](https://github.com/anjutiwari) ([#7428](https://github.com/dbt-labs/dbt-core/issues/7428), [#8223](https://github.com/dbt-labs/dbt-core/issues/8223))
- [@benmosher](https://github.com/benmosher) ([#8480](https://github.com/dbt-labs/dbt-core/issues/8480))
- [@d-kaneshiro](https://github.com/d-kaneshiro) ([#None](https://github.com/dbt-labs/dbt-core/issues/None))
- [@dave-connors-3](https://github.com/dave-connors-3) ([#8153](https://github.com/dbt-labs/dbt-core/issues/8153), [#8589](https://github.com/dbt-labs/dbt-core/issues/8589))
- [@dylan-murray](https://github.com/dylan-murray) ([#8683](https://github.com/dbt-labs/dbt-core/issues/8683))
- [@ezraerb](https://github.com/ezraerb) ([#6154](https://github.com/dbt-labs/dbt-core/issues/6154))
- [@gem7318](https://github.com/gem7318) ([#8010](https://github.com/dbt-labs/dbt-core/issues/8010))
- [@jamezrin](https://github.com/jamezrin) ([#8211](https://github.com/dbt-labs/dbt-core/issues/8211))
- [@jusbaldw](https://github.com/jusbaldw) ([#6643](https://github.com/dbt-labs/dbt-core/issues/6643))
- [@lllong33](https://github.com/lllong33) ([#5301](https://github.com/dbt-labs/dbt-core/issues/5301))
- [@marcodamore](https://github.com/marcodamore) ([#436](https://github.com/dbt-labs/dbt-core/issues/436))
- [@mescanne](https://github.com/mescanne) ([#8614](https://github.com/dbt-labs/dbt-core/issues/8614))
- [@pgoslatara](https://github.com/pgoslatara) ([#8100](https://github.com/dbt-labs/dbt-core/issues/8100))
- [@philippeboyd](https://github.com/philippeboyd) ([#5374](https://github.com/dbt-labs/dbt-core/issues/5374))
- [@ramonvermeulen](https://github.com/ramonvermeulen) ([#3990](https://github.com/dbt-labs/dbt-core/issues/3990))
- [@renanleme](https://github.com/renanleme) ([#8692](https://github.com/dbt-labs/dbt-core/issues/8692))

8
.changes/1.7.1.md Normal file
View File

@@ -0,0 +1,8 @@
## dbt-core 1.7.1 - November 07, 2023
### Fixes
- Fix compilation exception running empty seed file and support new Integer agate data_type ([#8895](https://github.com/dbt-labs/dbt-core/issues/8895))
- Update run_results.json from previous versions of dbt to support deferral and rerun from failure ([#9010](https://github.com/dbt-labs/dbt-core/issues/9010))
- Use MANIFEST.in to recursively include all jinja templates; fixes issue where some templates were not included in the distribution ([#9016](https://github.com/dbt-labs/dbt-core/issues/9016))
- Fix git repository with subdirectory for Deps ([#9000](https://github.com/dbt-labs/dbt-core/issues/9000))

11
.changes/1.7.10.md Normal file
View File

@@ -0,0 +1,11 @@
## dbt-core 1.7.10 - March 14, 2024
### Fixes
- Do not add duplicate input_measures ([#9360](https://github.com/dbt-labs/dbt-core/issues/9360))
- Fix partial parsing `KeyError` on deleted schema files ([#8860](https://github.com/dbt-labs/dbt-core/issues/8860))
- Support saved queries in `dbt list` ([#9532](https://github.com/dbt-labs/dbt-core/issues/9532))
### Dependencies
- Restrict protobuf to 4.* versions ([#9566](https://github.com/dbt-labs/dbt-core/pull/9566))

6
.changes/1.7.11.md Normal file
View File

@@ -0,0 +1,6 @@
## dbt-core 1.7.11 - March 28, 2024
### Fixes
- Tighten exception handling to avoid worker thread hangs. ([#9583](https://github.com/dbt-labs/dbt-core/issues/9583))
- Add field wrapper to BaseRelation members that were missing it. ([#9681](https://github.com/dbt-labs/dbt-core/issues/9681))

6
.changes/1.7.12.md Normal file
View File

@@ -0,0 +1,6 @@
## dbt-core 1.7.12 - April 16, 2024
### Fixes
- Fix assorted source freshness edgecases so check is run or actionable information is given ([#9078](https://github.com/dbt-labs/dbt-core/issues/9078))
- Exclude password-like fields for considering reparse ([#9795](https://github.com/dbt-labs/dbt-core/issues/9795))

8
.changes/1.7.13.md Normal file
View File

@@ -0,0 +1,8 @@
## dbt-core 1.7.13 - April 18, 2024
### Security
- Bump sqlparse to >=0.5.0, <0.6.0 to address GHSA-2m57-hf25-phgg ([#9951](https://github.com/dbt-labs/dbt-core/pull/9951))
### Contributors
- [@emmoop](https://github.com/emmoop) ([#9951](https://github.com/dbt-labs/dbt-core/pull/9951))

16
.changes/1.7.14.md Normal file
View File

@@ -0,0 +1,16 @@
## dbt-core 1.7.14 - May 02, 2024
### Features
- Move flags from UserConfig in profiles.yml to flags in dbt_project.yml ([#9183](https://github.com/dbt-labs/dbt-core/issues/9183))
- Add require_explicit_package_overrides_for_builtin_materializations to dbt_project.yml flags, which can be used to opt-out of overriding built-in materializations from packages ([#10007](https://github.com/dbt-labs/dbt-core/issues/10007))
### Fixes
- remove materialized views from renambeable relation and remove a quote ([#127](https://github.com/dbt-labs/dbt-core/issues/127))
- Replace usage of `Set` with `List` to fix issue with index updates intermittently happening out of order ([#72](https://github.com/dbt-labs/dbt-core/issues/72))
### Under the Hood
- Raise deprecation warning if installed package overrides built-in materialization ([#9971](https://github.com/dbt-labs/dbt-core/issues/9971))
- Remove the final underscore from secret environment variable constants. ([#10052](https://github.com/dbt-labs/dbt-core/issues/10052))

9
.changes/1.7.15.md Normal file
View File

@@ -0,0 +1,9 @@
## dbt-core 1.7.15 - May 22, 2024
### Fixes
- Fix the semicolon semantics for indexes while respecting other bug fix ([#85](https://github.com/dbt-labs/dbt-core/issues/85))
### Security
- Explicitly bind to localhost in docs serve ([#10209](https://github.com/dbt-labs/dbt-core/issues/10209))

5
.changes/1.7.16.md Normal file
View File

@@ -0,0 +1,5 @@
## dbt-core 1.7.16 - June 05, 2024
### Features
- Add --host flag to dbt docs serve, defaulting to '127.0.0.1' ([#10229](https://github.com/dbt-labs/dbt-core/issues/10229))

5
.changes/1.7.17.md Normal file
View File

@@ -0,0 +1,5 @@
## dbt-core 1.7.17 - June 20, 2024
### Docs
- Fix npm security vulnerabilities as of June 2024 ([dbt-docs/#513](https://github.com/dbt-labs/dbt-docs/issues/513))

16
.changes/1.7.2.md Normal file
View File

@@ -0,0 +1,16 @@
## dbt-core 1.7.2 - November 16, 2023
### Features
- Support setting export configs hierarchically via saved query and project configs ([#8956](https://github.com/dbt-labs/dbt-core/issues/8956))
### Fixes
- Fix formatting of tarball information in packages-lock.yml ([#9062](https://github.com/dbt-labs/dbt-core/issues/9062))
### Under the Hood
- Treat SystemExit as an interrupt if raised during node execution. ([#n/a](https://github.com/dbt-labs/dbt-core/issues/n/a))
### Contributors
- [@benmosher](https://github.com/benmosher) ([#n/a](https://github.com/dbt-labs/dbt-core/issues/n/a))

7
.changes/1.7.3.md Normal file
View File

@@ -0,0 +1,7 @@
## dbt-core 1.7.3 - November 29, 2023
### Fixes
- deps: Lock git packages to commit SHA during resolution ([#9050](https://github.com/dbt-labs/dbt-core/issues/9050))
- deps: Use PackageRenderer to read package-lock.json ([#9127](https://github.com/dbt-labs/dbt-core/issues/9127))
- Get sources working again in dbt docs generate ([#9119](https://github.com/dbt-labs/dbt-core/issues/9119))

12
.changes/1.7.4.md Normal file
View File

@@ -0,0 +1,12 @@
## dbt-core 1.7.4 - December 14, 2023
### Features
- Adds support for parsing conversion metric related properties for the semantic layer. ([#9203](https://github.com/dbt-labs/dbt-core/issues/9203))
### Fixes
- Ensure we produce valid jsonschema schemas for manifest, catalog, run-results, and sources ([#8991](https://github.com/dbt-labs/dbt-core/issues/8991))
### Contributors
- [@WilliamDee](https://github.com/WilliamDee) ([#9203](https://github.com/dbt-labs/dbt-core/issues/9203))

8
.changes/1.7.5.md Normal file
View File

@@ -0,0 +1,8 @@
## dbt-core 1.7.5 - January 18, 2024
### Fixes
- Preserve the value of vars and the --full-refresh flags when using retry. ([#9112](https://github.com/dbt-labs/dbt-core/issues/9112))
### Contributors
- [@peterallenwebb,](https://github.com/peterallenwebb,) ([#9112](https://github.com/dbt-labs/dbt-core/issues/9112))

6
.changes/1.7.6.md Normal file
View File

@@ -0,0 +1,6 @@
## dbt-core 1.7.6 - January 25, 2024
### Fixes
- Handle unknown `type_code` for model contracts ([#8877](https://github.com/dbt-labs/dbt-core/issues/8877), [#8353](https://github.com/dbt-labs/dbt-core/issues/8353))
- Fix retry command run from CLI ([#9444](https://github.com/dbt-labs/dbt-core/issues/9444))

6
.changes/1.7.7.md Normal file
View File

@@ -0,0 +1,6 @@
## dbt-core 1.7.7 - February 01, 2024
### Fixes
- Fix seed and source selection in `dbt docs generate` ([#9161](https://github.com/dbt-labs/dbt-core/issues/9161))
- Add TestGenerateCatalogWithExternalNodes, include empty nodes in node selection during docs generate ([#9456](https://github.com/dbt-labs/dbt-core/issues/9456))

6
.changes/1.7.8.md Normal file
View File

@@ -0,0 +1,6 @@
## dbt-core 1.7.8 - February 14, 2024
### Fixes
- When patching versioned models, set constraints after config ([#9364](https://github.com/dbt-labs/dbt-core/issues/9364))
- Store node_info in node associated logging events ([#9557](https://github.com/dbt-labs/dbt-core/issues/9557))

18
.changes/1.7.9.md Normal file
View File

@@ -0,0 +1,18 @@
## dbt-core 1.7.9 - February 28, 2024
### Fixes
- Fix node_info contextvar handling so incorrect node_info doesn't persist ([#8866](https://github.com/dbt-labs/dbt-core/issues/8866))
- Add target-path to retry ([#8948](https://github.com/dbt-labs/dbt-core/issues/8948))
### Under the Hood
- Make dbt-core compatible with Python 3.12 ([#9007](https://github.com/dbt-labs/dbt-core/issues/9007))
- Restrict protobuf to major version 4. ([#9566](https://github.com/dbt-labs/dbt-core/issues/9566))
### Security
- Update Jinja2 to >= 3.1.3 to address CVE-2024-22195 ([#CVE-2024-22195](https://github.com/dbt-labs/dbt-core/pull/CVE-2024-22195))
### Contributors
- [@l1xnan](https://github.com/l1xnan) ([#9007](https://github.com/dbt-labs/dbt-core/issues/9007))

View File

@@ -1,6 +0,0 @@
kind: Docs
body: Fix for column tests not rendering on quoted columns
time: 2023-05-31T11:54:19.687363-04:00
custom:
Author: drewbanin
Issue: "201"

View File

@@ -1,6 +0,0 @@
kind: Docs
body: Remove static SQL codeblock for metrics
time: 2023-07-18T19:24:22.155323+02:00
custom:
Author: marcodamore
Issue: "436"

View File

@@ -1,6 +0,0 @@
kind: Fixes
body: Enable converting deprecation warnings to errors
time: 2023-07-18T12:55:18.03914-04:00
custom:
Author: michelleark
Issue: "8130"

View File

@@ -31,43 +31,7 @@ kinds:
- {{.Body}} ({{ range $index, $element := $IssueList }}{{if $index}}, {{end}}{{$element}}{{end}})
- label: Under the Hood
- label: Dependencies
changeFormat: |-
{{- $PRList := list }}
{{- $changes := splitList " " $.Custom.PR }}
{{- range $pullrequest := $changes }}
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/pull/nbr)" | replace "nbr" $pullrequest }}
{{- $PRList = append $PRList $changeLink }}
{{- end -}}
- {{.Body}} ({{ range $index, $element := $PRList }}{{if $index}}, {{end}}{{$element}}{{end}})
skipGlobalChoices: true
additionalChoices:
- key: Author
label: GitHub Username(s) (separated by a single space if multiple)
type: string
minLength: 3
- key: PR
label: GitHub Pull Request Number (separated by a single space if multiple)
type: string
minLength: 1
- label: Security
changeFormat: |-
{{- $PRList := list }}
{{- $changes := splitList " " $.Custom.PR }}
{{- range $pullrequest := $changes }}
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/pull/nbr)" | replace "nbr" $pullrequest }}
{{- $PRList = append $PRList $changeLink }}
{{- end -}}
- {{.Body}} ({{ range $index, $element := $PRList }}{{if $index}}, {{end}}{{$element}}{{end}})
skipGlobalChoices: true
additionalChoices:
- key: Author
label: GitHub Username(s) (separated by a single space if multiple)
type: string
minLength: 3
- key: PR
label: GitHub Pull Request Number (separated by a single space if multiple)
type: string
minLength: 1
newlines:
afterChangelogHeader: 1
@@ -106,18 +70,10 @@ footerFormat: |
{{- $changeList := splitList " " $change.Custom.Author }}
{{- $IssueList := list }}
{{- $changeLink := $change.Kind }}
{{- if or (eq $change.Kind "Dependencies") (eq $change.Kind "Security") }}
{{- $changes := splitList " " $change.Custom.PR }}
{{- range $issueNbr := $changes }}
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/pull/nbr)" | replace "nbr" $issueNbr }}
{{- $IssueList = append $IssueList $changeLink }}
{{- end -}}
{{- else }}
{{- $changes := splitList " " $change.Custom.Issue }}
{{- range $issueNbr := $changes }}
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/issues/nbr)" | replace "nbr" $issueNbr }}
{{- $IssueList = append $IssueList $changeLink }}
{{- end -}}
{{- $changes := splitList " " $change.Custom.Issue }}
{{- range $issueNbr := $changes }}
{{- $changeLink := "[#nbr](https://github.com/dbt-labs/dbt-core/issues/nbr)" | replace "nbr" $issueNbr }}
{{- $IssueList = append $IssueList $changeLink }}
{{- end }}
{{- /* check if this contributor has other changes associated with them already */}}
{{- if hasKey $contributorDict $author }}

19
.github/CODEOWNERS vendored
View File

@@ -13,23 +13,6 @@
# the core team as a whole will be assigned
* @dbt-labs/core-team
### OSS Tooling Guild
/.github/ @dbt-labs/guild-oss-tooling
.bumpversion.cfg @dbt-labs/guild-oss-tooling
.changie.yaml @dbt-labs/guild-oss-tooling
pre-commit-config.yaml @dbt-labs/guild-oss-tooling
pytest.ini @dbt-labs/guild-oss-tooling
tox.ini @dbt-labs/guild-oss-tooling
pyproject.toml @dbt-labs/guild-oss-tooling
requirements.txt @dbt-labs/guild-oss-tooling
dev_requirements.txt @dbt-labs/guild-oss-tooling
/core/setup.py @dbt-labs/guild-oss-tooling
/core/MANIFEST.in @dbt-labs/guild-oss-tooling
### ADAPTERS
# Adapter interface ("base" + "sql" adapter defaults, cache)
@@ -40,7 +23,7 @@ dev_requirements.txt @dbt-labs/guild-oss-tooling
# Postgres plugin
/plugins/ @dbt-labs/core-adapters
/plugins/postgres/setup.py @dbt-labs/core-adapters @dbt-labs/guild-oss-tooling
/plugins/postgres/setup.py @dbt-labs/core-adapters
# Functional tests for adapter plugins
/tests/adapter @dbt-labs/core-adapters

View File

@@ -0,0 +1,58 @@
name: 🛠️ Implementation
description: This is an implementation ticket intended for use by the maintainers of dbt-core
title: "[<project>] <title>"
labels: ["user docs"]
body:
- type: markdown
attributes:
value: This is an implementation ticket intended for use by the maintainers of dbt-core
- type: checkboxes
attributes:
label: Housekeeping
description: >
A couple friendly reminders:
1. Remove the `user docs` label if the scope of this work does not require changes to https://docs.getdbt.com/docs: no end-user interface (e.g. yml spec, CLI, error messages, etc) or functional changes
2. Link any blocking issues in the "Blocked on" field under the "Core devs & maintainers" project.
options:
- label: I am a maintainer of dbt-core
required: true
- type: textarea
attributes:
label: Short description
description: |
Describe the scope of the ticket, a high-level implementation approach and any tradeoffs to consider
validations:
required: true
- type: textarea
attributes:
label: Acceptance criteria
description: |
What is the definition of done for this ticket? Include any relevant edge cases and/or test cases
validations:
required: true
- type: textarea
attributes:
label: Impact to Other Teams
description: |
Will this change impact other teams? Include details of the kinds of changes required (new tests, code changes, related tickets) and _add the relevant `Impact:[team]` label_.
placeholder: |
Example: This change impacts `dbt-redshift` because the tests will need to be modified. The `Impact:[Adapter]` label has been added.
validations:
required: true
- type: textarea
attributes:
label: Will backports be required?
description: |
Will this change need to be backported to previous versions? Add details, possible blockers to backporting and _add the relevant backport labels `backport 1.x.latest`_
placeholder: |
Example: Backport to 1.6.latest, 1.5.latest and 1.4.latest. Since 1.4 isn't using click, the backport may be complicated. The `backport 1.6.latest`, `backport 1.5.latest` and `backport 1.4.latest` labels have been added.
validations:
required: true
- type: textarea
attributes:
label: Context
description: |
Provide the "why", motivation, and alternative approaches considered -- linking to previous refinement issues, spikes, Notion docs as appropriate
validations:
validations:
required: false

View File

@@ -13,7 +13,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Wrangle latest tag
id: is_latest
uses: ./.github/actions/latest-wrangler

View File

@@ -28,3 +28,10 @@ updates:
schedule:
interval: "weekly"
rebase-strategy: "disabled"
# github dependencies
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
rebase-strategy: "disabled"

View File

@@ -1,15 +1,12 @@
resolves #
[docs](https://github.com/dbt-labs/docs.getdbt.com/issues/new/choose) dbt-labs/docs.getdbt.com/#
resolves #
<!---
Include the number of the issue addressed by this PR above if applicable.
PRs for code changes without an associated issue *will not be merged*.
See CONTRIBUTING.md for more information.
Include the number of the docs issue that was opened for this PR. If
this change has no user-facing implications, "N/A" suffices instead. New
docs tickets can be created by clicking the link above or by going to
https://github.com/dbt-labs/docs.getdbt.com/issues/new/choose.
Add the `user docs` label to this PR if it will need docs changes. An
issue will get opened in docs.getdbt.com upon successful merge of this PR.
-->
### Problem
@@ -33,3 +30,4 @@ resolves #
- [ ] I have run this code in development and it appears to resolve the stated issue
- [ ] This PR includes tests, or tests are not required/relevant for this PR
- [ ] This PR has no interface changes (e.g. macros, cli, logs, json artifacts, config files, adapter interface, etc) or this PR has already received feedback and approval from Product or DX
- [ ] This PR includes [type annotations](https://docs.python.org/3/library/typing.html) for new and modified functions

View File

@@ -58,4 +58,4 @@ jobs:
commit_message: "Add automated changelog yaml from template for bot PR"
changie_kind: ${{ matrix.changie_kind }}
label: ${{ matrix.label }}
custom_changelog_string: "custom:\n Author: ${{ github.event.pull_request.user.login }}\n PR: ${{ github.event.pull_request.number }}"
custom_changelog_string: "custom:\n Author: ${{ github.event.pull_request.user.login }}\n Issue: ${{ github.event.pull_request.number }}"

View File

@@ -2,10 +2,8 @@
# Checks that a file has been committed under the /.changes directory
# as a new CHANGELOG entry. Cannot check for a specific filename as
# it is dynamically generated by change type and timestamp.
# This workflow should not require any secrets since it runs for PRs
# from forked repos.
# By default, secrets are not passed to workflows running from
# a forked repo.
# This workflow runs on pull_request_target because it requires
# secrets to post comments.
# **why?**
# Ensure code change gets reflected in the CHANGELOG.
@@ -19,7 +17,7 @@
name: Check Changelog Entry
on:
pull_request:
pull_request_target:
types: [opened, reopened, labeled, unlabeled, synchronize]
workflow_dispatch:

43
.github/workflows/docs-issue.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
# **what?**
# Open an issue in docs.getdbt.com when a PR is labeled `user docs`
# **why?**
# To reduce barriers for keeping docs up to date
# **when?**
# When a PR is labeled `user docs` and is merged. Runs on pull_request_target to run off the workflow already merged,
# not the workflow that existed on the PR branch. This allows old PRs to get comments.
name: Open issues in docs.getdbt.com repo when a PR is labeled
run-name: "Open an issue in docs.getdbt.com for PR #${{ github.event.pull_request.number }}"
on:
pull_request_target:
types: [labeled, closed]
defaults:
run:
shell: bash
permissions:
issues: write # opens new issues
pull-requests: write # comments on PRs
jobs:
open_issues:
# we only want to run this when the PR has been merged or the label in the labeled event is `user docs`. Otherwise it runs the
# risk of duplicaton of issues being created due to merge and label both triggering this workflow to run and neither having
# generating the comment before the other runs. This lives here instead of the shared workflow because this is where we
# decide if it should run or not.
if: |
(github.event.pull_request.merged == true) &&
((github.event.action == 'closed' && contains( github.event.pull_request.labels.*.name, 'user docs')) ||
(github.event.action == 'labeled' && github.event.label.name == 'user docs'))
uses: dbt-labs/actions/.github/workflows/open-issue-in-repo.yml@main
with:
issue_repository: "dbt-labs/docs.getdbt.com"
issue_title: "Docs Changes Needed from ${{ github.event.repository.name }} PR #${{ github.event.pull_request.number }}"
issue_body: "At a minimum, update body to include a link to the page on docs.getdbt.com requiring updates and what part(s) of the page you would like to see updated."
secrets: inherit

View File

@@ -33,6 +33,11 @@ defaults:
run:
shell: bash
# top-level adjustments can be made here
env:
# number of parallel processes to spawn for python integration testing
PYTHON_INTEGRATION_TEST_WORKERS: 5
jobs:
code-quality:
name: code-quality
@@ -42,10 +47,10 @@ jobs:
steps:
- name: Check out the repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.8'
@@ -69,17 +74,17 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
env:
TOXENV: "unit"
steps:
- name: Check out the repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
@@ -90,8 +95,12 @@ jobs:
python -m pip install tox
tox --version
- name: Run tox
run: tox
- name: Run unit tests
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
command: tox -e unit
- name: Get current date
if: always()
@@ -102,27 +111,60 @@ jobs:
- name: Upload Unit Test Coverage to Codecov
if: ${{ matrix.python-version == '3.11' }}
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unit
integration-metadata:
name: integration test metadata generation
runs-on: ubuntu-latest
outputs:
split-groups: ${{ steps.generate-split-groups.outputs.split-groups }}
include: ${{ steps.generate-include.outputs.include }}
steps:
- name: generate split-groups
id: generate-split-groups
run: |
MATRIX_JSON="["
for B in $(seq 1 ${{ env.PYTHON_INTEGRATION_TEST_WORKERS }}); do
MATRIX_JSON+=$(sed 's/^/"/;s/$/"/' <<< "${B}")
done
MATRIX_JSON="${MATRIX_JSON//\"\"/\", \"}"
MATRIX_JSON+="]"
echo "split-groups=${MATRIX_JSON}"
echo "split-groups=${MATRIX_JSON}" >> $GITHUB_OUTPUT
- name: generate include
id: generate-include
run: |
INCLUDE=('"python-version":"3.8","os":"windows-latest"' '"python-version":"3.8","os":"macos-12"' )
INCLUDE_GROUPS="["
for include in ${INCLUDE[@]}; do
for group in $(seq 1 ${{ env.PYTHON_INTEGRATION_TEST_WORKERS }}); do
INCLUDE_GROUPS+=$(sed 's/$/, /' <<< "{\"split-group\":\"${group}\",${include}}")
done
done
INCLUDE_GROUPS=$(echo $INCLUDE_GROUPS | sed 's/,*$//g')
INCLUDE_GROUPS+="]"
echo "include=${INCLUDE_GROUPS}"
echo "include=${INCLUDE_GROUPS}" >> $GITHUB_OUTPUT
integration:
name: integration test / python ${{ matrix.python-version }} / ${{ matrix.os }}
name: (${{ matrix.split-group }}) integration test / python ${{ matrix.python-version }} / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60
timeout-minutes: 30
needs:
- integration-metadata
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
os: [ubuntu-20.04]
include:
- python-version: 3.8
os: windows-latest
- python-version: 3.8
os: macos-latest
split-group: ${{ fromJson(needs.integration-metadata.outputs.split-groups) }}
include: ${{ fromJson(needs.integration-metadata.outputs.include) }}
env:
TOXENV: integration
DBT_INVOCATION_ENV: github-actions
@@ -137,10 +179,10 @@ jobs:
steps:
- name: Check out the repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
@@ -163,8 +205,14 @@ jobs:
python -m pip install tox
tox --version
- name: Run tests
run: tox -- --ddtrace
- name: Run integration tests
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: tox -- --ddtrace
env:
PYTEST_ADDOPTS: ${{ format('--splits {0} --group {1}', env.PYTHON_INTEGRATION_TEST_WORKERS, matrix.split-group) }}
- name: Get current date
if: always()
@@ -173,17 +221,35 @@ jobs:
CURRENT_DATE=$(date +'%Y-%m-%dT%H_%M_%S') # no colons allowed for artifacts
echo "date=$CURRENT_DATE" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: always()
with:
name: logs_${{ matrix.python-version }}_${{ matrix.os }}_${{ steps.date.outputs.date }}
name: logs_${{ matrix.python-version }}_${{ matrix.os }}_${{ matrix.split-group }}_${{ steps.date.outputs.date }}
path: ./logs
- name: Upload Integration Test Coverage to Codecov
if: ${{ matrix.python-version == '3.11' }}
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: integration
integration-report:
if: ${{ always() }}
name: Integration Test Suite
runs-on: ubuntu-latest
needs: integration
steps:
- name: "Integration Tests Failed"
if: ${{ contains(needs.integration.result, 'failure') || contains(needs.integration.result, 'cancelled') }}
# when this is true the next step won't execute
run: |
echo "::notice title='Integration test suite failed'"
exit 1
- name: "Integration Tests Passed"
run: |
echo "::notice title='Integration test suite passed'"
build:
name: build packages
@@ -192,10 +258,10 @@ jobs:
steps:
- name: Check out the repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.8'
@@ -230,7 +296,7 @@ jobs:
- name: Install source distributions
# ignore dbt-1.0.0, which intentionally raises an error when installed from source
run: |
find ./dist/dbt-[a-z]*.gz -maxdepth 1 -type f | xargs python -m pip install --force-reinstall --find-links=dist/
find ./dist/*.gz -maxdepth 1 -type f | xargs python -m pip install --force-reinstall --find-links=dist/
- name: Check source distributions
run: |

View File

@@ -48,7 +48,7 @@ jobs:
# explicitly checkout the performance runner from main regardless of which
# version we are modeling.
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: main
@@ -87,12 +87,12 @@ jobs:
# explicitly checkout the performance runner from main regardless of which
# version we are modeling.
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: main
# attempts to access a previously cached runner
- uses: actions/cache@v3
- uses: actions/cache@v4
id: cache
with:
path: ${{ env.RUNNER_CACHE_PATH }}
@@ -148,7 +148,7 @@ jobs:
echo "release_branch: ${{ needs.set-variables.outputs.release_branch }}"
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.8"
@@ -160,13 +160,13 @@ jobs:
# explicitly checkout main to get the latest project definitions
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: main
# this was built in the previous job so it will be there.
- name: Fetch Runner
uses: actions/cache@v3
uses: actions/cache@v4
id: cache
with:
path: ${{ env.RUNNER_CACHE_PATH }}
@@ -195,7 +195,7 @@ jobs:
- name: '[DEBUG] ls baseline directory after run'
run: ls -R performance/baselines/
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: baseline
path: performance/baselines/${{ needs.set-variables.outputs.release_id }}/
@@ -225,7 +225,7 @@ jobs:
echo "release_branch: ${{ needs.set-variables.outputs.release_branch }}"
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ matrix.base-branch }}
@@ -235,7 +235,7 @@ jobs:
git push origin ${{ matrix.target-branch }}
git branch --set-upstream-to=origin/${{ matrix.target-branch }} ${{ matrix.target-branch }}
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: baseline
path: performance/baselines/${{ needs.set-variables.outputs.release_id }}

View File

@@ -39,7 +39,7 @@ jobs:
steps:
- name: "Checkout ${{ github.repository }} Branch ${{ env.RELEASE_BRANCH }}"
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ env.RELEASE_BRANCH }}

View File

@@ -36,7 +36,7 @@ jobs:
latest: ${{ steps.latest.outputs.latest }}
minor_latest: ${{ steps.latest.outputs.minor_latest }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Split version
id: version
run: |
@@ -83,7 +83,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push MAJOR.MINOR.PATCH tag
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
file: docker/Dockerfile
push: True
@@ -94,7 +94,7 @@ jobs:
ghcr.io/dbt-labs/${{ github.event.inputs.package }}:${{ github.event.inputs.version_number }}
- name: Build and push MINOR.latest tag
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
if: ${{ needs.get_version_meta.outputs.minor_latest == 'True' }}
with:
file: docker/Dockerfile
@@ -106,7 +106,7 @@ jobs:
ghcr.io/dbt-labs/${{ github.event.inputs.package }}:${{ needs.get_version_meta.outputs.major }}.${{ needs.get_version_meta.outputs.minor }}.latest
- name: Build and push latest tag
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
if: ${{ needs.get_version_meta.outputs.latest == 'True' }}
with:
file: docker/Dockerfile

View File

@@ -0,0 +1,30 @@
# **what?**
# Cleanup branches left over from automation and testing. Also cleanup
# draft releases from release testing.
# **why?**
# The automations are leaving behind branches and releases that clutter
# the repository. Sometimes we need them to debug processes so we don't
# want them immediately deleted. Running on Saturday to avoid running
# at the same time as an actual release to prevent breaking a release
# mid-release.
# **when?**
# Mainly on a schedule of 12:00 Saturday.
# Manual trigger can also run on demand
name: Repository Cleanup
on:
schedule:
- cron: '0 12 * * SAT' # At 12:00 on Saturday - details in `why` above
workflow_dispatch: # for manual triggering
permissions:
contents: write
jobs:
cleanup-repo:
uses: dbt-labs/actions/.github/workflows/repository-cleanup.yml@main
secrets: inherit

View File

@@ -37,21 +37,20 @@ jobs:
steps:
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Checkout dbt repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: ${{ env.DBT_REPO_DIRECTORY }}
- name: Checkout schemas.getdbt.com repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: dbt-labs/schemas.getdbt.com
ref: 'main'
ssh-key: ${{ secrets.SCHEMA_SSH_PRIVATE_KEY }}
path: ${{ env.SCHEMA_REPO_DIRECTORY }}
- name: Generate current schema
@@ -63,27 +62,14 @@ jobs:
pip install -r dev-requirements.txt -r editable-requirements.txt
python scripts/collect-artifact-schema.py --path ${{ env.LATEST_SCHEMA_PATH }}
# Copy generated schema files into the schemas.getdbt.com repo
# Do a git diff to find any changes
# Ignore any date or version changes though
- name: Compare schemas
run: |
cp -r ${{ env.LATEST_SCHEMA_PATH }}/dbt ${{ env.SCHEMA_REPO_DIRECTORY }}
cd ${{ env.SCHEMA_REPO_DIRECTORY }}
diff_results=$(git diff -I='*[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T' \
-I='*[0-9]{1}.[0-9]{2}.[0-9]{1}(rc[0-9]|b[0-9]| )' --compact-summary)
if [[ $(echo diff_results) ]]; then
echo $diff_results
echo "Schema changes detected!"
git diff -I='*[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T' \
-I='*[0-9]{1}.[0-9]{2}.[0-9]{1}(rc[0-9]|b[0-9]| )' > ${{ env.SCHEMA_DIFF_ARTIFACT }}
exit 1
else
echo "No schema changes detected"
fi
git diff -I='*[0-9]{4}-[0-9]{2}-[0-9]{2}' -I='*[0-9]+\.[0-9]+\.[0-9]+' --exit-code > ${{ env.SCHEMA_DIFF_ARTIFACT }}
- name: Upload schema diff
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: 'schema_schanges.txt'

View File

@@ -18,11 +18,41 @@ on:
permissions: read-all
# top-level adjustments can be made here
env:
# number of parallel processes to spawn for python testing
PYTHON_INTEGRATION_TEST_WORKERS: 5
jobs:
integration-metadata:
name: integration test metadata generation
runs-on: ubuntu-latest
outputs:
split-groups: ${{ steps.generate-split-groups.outputs.split-groups }}
steps:
- name: generate split-groups
id: generate-split-groups
run: |
MATRIX_JSON="["
for B in $(seq 1 ${{ env.PYTHON_INTEGRATION_TEST_WORKERS }}); do
MATRIX_JSON+=$(sed 's/^/"/;s/$/"/' <<< "${B}")
done
MATRIX_JSON="${MATRIX_JSON//\"\"/\", \"}"
MATRIX_JSON+="]"
echo "split-groups=${MATRIX_JSON}" >> $GITHUB_OUTPUT
# run the performance measurements on the current or default branch
test-schema:
name: Test Log Schema
runs-on: ubuntu-20.04
timeout-minutes: 30
needs:
- integration-metadata
strategy:
fail-fast: false
matrix:
split-group: ${{ fromJson(needs.integration-metadata.outputs.split-groups) }}
env:
# turns warnings into errors
RUSTFLAGS: "-D warnings"
@@ -39,12 +69,12 @@ jobs:
steps:
- name: checkout dev
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.8"
@@ -64,4 +94,19 @@ jobs:
# integration tests generate a ton of logs in different files. the next step will find them all.
# we actually care if these pass, because the normal test run doesn't usually include many json log outputs
- name: Run integration tests
run: tox -e integration -- -nauto
uses: nick-fields/retry@v3
with:
timeout_minutes: 30
max_attempts: 3
command: tox -e integration -- -nauto
env:
PYTEST_ADDOPTS: ${{ format('--splits {0} --group {1}', env.PYTHON_INTEGRATION_TEST_WORKERS, matrix.split-group) }}
test-schema-report:
name: Log Schema Test Suite
runs-on: ubuntu-latest
needs: test-schema
steps:
- name: "[Notification] Log test suite passes"
run: |
echo "::notice title="Log test suite passes""

View File

@@ -36,7 +36,7 @@ on:
type: choice
options:
- 'ubuntu-latest'
- 'macos-latest'
- 'macos-12'
- 'windows-latest'
num_runs_per_batch:
description: 'Max number of times to run the test per batch. We always run 10 batches.'
@@ -83,12 +83,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}
- name: "Setup Python"
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "${{ inputs.python_version }}"
@@ -101,7 +101,7 @@ jobs:
# mac and windows don't use make due to limitations with docker with those runners in GitHub
- name: "Set up postgres (macos)"
if: inputs.os == 'macos-latest'
if: inputs.os == 'macos-12'
uses: ./.github/actions/setup-postgres-macos
- name: "Set up postgres (windows)"

View File

@@ -37,7 +37,7 @@ repos:
alias: flake8-check
stages: [manual]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.3.0
rev: v1.4.1
hooks:
- id: mypy
# N.B.: Mypy is... a bit fragile.

View File

@@ -5,6 +5,336 @@
- "Breaking changes" listed under a version may require action from end users or external maintainers when upgrading to that version.
- Do not edit this file directly. This file is auto-generated using [changie](https://github.com/miniscruff/changie). For details on how to document a change, see [the contributing guide](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md#adding-changelog-entry)
## dbt-core 1.7.17 - June 20, 2024
### Docs
- Fix npm security vulnerabilities as of June 2024 ([dbt-docs/#513](https://github.com/dbt-labs/dbt-docs/issues/513))
## dbt-core 1.7.16 - June 05, 2024
### Features
- Add --host flag to dbt docs serve, defaulting to '127.0.0.1' ([#10229](https://github.com/dbt-labs/dbt-core/issues/10229))
## dbt-core 1.7.15 - May 22, 2024
### Fixes
- Fix the semicolon semantics for indexes while respecting other bug fix ([#85](https://github.com/dbt-labs/dbt-core/issues/85))
### Security
- Explicitly bind to localhost in docs serve ([#10209](https://github.com/dbt-labs/dbt-core/issues/10209))
## dbt-core 1.7.14 - May 02, 2024
### Features
- Move flags from UserConfig in profiles.yml to flags in dbt_project.yml ([#9183](https://github.com/dbt-labs/dbt-core/issues/9183))
- Add require_explicit_package_overrides_for_builtin_materializations to dbt_project.yml flags, which can be used to opt-out of overriding built-in materializations from packages ([#10007](https://github.com/dbt-labs/dbt-core/issues/10007))
### Fixes
- remove materialized views from renambeable relation and remove a quote ([#127](https://github.com/dbt-labs/dbt-core/issues/127))
- Replace usage of `Set` with `List` to fix issue with index updates intermittently happening out of order ([#72](https://github.com/dbt-labs/dbt-core/issues/72))
### Under the Hood
- Raise deprecation warning if installed package overrides built-in materialization ([#9971](https://github.com/dbt-labs/dbt-core/issues/9971))
- Remove the final underscore from secret environment variable constants. ([#10052](https://github.com/dbt-labs/dbt-core/issues/10052))
## dbt-core 1.7.13 - April 18, 2024
### Security
- Bump sqlparse to >=0.5.0, <0.6.0 to address GHSA-2m57-hf25-phgg ([#9951](https://github.com/dbt-labs/dbt-core/pull/9951))
### Contributors
- [@emmoop](https://github.com/emmoop) ([#9951](https://github.com/dbt-labs/dbt-core/pull/9951))
## dbt-core 1.7.12 - April 16, 2024
### Fixes
- Fix assorted source freshness edgecases so check is run or actionable information is given ([#9078](https://github.com/dbt-labs/dbt-core/issues/9078))
- Exclude password-like fields for considering reparse ([#9795](https://github.com/dbt-labs/dbt-core/issues/9795))
## dbt-core 1.7.11 - March 28, 2024
### Fixes
- Tighten exception handling to avoid worker thread hangs. ([#9583](https://github.com/dbt-labs/dbt-core/issues/9583))
- Add field wrapper to BaseRelation members that were missing it. ([#9681](https://github.com/dbt-labs/dbt-core/issues/9681))
## dbt-core 1.7.10 - March 14, 2024
### Fixes
- Do not add duplicate input_measures ([#9360](https://github.com/dbt-labs/dbt-core/issues/9360))
- Fix partial parsing `KeyError` on deleted schema files ([#8860](https://github.com/dbt-labs/dbt-core/issues/8860))
- Support saved queries in `dbt list` ([#9532](https://github.com/dbt-labs/dbt-core/issues/9532))
### Dependencies
- Restrict protobuf to 4.* versions ([#9566](https://github.com/dbt-labs/dbt-core/pull/9566))
## dbt-core 1.7.9 - February 28, 2024
### Fixes
- Fix node_info contextvar handling so incorrect node_info doesn't persist ([#8866](https://github.com/dbt-labs/dbt-core/issues/8866))
- Add target-path to retry ([#8948](https://github.com/dbt-labs/dbt-core/issues/8948))
### Under the Hood
- Make dbt-core compatible with Python 3.12 ([#9007](https://github.com/dbt-labs/dbt-core/issues/9007))
- Restrict protobuf to major version 4. ([#9566](https://github.com/dbt-labs/dbt-core/issues/9566))
### Security
- Update Jinja2 to >= 3.1.3 to address CVE-2024-22195 ([#CVE-2024-22195](https://github.com/dbt-labs/dbt-core/pull/CVE-2024-22195))
### Contributors
- [@l1xnan](https://github.com/l1xnan) ([#9007](https://github.com/dbt-labs/dbt-core/issues/9007))
## dbt-core 1.7.8 - February 14, 2024
### Fixes
- When patching versioned models, set constraints after config ([#9364](https://github.com/dbt-labs/dbt-core/issues/9364))
- Store node_info in node associated logging events ([#9557](https://github.com/dbt-labs/dbt-core/issues/9557))
## dbt-core 1.7.7 - February 01, 2024
### Fixes
- Fix seed and source selection in `dbt docs generate` ([#9161](https://github.com/dbt-labs/dbt-core/issues/9161))
- Add TestGenerateCatalogWithExternalNodes, include empty nodes in node selection during docs generate ([#9456](https://github.com/dbt-labs/dbt-core/issues/9456))
## dbt-core 1.7.6 - January 25, 2024
### Fixes
- Handle unknown `type_code` for model contracts ([#8877](https://github.com/dbt-labs/dbt-core/issues/8877), [#8353](https://github.com/dbt-labs/dbt-core/issues/8353))
- Fix retry command run from CLI ([#9444](https://github.com/dbt-labs/dbt-core/issues/9444))
## dbt-core 1.7.5 - January 18, 2024
### Fixes
- Preserve the value of vars and the --full-refresh flags when using retry. ([#9112](https://github.com/dbt-labs/dbt-core/issues/9112))
### Contributors
- [@peterallenwebb,](https://github.com/peterallenwebb,) ([#9112](https://github.com/dbt-labs/dbt-core/issues/9112))
## dbt-core 1.7.4 - December 14, 2023
### Features
- Adds support for parsing conversion metric related properties for the semantic layer. ([#9203](https://github.com/dbt-labs/dbt-core/issues/9203))
### Fixes
- Ensure we produce valid jsonschema schemas for manifest, catalog, run-results, and sources ([#8991](https://github.com/dbt-labs/dbt-core/issues/8991))
### Contributors
- [@WilliamDee](https://github.com/WilliamDee) ([#9203](https://github.com/dbt-labs/dbt-core/issues/9203))
## dbt-core 1.7.3 - November 29, 2023
### Fixes
- deps: Lock git packages to commit SHA during resolution ([#9050](https://github.com/dbt-labs/dbt-core/issues/9050))
- deps: Use PackageRenderer to read package-lock.json ([#9127](https://github.com/dbt-labs/dbt-core/issues/9127))
- Get sources working again in dbt docs generate ([#9119](https://github.com/dbt-labs/dbt-core/issues/9119))
## dbt-core 1.7.2 - November 16, 2023
### Features
- Support setting export configs hierarchically via saved query and project configs ([#8956](https://github.com/dbt-labs/dbt-core/issues/8956))
### Fixes
- Fix formatting of tarball information in packages-lock.yml ([#9062](https://github.com/dbt-labs/dbt-core/issues/9062))
### Under the Hood
- Treat SystemExit as an interrupt if raised during node execution. ([#n/a](https://github.com/dbt-labs/dbt-core/issues/n/a))
### Contributors
- [@benmosher](https://github.com/benmosher) ([#n/a](https://github.com/dbt-labs/dbt-core/issues/n/a))
## dbt-core 1.7.1 - November 07, 2023
### Fixes
- Fix compilation exception running empty seed file and support new Integer agate data_type ([#8895](https://github.com/dbt-labs/dbt-core/issues/8895))
- Update run_results.json from previous versions of dbt to support deferral and rerun from failure ([#9010](https://github.com/dbt-labs/dbt-core/issues/9010))
- Use MANIFEST.in to recursively include all jinja templates; fixes issue where some templates were not included in the distribution ([#9016](https://github.com/dbt-labs/dbt-core/issues/9016))
- Fix git repository with subdirectory for Deps ([#9000](https://github.com/dbt-labs/dbt-core/issues/9000))
## dbt-core 1.7.0 - November 02, 2023
### Breaking Changes
- Removed the FirstRunResultError and AfterFirstRunResultError event types, using the existing RunResultError in their place. ([#7963](https://github.com/dbt-labs/dbt-core/issues/7963))
### Features
- add log file of installed packages via dbt deps ([#6643](https://github.com/dbt-labs/dbt-core/issues/6643))
- Enable re-population of metadata vars post-environment change during programmatic invocation ([#8010](https://github.com/dbt-labs/dbt-core/issues/8010))
- Added support to configure a delimiter for a seed file, defaults to comma ([#3990](https://github.com/dbt-labs/dbt-core/issues/3990))
- Allow specification of `create_metric: true` on measures ([#8125](https://github.com/dbt-labs/dbt-core/issues/8125))
- Add node attributes related to compilation to run_results.json ([#7519](https://github.com/dbt-labs/dbt-core/issues/7519))
- Add --no-inject-ephemeral-ctes flag for `compile` command, for usage by linting. ([#8480](https://github.com/dbt-labs/dbt-core/issues/8480))
- Support configuration of semantic models with the addition of enable/disable and group enablement. ([#7968](https://github.com/dbt-labs/dbt-core/issues/7968))
- Accept a `dbt-cloud` config in dbt_project.yml ([#8438](https://github.com/dbt-labs/dbt-core/issues/8438))
- Support atomic replace in the global replace macro ([#8539](https://github.com/dbt-labs/dbt-core/issues/8539))
- Use translate_type on data_type in model.columns in templates by default, remove no op `TYPE_LABELS` ([#8007](https://github.com/dbt-labs/dbt-core/issues/8007))
- Add an option to generate static documentation ([#8614](https://github.com/dbt-labs/dbt-core/issues/8614))
- Allow setting "access" as a config in addition to as a property ([#8383](https://github.com/dbt-labs/dbt-core/issues/8383))
- Loosen typing requirement on renameable/replaceable relations to Iterable to allow adapters more flexibility in registering relation types, include docstrings as suggestions ([#8647](https://github.com/dbt-labs/dbt-core/issues/8647))
- Add support for optional label in semantic_models, measures, dimensions and entities. ([#8595](https://github.com/dbt-labs/dbt-core/issues/8595), [#8755](https://github.com/dbt-labs/dbt-core/issues/8755))
- Allow adapters to include package logs in dbt standard logging ([#7859](https://github.com/dbt-labs/dbt-core/issues/7859))
- Support storing test failures as views ([#6914](https://github.com/dbt-labs/dbt-core/issues/6914))
- resolve packages with same git repo and unique subdirectory ([#5374](https://github.com/dbt-labs/dbt-core/issues/5374))
- Add new ResourceReport event to record memory/cpu/io metrics ([#8342](https://github.com/dbt-labs/dbt-core/issues/8342))
- Adding `date_spine` macro (and supporting macros) from dbt-utils to dbt-core ([#8172](https://github.com/dbt-labs/dbt-core/issues/8172))
- Support `fill_nulls_with` and `join_to_timespine` for metric nodes ([#8593](https://github.com/dbt-labs/dbt-core/issues/8593), [#8755](https://github.com/dbt-labs/dbt-core/issues/8755))
- Raise a warning when a contracted model has a numeric field without scale defined ([#8183](https://github.com/dbt-labs/dbt-core/issues/8183))
- Added support for retrieving partial catalog information from a schema ([#8521](https://github.com/dbt-labs/dbt-core/issues/8521))
- Add meta attribute to SemanticModels config ([#8511](https://github.com/dbt-labs/dbt-core/issues/8511))
- Selectors with docs generate limits catalog generation ([#6014](https://github.com/dbt-labs/dbt-core/issues/6014))
- Allow freshness to be determined via DBMS metadata for supported adapters ([#8704](https://github.com/dbt-labs/dbt-core/issues/8704))
- Add support semantic layer SavedQuery node type ([#8594](https://github.com/dbt-labs/dbt-core/issues/8594))
- Add exports to SavedQuery spec ([#8892](https://github.com/dbt-labs/dbt-core/issues/8892))
### Fixes
- Copy dir during `dbt deps` if symlink fails ([#7428](https://github.com/dbt-labs/dbt-core/issues/7428), [#8223](https://github.com/dbt-labs/dbt-core/issues/8223))
- If --profile specified with dbt-init, create the project with the specified profile ([#6154](https://github.com/dbt-labs/dbt-core/issues/6154))
- Fixed double-underline ([#5301](https://github.com/dbt-labs/dbt-core/issues/5301))
- Copy target_schema from config into snapshot node ([#6745](https://github.com/dbt-labs/dbt-core/issues/6745))
- Enable converting deprecation warnings to errors ([#8130](https://github.com/dbt-labs/dbt-core/issues/8130))
- Add status to Parse Inline Error ([#8173](https://github.com/dbt-labs/dbt-core/issues/8173))
- Ensure `warn_error_options` get serialized in `invocation_args_dict` ([#7694](https://github.com/dbt-labs/dbt-core/issues/7694))
- Stop detecting materialization macros based on macro name ([#6231](https://github.com/dbt-labs/dbt-core/issues/6231))
- Update `dbt deps` download retry logic to handle `EOFError` exceptions ([#6653](https://github.com/dbt-labs/dbt-core/issues/6653))
- Improve handling of CTE injection with ephemeral models ([#8213](https://github.com/dbt-labs/dbt-core/issues/8213))
- Fix unbound local variable error in `checked_agg_time_dimension_for_measure` ([#8230](https://github.com/dbt-labs/dbt-core/issues/8230))
- Ensure runtime errors are raised for graph runnable tasks (compile, show, run, etc) ([#8166](https://github.com/dbt-labs/dbt-core/issues/8166))
- Fix retry not working with log-file-max-bytes ([#8297](https://github.com/dbt-labs/dbt-core/issues/8297))
- Add explicit support for integers for the show command ([#8153](https://github.com/dbt-labs/dbt-core/issues/8153))
- Detect changes to model access, version, or latest_version in state:modified ([#8189](https://github.com/dbt-labs/dbt-core/issues/8189))
- Add connection status into list of statuses for dbt debug ([#8350](https://github.com/dbt-labs/dbt-core/issues/8350))
- fix fqn-selection for external versioned models ([#8374](https://github.com/dbt-labs/dbt-core/issues/8374))
- Fix: DbtInternalError after model that previously ref'd external model is deleted ([#8375](https://github.com/dbt-labs/dbt-core/issues/8375))
- Fix using list command with path selector and project-dir ([#8385](https://github.com/dbt-labs/dbt-core/issues/8385))
- Remedy performance regression by only writing run_results.json once. ([#8360](https://github.com/dbt-labs/dbt-core/issues/8360))
- Add support for swapping materialized views with tables/views and vice versa ([#8449](https://github.com/dbt-labs/dbt-core/issues/8449))
- Turn breaking changes to contracted models into warnings for unversioned models ([#8384](https://github.com/dbt-labs/dbt-core/issues/8384), [#8282](https://github.com/dbt-labs/dbt-core/issues/8282))
- Ensure parsing does not break when `window_groupings` is not specified for `non_additive_dimension` ([#8453](https://github.com/dbt-labs/dbt-core/issues/8453))
- fix ambiguous reference error for tests and versions when model name is duplicated across packages ([#8327](https://github.com/dbt-labs/dbt-core/issues/8327), [#8493](https://github.com/dbt-labs/dbt-core/issues/8493))
- Fix "Internal Error: Expected node <unique-id> not found in manifest" when depends_on set on ModelNodeArgs ([#8506](https://github.com/dbt-labs/dbt-core/issues/8506))
- Fix snapshot success message ([#7583](https://github.com/dbt-labs/dbt-core/issues/7583))
- Parse the correct schema version from manifest ([#8544](https://github.com/dbt-labs/dbt-core/issues/8544))
- make version comparison insensitive to order ([#8571](https://github.com/dbt-labs/dbt-core/issues/8571))
- Update metric helper functions to work with new semantic layer metrics ([#8134](https://github.com/dbt-labs/dbt-core/issues/8134))
- Disallow cleaning paths outside current working directory ([#8318](https://github.com/dbt-labs/dbt-core/issues/8318))
- Warn when --state == --target ([#8160](https://github.com/dbt-labs/dbt-core/issues/8160))
- update dbt show to include limit in DWH query ([#8496,](https://github.com/dbt-labs/dbt-core/issues/8496,), [#8417](https://github.com/dbt-labs/dbt-core/issues/8417))
- Support quoted parameter list for MultiOption CLI options. ([#8598](https://github.com/dbt-labs/dbt-core/issues/8598))
- Support global flags passed in after subcommands ([#6497](https://github.com/dbt-labs/dbt-core/issues/6497))
- Lower bound of `8.0.2` for `click` ([#8683](https://github.com/dbt-labs/dbt-core/issues/8683))
- Fixes test type edges filter ([#8692](https://github.com/dbt-labs/dbt-core/issues/8692))
- semantic models in graph selection ([#8589](https://github.com/dbt-labs/dbt-core/issues/8589))
- Support doc blocks in nested semantic model YAML ([#8509](https://github.com/dbt-labs/dbt-core/issues/8509))
- avoid double-rendering sql_header in dbt show ([#8739](https://github.com/dbt-labs/dbt-core/issues/8739))
- Fix tag selection for projects with semantic models ([#8749](https://github.com/dbt-labs/dbt-core/issues/8749))
- Foreign key constraint on incremental model results in Database Error ([#8022](https://github.com/dbt-labs/dbt-core/issues/8022))
- Support docs blocks on versioned model column descriptions ([#8540](https://github.com/dbt-labs/dbt-core/issues/8540))
- Enable seeds to be handled from stored manifest data ([#6875](https://github.com/dbt-labs/dbt-core/issues/6875))
- Override path-like args in dbt retry ([#8682](https://github.com/dbt-labs/dbt-core/issues/8682))
- Group updates on unmodified nodes are handled gracefully for state:modified ([#8371](https://github.com/dbt-labs/dbt-core/issues/8371))
- Partial parsing fix for adding groups and updating models at the same time ([#8697](https://github.com/dbt-labs/dbt-core/issues/8697))
- Fix partial parsing not working for semantic model change ([#8859](https://github.com/dbt-labs/dbt-core/issues/8859))
- Rework get_catalog implementation to retain previous adapter interface semantics ([#8846](https://github.com/dbt-labs/dbt-core/issues/8846))
- Add back contract enforcement for temporary tables on postgres ([#8857](https://github.com/dbt-labs/dbt-core/issues/8857))
- Add version to fqn when version==0 ([#8836](https://github.com/dbt-labs/dbt-core/issues/8836))
- Fix cased comparison in catalog-retrieval function. ([#8939](https://github.com/dbt-labs/dbt-core/issues/8939))
- Catalog queries now assign the correct type to materialized views ([#8864](https://github.com/dbt-labs/dbt-core/issues/8864))
- Make relation filtering None-tolerant for maximal flexibility across adapters. ([#8974](https://github.com/dbt-labs/dbt-core/issues/8974))
### Docs
- Corrected spelling of "Partiton" ([dbt-docs/#8100](https://github.com/dbt-labs/dbt-docs/issues/8100))
- Remove static SQL codeblock for metrics ([dbt-docs/#436](https://github.com/dbt-labs/dbt-docs/issues/436))
- fixed comment util.py ([dbt-docs/#None](https://github.com/dbt-labs/dbt-docs/issues/None))
- Fix newline escapes and improve formatting in docker README ([dbt-docs/#8211](https://github.com/dbt-labs/dbt-docs/issues/8211))
- Display contract and column constraints on the model page ([dbt-docs/#433](https://github.com/dbt-labs/dbt-docs/issues/433))
- Display semantic model details in docs ([dbt-docs/#431](https://github.com/dbt-labs/dbt-docs/issues/431))
### Under the Hood
- Switch from hologram to mashumaro jsonschema ([#8426](https://github.com/dbt-labs/dbt-core/issues/8426))
- Refactor flaky test pp_versioned_models ([#7781](https://github.com/dbt-labs/dbt-core/issues/7781))
- format exception from dbtPlugin.initialize ([#8152](https://github.com/dbt-labs/dbt-core/issues/8152))
- A way to control maxBytes for a single dbt.log file ([#8199](https://github.com/dbt-labs/dbt-core/issues/8199))
- Ref expressions with version can now be processed by the latest version of the high-performance dbt-extractor library. ([#7688](https://github.com/dbt-labs/dbt-core/issues/7688))
- Bump manifest schema version to v11, freeze manifest v10 ([#8333](https://github.com/dbt-labs/dbt-core/issues/8333))
- add tracking for plugin.get_nodes calls ([#8344](https://github.com/dbt-labs/dbt-core/issues/8344))
- add internal flag: --no-partial-parse-file-diff to inform whether to compute a file diff during partial parsing ([#8363](https://github.com/dbt-labs/dbt-core/issues/8363))
- Add return values to a number of functions for mypy ([#8389](https://github.com/dbt-labs/dbt-core/issues/8389))
- Fix mypy warnings for ManifestLoader.load() ([#8401](https://github.com/dbt-labs/dbt-core/issues/8401))
- Use python version 3.10.7 in Docker image. ([#8444](https://github.com/dbt-labs/dbt-core/issues/8444))
- Re-organize jinja macros: relation-specific in /macros/adapters/relations/<relation>, relation agnostic in /macros/relations ([#8449](https://github.com/dbt-labs/dbt-core/issues/8449))
- Update typing to meet mypy standards ([#8396](https://github.com/dbt-labs/dbt-core/issues/8396))
- Mypy errors - adapters/factory.py ([#8387](https://github.com/dbt-labs/dbt-core/issues/8387))
- Added more type annotations. ([#8537](https://github.com/dbt-labs/dbt-core/issues/8537))
- Audit potential circular dependencies ([#8349](https://github.com/dbt-labs/dbt-core/issues/8349))
- Add functional test for advanced ref override ([#8566](https://github.com/dbt-labs/dbt-core/issues/8566))
- Add typing to __init__ in base.py ([#8398](https://github.com/dbt-labs/dbt-core/issues/8398))
- Fix untyped functions in task/runnable.py (mypy warning) ([#8402](https://github.com/dbt-labs/dbt-core/issues/8402))
- add a test for ephemeral cte injection ([#8225](https://github.com/dbt-labs/dbt-core/issues/8225))
- Fix test_numeric_values to look for more specific strings ([#8470](https://github.com/dbt-labs/dbt-core/issues/8470))
- Pin types-requests<2.31.0 in `dev-requirements.txt` ([#8789](https://github.com/dbt-labs/dbt-core/issues/8789))
- Add warning_tag to UnversionedBreakingChange ([#8827](https://github.com/dbt-labs/dbt-core/issues/8827))
- Update v10 manifest schema to match 1.6 for testing schema compatibility ([#8835](https://github.com/dbt-labs/dbt-core/issues/8835))
- Add a no-op runner for Saved Qeury ([#8893](https://github.com/dbt-labs/dbt-core/issues/8893))
### Dependencies
- Bump mypy from 1.3.0 to 1.4.0 ([#7912](https://github.com/dbt-labs/dbt-core/pull/7912))
- Bump mypy from 1.4.0 to 1.4.1 ([#8219](https://github.com/dbt-labs/dbt-core/pull/8219))
- Update pin for click<9 ([#8232](https://github.com/dbt-labs/dbt-core/pull/8232))
- Add upper bound to sqlparse pin of <0.5 ([#8236](https://github.com/dbt-labs/dbt-core/pull/8236))
- Support dbt-semantic-interfaces 0.2.0 ([#8250](https://github.com/dbt-labs/dbt-core/pull/8250))
- Bump docker/build-push-action from 4 to 5 ([#8783](https://github.com/dbt-labs/dbt-core/pull/8783))
- Upgrade dbt-semantic-interfaces dep to 0.3.0 ([#8819](https://github.com/dbt-labs/dbt-core/pull/8819))
- Begin using DSI 0.4.x ([#8892](https://github.com/dbt-labs/dbt-core/pull/8892))
### Contributors
- [@anjutiwari](https://github.com/anjutiwari) ([#7428](https://github.com/dbt-labs/dbt-core/issues/7428), [#8223](https://github.com/dbt-labs/dbt-core/issues/8223))
- [@benmosher](https://github.com/benmosher) ([#8480](https://github.com/dbt-labs/dbt-core/issues/8480))
- [@d-kaneshiro](https://github.com/d-kaneshiro) ([#None](https://github.com/dbt-labs/dbt-core/issues/None))
- [@dave-connors-3](https://github.com/dave-connors-3) ([#8153](https://github.com/dbt-labs/dbt-core/issues/8153), [#8589](https://github.com/dbt-labs/dbt-core/issues/8589))
- [@dylan-murray](https://github.com/dylan-murray) ([#8683](https://github.com/dbt-labs/dbt-core/issues/8683))
- [@ezraerb](https://github.com/ezraerb) ([#6154](https://github.com/dbt-labs/dbt-core/issues/6154))
- [@gem7318](https://github.com/gem7318) ([#8010](https://github.com/dbt-labs/dbt-core/issues/8010))
- [@jamezrin](https://github.com/jamezrin) ([#8211](https://github.com/dbt-labs/dbt-core/issues/8211))
- [@jusbaldw](https://github.com/jusbaldw) ([#6643](https://github.com/dbt-labs/dbt-core/issues/6643))
- [@lllong33](https://github.com/lllong33) ([#5301](https://github.com/dbt-labs/dbt-core/issues/5301))
- [@marcodamore](https://github.com/marcodamore) ([#436](https://github.com/dbt-labs/dbt-core/issues/436))
- [@mescanne](https://github.com/mescanne) ([#8614](https://github.com/dbt-labs/dbt-core/issues/8614))
- [@pgoslatara](https://github.com/pgoslatara) ([#8100](https://github.com/dbt-labs/dbt-core/issues/8100))
- [@philippeboyd](https://github.com/philippeboyd) ([#5374](https://github.com/dbt-labs/dbt-core/issues/5374))
- [@ramonvermeulen](https://github.com/ramonvermeulen) ([#3990](https://github.com/dbt-labs/dbt-core/issues/3990))
- [@renanleme](https://github.com/renanleme) ([#8692](https://github.com/dbt-labs/dbt-core/issues/8692))
## Previous Releases
For information on prior major and minor releases, see their changelogs:

View File

@@ -0,0 +1,13 @@
ignore:
- ".github"
- ".changes"
coverage:
status:
project:
default:
target: auto
threshold: 0.1% # Reduce noise by ignoring rounding errors in coverage drops
patch:
default:
target: auto
threshold: 80%

View File

@@ -7,12 +7,12 @@ from dbt.exceptions import DbtRuntimeError
@dataclass
class Column:
# Note: This is automatically used by contract code
# No-op conversions (INTEGER => INT) have been removed.
# Any adapter that wants to take advantage of "translate_type"
# should create a ClassVar with the appropriate conversions.
TYPE_LABELS: ClassVar[Dict[str, str]] = {
"STRING": "TEXT",
"TIMESTAMP": "TIMESTAMP",
"FLOAT": "FLOAT",
"INTEGER": "INT",
"BOOLEAN": "BOOLEAN",
}
column: str
dtype: str

View File

@@ -72,7 +72,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
TYPE: str = NotImplemented
def __init__(self, profile: AdapterRequiredConfig):
def __init__(self, profile: AdapterRequiredConfig) -> None:
self.profile = profile
self.thread_connections: Dict[Hashable, Connection] = {}
self.lock: RLock = flags.MP_CONTEXT.RLock()
@@ -400,7 +400,7 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
@abc.abstractmethod
def execute(
self, sql: str, auto_begin: bool = False, fetch: bool = False
self, sql: str, auto_begin: bool = False, fetch: bool = False, limit: Optional[int] = None
) -> Tuple[AdapterResponse, agate.Table]:
"""Execute the given SQL.
@@ -408,7 +408,28 @@ class BaseConnectionManager(metaclass=abc.ABCMeta):
:param bool auto_begin: If set, and dbt is not currently inside a
transaction, automatically begin one.
:param bool fetch: If set, fetch results.
:param int limit: If set, limits the result set
:return: A tuple of the query status and results (empty if fetch=False).
:rtype: Tuple[AdapterResponse, agate.Table]
"""
raise dbt.exceptions.NotImplementedError("`execute` is not implemented for this adapter!")
def add_select_query(self, sql: str) -> Tuple[Connection, Any]:
"""
This was added here because base.impl.BaseAdapter.get_column_schema_from_query expects it to be here.
That method wouldn't work unless the adapter used sql.impl.SQLAdapter, sql.connections.SQLConnectionManager
or defined this method on <Adapter>ConnectionManager before passing it in to <Adapter>Adapter.
See https://github.com/dbt-labs/dbt-core/issues/8396 for more information.
"""
raise dbt.exceptions.NotImplementedError(
"`add_select_query` is not implemented for this adapter!"
)
@classmethod
def data_type_code_to_name(cls, type_code: Union[int, str]) -> str:
"""Get the string representation of the data type from the type_code."""
# https://peps.python.org/pep-0249/#type-objects
raise dbt.exceptions.NotImplementedError(
"`data_type_code_to_name` is not implemented for this adapter!"
)

View File

@@ -17,9 +17,11 @@ from typing import (
Set,
Tuple,
Type,
TypedDict,
Union,
)
from dbt.adapters.capability import Capability, CapabilityDict
from dbt.contracts.graph.nodes import ColumnLevelConstraint, ConstraintType, ModelLevelConstraint
import agate
@@ -43,8 +45,14 @@ from dbt.exceptions import (
UnexpectedNullError,
)
from dbt.adapters.protocol import AdapterConfig, ConnectionManagerProtocol
from dbt.clients.agate_helper import empty_table, merge_tables, table_from_rows
from dbt.adapters.protocol import AdapterConfig
from dbt.clients.agate_helper import (
empty_table,
get_column_value_uncased,
merge_tables,
table_from_rows,
Integer,
)
from dbt.clients.jinja import MacroGenerator
from dbt.contracts.graph.manifest import Manifest, MacroManifest
from dbt.contracts.graph.nodes import ResultNode
@@ -60,7 +68,7 @@ from dbt.events.types import (
)
from dbt.utils import filter_null_values, executor, cast_to_str, AttrDict
from dbt.adapters.base.connections import Connection, AdapterResponse
from dbt.adapters.base.connections import Connection, AdapterResponse, BaseConnectionManager
from dbt.adapters.base.meta import AdapterMeta, available
from dbt.adapters.base.relation import (
ComponentName,
@@ -74,7 +82,9 @@ from dbt.adapters.cache import RelationsCache, _make_ref_key_dict
from dbt import deprecations
GET_CATALOG_MACRO_NAME = "get_catalog"
GET_CATALOG_RELATIONS_MACRO_NAME = "get_catalog_relations"
FRESHNESS_MACRO_NAME = "collect_freshness"
GET_RELATION_LAST_MODIFIED_MACRO_NAME = "get_relation_last_modified"
class ConstraintSupport(str, Enum):
@@ -109,7 +119,7 @@ def _catalog_filter_schemas(manifest: Manifest) -> Callable[[agate.Row], bool]:
return test
def _utc(dt: Optional[datetime], source: BaseRelation, field_name: str) -> datetime:
def _utc(dt: Optional[datetime], source: Optional[BaseRelation], field_name: str) -> datetime:
"""If dt has a timezone, return a new datetime that's in UTC. Otherwise,
assume the datetime is already for UTC and add the timezone.
"""
@@ -161,6 +171,12 @@ class PythonJobHelper:
raise NotImplementedError("PythonJobHelper submit function is not implemented yet")
class FreshnessResponse(TypedDict):
max_loaded_at: datetime
snapshotted_at: datetime
age: float # age in seconds
class BaseAdapter(metaclass=AdapterMeta):
"""The BaseAdapter provides an abstract base class for adapters.
@@ -208,7 +224,7 @@ class BaseAdapter(metaclass=AdapterMeta):
Relation: Type[BaseRelation] = BaseRelation
Column: Type[BaseColumn] = BaseColumn
ConnectionManager: Type[ConnectionManagerProtocol]
ConnectionManager: Type[BaseConnectionManager]
# A set of clobber config fields accepted by this adapter
# for use in materializations
@@ -222,7 +238,11 @@ class BaseAdapter(metaclass=AdapterMeta):
ConstraintType.foreign_key: ConstraintSupport.ENFORCED,
}
def __init__(self, config):
# This static member variable can be overriden in concrete adapter
# implementations to indicate adapter support for optional capabilities.
_capabilities = CapabilityDict({})
def __init__(self, config) -> None:
self.config = config
self.cache = RelationsCache()
self.connections = self.ConnectionManager(config)
@@ -315,14 +335,21 @@ class BaseAdapter(metaclass=AdapterMeta):
@available.parse(lambda *a, **k: ("", empty_table()))
def get_partitions_metadata(self, table: str) -> Tuple[agate.Table]:
"""Obtain partitions metadata for a BigQuery partitioned table.
"""
TODO: Can we move this to dbt-bigquery?
Obtain partitions metadata for a BigQuery partitioned table.
:param str table_id: a partitioned table id, in standard SQL format.
:param str table: a partitioned table id, in standard SQL format.
:return: a partition metadata tuple, as described in
https://cloud.google.com/bigquery/docs/creating-partitioned-tables#getting_partition_metadata_using_meta_tables.
:rtype: agate.Table
"""
return self.connections.get_partitions_metadata(table=table)
if hasattr(self.connections, "get_partitions_metadata"):
return self.connections.get_partitions_metadata(table=table)
else:
raise NotImplementedError(
"`get_partitions_metadata` is not implemented for this adapter!"
)
###
# Methods that should never be overridden
@@ -408,7 +435,30 @@ class BaseAdapter(metaclass=AdapterMeta):
lowercase strings.
"""
info_schema_name_map = SchemaSearchMap()
nodes: Iterator[ResultNode] = chain(
relations = self._get_catalog_relations(manifest)
for relation in relations:
info_schema_name_map.add(relation)
# result is a map whose keys are information_schema Relations without
# identifiers that have appropriate database prefixes, and whose values
# are sets of lowercase schema names that are valid members of those
# databases
return info_schema_name_map
def _get_catalog_relations_by_info_schema(
self, relations
) -> Dict[InformationSchema, List[BaseRelation]]:
relations_by_info_schema: Dict[InformationSchema, List[BaseRelation]] = dict()
for relation in relations:
info_schema = relation.information_schema_only()
if info_schema not in relations_by_info_schema:
relations_by_info_schema[info_schema] = []
relations_by_info_schema[info_schema].append(relation)
return relations_by_info_schema
def _get_catalog_relations(self, manifest: Manifest) -> List[BaseRelation]:
nodes = chain(
[
node
for node in manifest.nodes.values()
@@ -416,14 +466,9 @@ class BaseAdapter(metaclass=AdapterMeta):
],
manifest.sources.values(),
)
for node in nodes:
relation = self.Relation.create_from(self.config, node)
info_schema_name_map.add(relation)
# result is a map whose keys are information_schema Relations without
# identifiers that have appropriate database prefixes, and whose values
# are sets of lowercase schema names that are valid members of those
# databases
return info_schema_name_map
relations = [self.Relation.create_from(self.config, n) for n in nodes]
return relations
def _relations_cache_for_schemas(
self, manifest: Manifest, cache_schemas: Optional[Set[BaseRelation]] = None
@@ -453,9 +498,10 @@ class BaseAdapter(metaclass=AdapterMeta):
# it's possible that there were no relations in some schemas. We want
# to insert the schemas we query into the cache's `.schemas` attribute
# so we can check it later
cache_update: Set[Tuple[Optional[str], Optional[str]]] = set()
cache_update: Set[Tuple[Optional[str], str]] = set()
for relation in cache_schemas:
cache_update.add((relation.database, relation.schema))
if relation.schema:
cache_update.add((relation.database, relation.schema))
self.cache.update_schemas(cache_update)
def set_relations_cache(
@@ -917,6 +963,17 @@ class BaseAdapter(metaclass=AdapterMeta):
"""
raise NotImplementedError("`convert_number_type` is not implemented for this adapter!")
@classmethod
def convert_integer_type(cls, agate_table: agate.Table, col_idx: int) -> str:
"""Return the type in the database that best maps to the agate.Number
type for the given agate table and column index.
:param agate_table: The table
:param col_idx: The index into the agate table for the column.
:return: The name of the type in the database
"""
return "integer"
@classmethod
@abc.abstractmethod
def convert_boolean_type(cls, agate_table: agate.Table, col_idx: int) -> str:
@@ -974,6 +1031,7 @@ class BaseAdapter(metaclass=AdapterMeta):
def convert_agate_type(cls, agate_table: agate.Table, col_idx: int) -> Optional[str]:
agate_type: Type = agate_table.column_types[col_idx]
conversions: List[Tuple[Type, Callable[..., str]]] = [
(Integer, cls.convert_integer_type),
(agate.Text, cls.convert_text_type),
(agate.Number, cls.convert_number_type),
(agate.Boolean, cls.convert_boolean_type),
@@ -1085,25 +1143,108 @@ class BaseAdapter(metaclass=AdapterMeta):
results = self._catalog_filter_table(table, manifest) # type: ignore[arg-type]
return results
def get_catalog(self, manifest: Manifest) -> Tuple[agate.Table, List[Exception]]:
schema_map = self._get_catalog_schemas(manifest)
def _get_one_catalog_by_relations(
self,
information_schema: InformationSchema,
relations: List[BaseRelation],
manifest: Manifest,
) -> agate.Table:
kwargs = {
"information_schema": information_schema,
"relations": relations,
}
table = self.execute_macro(
GET_CATALOG_RELATIONS_MACRO_NAME,
kwargs=kwargs,
# pass in the full manifest, so we get any local project
# overrides
manifest=manifest,
)
results = self._catalog_filter_table(table, manifest) # type: ignore[arg-type]
return results
def get_filtered_catalog(
self, manifest: Manifest, relations: Optional[Set[BaseRelation]] = None
):
catalogs: agate.Table
if (
relations is None
or len(relations) > 100
or not self.supports(Capability.SchemaMetadataByRelations)
):
# Do it the traditional way. We get the full catalog.
catalogs, exceptions = self.get_catalog(manifest)
else:
# Do it the new way. We try to save time by selecting information
# only for the exact set of relations we are interested in.
catalogs, exceptions = self.get_catalog_by_relations(manifest, relations)
if relations and catalogs:
relation_map = {
(
r.database.casefold() if r.database else None,
r.schema.casefold() if r.schema else None,
r.identifier.casefold() if r.identifier else None,
)
for r in relations
}
def in_map(row: agate.Row):
d = _expect_row_value("table_database", row)
s = _expect_row_value("table_schema", row)
i = _expect_row_value("table_name", row)
d = d.casefold() if d is not None else None
s = s.casefold() if s is not None else None
i = i.casefold() if i is not None else None
return (d, s, i) in relation_map
catalogs = catalogs.where(in_map)
return catalogs, exceptions
def row_matches_relation(self, row: agate.Row, relations: Set[BaseRelation]):
pass
def get_catalog(self, manifest: Manifest) -> Tuple[agate.Table, List[Exception]]:
with executor(self.config) as tpe:
futures: List[Future[agate.Table]] = []
schema_map: SchemaSearchMap = self._get_catalog_schemas(manifest)
for info, schemas in schema_map.items():
if len(schemas) == 0:
continue
name = ".".join([str(info.database), "information_schema"])
fut = tpe.submit_connected(
self, name, self._get_one_catalog, info, schemas, manifest
)
futures.append(fut)
catalogs, exceptions = catch_as_completed(futures)
catalogs, exceptions = catch_as_completed(futures)
return catalogs, exceptions
def get_catalog_by_relations(
self, manifest: Manifest, relations: Set[BaseRelation]
) -> Tuple[agate.Table, List[Exception]]:
with executor(self.config) as tpe:
futures: List[Future[agate.Table]] = []
relations_by_schema = self._get_catalog_relations_by_info_schema(relations)
for info_schema in relations_by_schema:
name = ".".join([str(info_schema.database), "information_schema"])
relations = set(relations_by_schema[info_schema])
fut = tpe.submit_connected(
self,
name,
self._get_one_catalog_by_relations,
info_schema,
relations,
manifest,
)
futures.append(fut)
catalogs, exceptions = catch_as_completed(futures)
return catalogs, exceptions
def cancel_open_connections(self):
"""Cancel all open connections."""
return self.connections.cancel_open()
@@ -1114,7 +1255,7 @@ class BaseAdapter(metaclass=AdapterMeta):
loaded_at_field: str,
filter: Optional[str],
manifest: Optional[Manifest] = None,
) -> Tuple[Optional[AdapterResponse], Dict[str, Any]]:
) -> Tuple[Optional[AdapterResponse], FreshnessResponse]:
"""Calculate the freshness of sources in dbt, and return it"""
kwargs: Dict[str, Any] = {
"source": source,
@@ -1149,13 +1290,52 @@ class BaseAdapter(metaclass=AdapterMeta):
snapshotted_at = _utc(table[0][1], source, loaded_at_field)
age = (snapshotted_at - max_loaded_at).total_seconds()
freshness = {
freshness: FreshnessResponse = {
"max_loaded_at": max_loaded_at,
"snapshotted_at": snapshotted_at,
"age": age,
}
return adapter_response, freshness
def calculate_freshness_from_metadata(
self,
source: BaseRelation,
manifest: Optional[Manifest] = None,
) -> Tuple[Optional[AdapterResponse], FreshnessResponse]:
kwargs: Dict[str, Any] = {
"information_schema": source.information_schema_only(),
"relations": [source],
}
result = self.execute_macro(
GET_RELATION_LAST_MODIFIED_MACRO_NAME, kwargs=kwargs, manifest=manifest
)
adapter_response, table = result.response, result.table # type: ignore[attr-defined]
try:
row = table[0]
last_modified_val = get_column_value_uncased("last_modified", row)
snapshotted_at_val = get_column_value_uncased("snapshotted_at", row)
except Exception:
raise MacroResultError(GET_RELATION_LAST_MODIFIED_MACRO_NAME, table)
if last_modified_val is None:
# Interpret missing value as "infinitely long ago"
max_loaded_at = datetime(1, 1, 1, 0, 0, 0, tzinfo=pytz.UTC)
else:
max_loaded_at = _utc(last_modified_val, None, "last_modified")
snapshotted_at = _utc(snapshotted_at_val, None, "snapshotted_at")
age = (snapshotted_at - max_loaded_at).total_seconds()
freshness: FreshnessResponse = {
"max_loaded_at": max_loaded_at,
"snapshotted_at": snapshotted_at,
"age": age,
}
return adapter_response, freshness
def pre_model_hook(self, config: Mapping[str, Any]) -> Any:
"""A hook for running some operation before the model materialization
runs. The hook can assume it has a connection available.
@@ -1429,6 +1609,14 @@ class BaseAdapter(metaclass=AdapterMeta):
else:
return None
@classmethod
def capabilities(cls) -> CapabilityDict:
return cls._capabilities
@classmethod
def supports(cls, capability: Capability) -> bool:
return bool(cls.capabilities()[capability])
COLUMNS_EQUAL_SQL = """
with diff_count as (

View File

@@ -93,7 +93,7 @@ class AdapterMeta(abc.ABCMeta):
_available_: FrozenSet[str]
_parse_replacements_: Dict[str, Callable]
def __new__(mcls, name, bases, namespace, **kwargs):
def __new__(mcls, name, bases, namespace, **kwargs) -> "AdapterMeta":
# mypy does not like the `**kwargs`. But `ABCMeta` itself takes
# `**kwargs` in its argspec here (and passes them to `type.__new__`.
# I'm not sure there is any benefit to it after poking around a bit,

View File

@@ -29,7 +29,7 @@ class AdapterPlugin:
credentials: Type[Credentials],
include_path: str,
dependencies: Optional[List[str]] = None,
):
) -> None:
self.adapter: Type[AdapterProtocol] = adapter
self.credentials: Type[Credentials] = credentials

View File

@@ -11,7 +11,7 @@ from dbt.exceptions import DbtRuntimeError
class NodeWrapper:
def __init__(self, node):
def __init__(self, node) -> None:
self._inner_node = node
def __getattr__(self, name):
@@ -25,9 +25,9 @@ class _QueryComment(local):
- a source_name indicating what set the current thread's query comment
"""
def __init__(self, initial):
def __init__(self, initial) -> None:
self.query_comment: Optional[str] = initial
self.append = False
self.append: bool = False
def add(self, sql: str) -> str:
if not self.query_comment:
@@ -57,7 +57,7 @@ QueryStringFunc = Callable[[str, Optional[NodeWrapper]], str]
class MacroQueryStringSetter:
def __init__(self, config: AdapterRequiredConfig, manifest: Manifest):
def __init__(self, config: AdapterRequiredConfig, manifest: Manifest) -> None:
self.manifest = manifest
self.config = config

View File

@@ -1,6 +1,6 @@
from collections.abc import Hashable
from dataclasses import dataclass, field
from typing import Optional, TypeVar, Any, Type, Dict, Iterator, Tuple, Set
from typing import Optional, TypeVar, Any, Type, Dict, Iterator, Tuple, Set, Union, FrozenSet
from dbt.contracts.graph.nodes import SourceDefinition, ManifestNode, ResultNode, ParsedNode
from dbt.contracts.relation import (
@@ -23,6 +23,7 @@ import dbt.exceptions
Self = TypeVar("Self", bound="BaseRelation")
SerializableIterable = Union[Tuple, FrozenSet]
@dataclass(frozen=True, eq=False, repr=False)
@@ -36,6 +37,18 @@ class BaseRelation(FakeAPIObject, Hashable):
quote_policy: Policy = field(default_factory=lambda: Policy())
dbt_created: bool = False
# register relation types that can be renamed for the purpose of replacing relations using stages and backups
# adding a relation type here also requires defining the associated rename macro
# e.g. adding RelationType.View in dbt-postgres requires that you define:
# include/postgres/macros/relations/view/rename.sql::postgres__get_rename_view_sql()
renameable_relations: SerializableIterable = field(default_factory=frozenset)
# register relation types that are atomically replaceable, e.g. they have "create or replace" syntax
# adding a relation type here also requires defining the associated replace macro
# e.g. adding RelationType.View in dbt-postgres requires that you define:
# include/postgres/macros/relations/view/replace.sql::postgres__get_replace_view_sql()
replaceable_relations: SerializableIterable = field(default_factory=frozenset)
def _is_exactish_match(self, field: ComponentName, value: str) -> bool:
if self.dbt_created and self.quote_policy.get_part(field) is False:
return self.path.get_lowered_part(field) == value.lower()
@@ -169,7 +182,6 @@ class BaseRelation(FakeAPIObject, Hashable):
return self.include(identifier=False).replace_path(identifier=None)
def _render_iterator(self) -> Iterator[Tuple[Optional[ComponentName], Optional[str]]]:
for key in ComponentName:
path_part: Optional[str] = None
if self.include_policy.get_part(key):
@@ -286,6 +298,14 @@ class BaseRelation(FakeAPIObject, Hashable):
)
return cls.from_dict(kwargs)
@property
def can_be_renamed(self) -> bool:
return self.type in self.renameable_relations
@property
def can_be_replaced(self) -> bool:
return self.type in self.replaceable_relations
def __repr__(self) -> str:
return "<{} {}>".format(self.__class__.__name__, self.render())
@@ -439,11 +459,11 @@ class SchemaSearchMap(Dict[InformationSchema, Set[Optional[str]]]):
self[key].add(schema)
def search(self) -> Iterator[Tuple[InformationSchema, Optional[str]]]:
for information_schema_name, schemas in self.items():
for information_schema, schemas in self.items():
for schema in schemas:
yield information_schema_name, schema
yield information_schema, schema
def flatten(self, allow_multiple_databases: bool = False):
def flatten(self, allow_multiple_databases: bool = False) -> "SchemaSearchMap":
new = self.__class__()
# make sure we don't have multiple databases if allow_multiple_databases is set to False

View File

@@ -38,8 +38,8 @@ class _CachedRelation:
:attr BaseRelation inner: The underlying dbt relation.
"""
def __init__(self, inner):
self.referenced_by = {}
def __init__(self, inner) -> None:
self.referenced_by: Dict[_ReferenceKey, _CachedRelation] = {}
self.inner = inner
def __str__(self) -> str:

View File

@@ -0,0 +1,52 @@
from dataclasses import dataclass
from enum import Enum
from typing import Optional, DefaultDict, Mapping
class Capability(str, Enum):
"""Enumeration of optional adapter features which can be probed using BaseAdapter.has_feature()"""
SchemaMetadataByRelations = "SchemaMetadataByRelations"
"""Indicates efficient support for retrieving schema metadata for a list of relations, rather than always retrieving
all the relations in a schema."""
TableLastModifiedMetadata = "TableLastModifiedMetadata"
"""Indicates support for determining the time of the last table modification by querying database metadata."""
class Support(str, Enum):
Unknown = "Unknown"
"""The adapter has not declared whether this capability is a feature of the underlying DBMS."""
Unsupported = "Unsupported"
"""This capability is not possible with the underlying DBMS, so the adapter does not implement related macros."""
NotImplemented = "NotImplemented"
"""This capability is available in the underlying DBMS, but support has not yet been implemented in the adapter."""
Versioned = "Versioned"
"""Some versions of the DBMS supported by the adapter support this capability and the adapter has implemented any
macros needed to use it."""
Full = "Full"
"""All versions of the DBMS supported by the adapter support this capability and the adapter has implemented any
macros needed to use it."""
@dataclass
class CapabilitySupport:
support: Support
first_version: Optional[str] = None
def __bool__(self):
return self.support == Support.Versioned or self.support == Support.Full
class CapabilityDict(DefaultDict[Capability, CapabilitySupport]):
def __init__(self, vals: Mapping[Capability, CapabilitySupport]):
super().__init__(self._default)
self.update(vals)
@staticmethod
def _default():
return CapabilitySupport(support=Support.Unknown)

View File

@@ -19,7 +19,7 @@ Adapter = AdapterProtocol
class AdapterContainer:
def __init__(self):
def __init__(self) -> None:
self.lock = threading.Lock()
self.adapters: Dict[str, Adapter] = {}
self.plugins: Dict[str, AdapterPlugin] = {}

View File

@@ -90,7 +90,7 @@ class AdapterProtocol( # type: ignore[misc]
ConnectionManager: Type[ConnectionManager_T]
connections: ConnectionManager_T
def __init__(self, config: AdapterRequiredConfig):
def __init__(self, config: AdapterRequiredConfig) -> None:
...
@classmethod

View File

@@ -1,6 +1,6 @@
import abc
import time
from typing import List, Optional, Tuple, Any, Iterable, Dict, Union
from typing import List, Optional, Tuple, Any, Iterable, Dict
import agate
@@ -131,14 +131,6 @@ class SQLConnectionManager(BaseConnectionManager):
return dbt.clients.agate_helper.table_from_data_flat(data, column_names)
@classmethod
def data_type_code_to_name(cls, type_code: Union[int, str]) -> str:
"""Get the string representation of the data type from the type_code."""
# https://peps.python.org/pep-0249/#type-objects
raise dbt.exceptions.NotImplementedError(
"`data_type_code_to_name` is not implemented for this adapter!"
)
def execute(
self, sql: str, auto_begin: bool = False, fetch: bool = False, limit: Optional[int] = None
) -> Tuple[AdapterResponse, agate.Table]:

View File

@@ -75,6 +75,10 @@ class SQLAdapter(BaseAdapter):
decimals = agate_table.aggregate(agate.MaxPrecision(col_idx)) # type: ignore[attr-defined]
return "float8" if decimals else "integer"
@classmethod
def convert_integer_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "integer"
@classmethod
def convert_boolean_type(cls, agate_table: agate.Table, col_idx: int) -> str:
return "boolean"

View File

@@ -3,6 +3,7 @@ import sys
from dataclasses import dataclass
from importlib import import_module
from multiprocessing import get_context
from pathlib import Path
from pprint import pformat as pf
from typing import Any, Callable, Dict, List, Optional, Set, Union
@@ -11,8 +12,8 @@ from click.core import Command as ClickCommand, Group, ParameterSource
from dbt.cli.exceptions import DbtUsageException
from dbt.cli.resolvers import default_log_path, default_project_dir
from dbt.cli.types import Command as CliCommand
from dbt.config.profile import read_user_config
from dbt.contracts.project import UserConfig
from dbt.config.project import read_project_flags
from dbt.contracts.project import ProjectFlags
from dbt.exceptions import DbtInternalError
from dbt.deprecations import renamed_env_var
from dbt.helper_types import WarnErrorOptions
@@ -24,7 +25,8 @@ if os.name != "nt":
FLAGS_DEFAULTS = {
"INDIRECT_SELECTION": "eager",
"TARGET_PATH": None,
# Cli args without user_config or env var option.
"WARN_ERROR": None,
# Cli args without project_flags or env var option.
"FULL_REFRESH": False,
"STRICT_MODE": False,
"STORE_FAILURES": False,
@@ -57,11 +59,10 @@ def args_to_context(args: List[str]) -> Context:
from dbt.cli.main import cli
cli_ctx = cli.make_context(cli.name, args)
# Split args if they're a comma seperated string.
# Split args if they're a comma separated string.
if len(args) == 1 and "," in args[0]:
args = args[0].split(",")
sub_command_name, sub_command, args = cli.resolve_command(cli_ctx, args)
# Handle source and docs group.
if isinstance(sub_command, Group):
sub_command_name, sub_command, args = sub_command.resolve_command(cli_ctx, args)
@@ -77,9 +78,8 @@ class Flags:
"""Primary configuration artifact for running dbt"""
def __init__(
self, ctx: Optional[Context] = None, user_config: Optional[UserConfig] = None
self, ctx: Optional[Context] = None, project_flags: Optional[ProjectFlags] = None
) -> None:
# Set the default flags.
for key, value in FLAGS_DEFAULTS.items():
object.__setattr__(self, key, value)
@@ -121,7 +121,6 @@ class Flags:
# respected over DBT_PRINT or --print.
new_name: Union[str, None] = None
if param_name in DEPRECATED_PARAMS:
# Deprecated env vars can only be set via env var.
# We use the deprecated option in click to serialize the value
# from the env var string.
@@ -202,27 +201,40 @@ class Flags:
invoked_subcommand_ctx, params_assigned_from_default, deprecated_env_vars
)
if not user_config:
if not project_flags:
project_dir = getattr(self, "PROJECT_DIR", str(default_project_dir()))
profiles_dir = getattr(self, "PROFILES_DIR", None)
user_config = read_user_config(profiles_dir) if profiles_dir else None
if profiles_dir and project_dir:
project_flags = read_project_flags(project_dir, profiles_dir)
else:
project_flags = None
# Add entire invocation command to flags
object.__setattr__(self, "INVOCATION_COMMAND", "dbt " + " ".join(sys.argv[1:]))
# Overwrite default assignments with user config if available.
if user_config:
if project_flags:
# Overwrite default assignments with project flags if available.
param_assigned_from_default_copy = params_assigned_from_default.copy()
for param_assigned_from_default in params_assigned_from_default:
user_config_param_value = getattr(user_config, param_assigned_from_default, None)
if user_config_param_value is not None:
project_flags_param_value = getattr(
project_flags, param_assigned_from_default, None
)
if project_flags_param_value is not None:
object.__setattr__(
self,
param_assigned_from_default.upper(),
convert_config(param_assigned_from_default, user_config_param_value),
convert_config(param_assigned_from_default, project_flags_param_value),
)
param_assigned_from_default_copy.remove(param_assigned_from_default)
params_assigned_from_default = param_assigned_from_default_copy
# Add project-level flags that are not available as CLI options / env vars
for (
project_level_flag_name,
project_level_flag_value,
) in project_flags.project_only_flags.items():
object.__setattr__(self, project_level_flag_name.upper(), project_level_flag_value)
# Set hard coded flags.
object.__setattr__(self, "WHICH", invoked_subcommand_name or ctx.info_name)
object.__setattr__(self, "MP_CONTEXT", get_context("spawn"))
@@ -236,9 +248,11 @@ class Flags:
# Starting in v1.5, if `log-path` is set in `dbt_project.yml`, it will raise a deprecation warning,
# with the possibility of removing it in a future release.
if getattr(self, "LOG_PATH", None) is None:
project_dir = getattr(self, "PROJECT_DIR", default_project_dir())
project_dir = getattr(self, "PROJECT_DIR", str(default_project_dir()))
version_check = getattr(self, "VERSION_CHECK", True)
object.__setattr__(self, "LOG_PATH", default_log_path(project_dir, version_check))
object.__setattr__(
self, "LOG_PATH", default_log_path(Path(project_dir), version_check)
)
# Support console DO NOT TRACK initiative.
if os.getenv("DO_NOT_TRACK", "").lower() in ("1", "t", "true", "y", "yes"):
@@ -316,10 +330,8 @@ def command_params(command: CliCommand, args_dict: Dict[str, Any]) -> CommandPar
default_args = set([x.lower() for x in FLAGS_DEFAULTS.keys()])
res = command.to_list()
for k, v in args_dict.items():
k = k.lower()
# if a "which" value exists in the args dict, it should match the command provided
if k == WHICH_KEY:
if v != command.value:
@@ -329,7 +341,9 @@ def command_params(command: CliCommand, args_dict: Dict[str, Any]) -> CommandPar
continue
# param was assigned from defaults and should not be included
if k not in (cmd_args | prnt_args) - default_args:
if k not in (cmd_args | prnt_args) or (
k in default_args and v == FLAGS_DEFAULTS[k.upper()]
):
continue
# if the param is in parent args, it should come before the arg name
@@ -342,9 +356,14 @@ def command_params(command: CliCommand, args_dict: Dict[str, Any]) -> CommandPar
spinal_cased = k.replace("_", "-")
# MultiOption flags come back as lists, but we want to pass them as space separated strings
if isinstance(v, list):
v = " ".join(v)
if k == "macro" and command == CliCommand.RUN_OPERATION:
add_fn(v)
elif v in (None, False):
# None is a Singleton, False is a Flyweight, only one instance of each.
elif v is None or v is False:
add_fn(f"--no-{spinal_cased}")
elif v is True:
add_fn(f"--{spinal_cased}")

View File

@@ -1,3 +1,4 @@
import functools
from copy import copy
from dataclasses import dataclass
from typing import Callable, List, Optional, Union
@@ -64,7 +65,7 @@ class dbtRunner:
self,
manifest: Optional[Manifest] = None,
callbacks: Optional[List[Callable[[EventMsg], None]]] = None,
):
) -> None:
self.manifest = manifest
if callbacks is None:
@@ -118,6 +119,44 @@ class dbtRunner:
)
# approach from https://github.com/pallets/click/issues/108#issuecomment-280489786
def global_flags(func):
@p.cache_selected_only
@p.debug
@p.deprecated_print
@p.enable_legacy_logger
@p.fail_fast
@p.log_cache_events
@p.log_file_max_bytes
@p.log_format_file
@p.log_level
@p.log_level_file
@p.log_path
@p.macro_debugging
@p.partial_parse
@p.partial_parse_file_path
@p.partial_parse_file_diff
@p.populate_cache
@p.print
@p.printer_width
@p.quiet
@p.record_timing_info
@p.send_anonymous_usage_stats
@p.single_threaded
@p.static_parser
@p.use_colors
@p.use_colors_file
@p.use_experimental_parser
@p.version
@p.version_check
@p.write_json
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# dbt
@click.group(
context_settings={"help_option_names": ["-h", "--help"]},
@@ -126,36 +165,11 @@ class dbtRunner:
epilog="Specify one of these sub-commands and you can find more help from there.",
)
@click.pass_context
@p.cache_selected_only
@p.debug
@p.deprecated_print
@p.enable_legacy_logger
@p.fail_fast
@p.log_cache_events
@p.log_format
@p.log_format_file
@p.log_level
@p.log_level_file
@p.log_path
@p.macro_debugging
@p.partial_parse
@p.partial_parse_file_path
@p.populate_cache
@p.print
@p.printer_width
@p.quiet
@p.record_timing_info
@p.send_anonymous_usage_stats
@p.single_threaded
@p.static_parser
@p.use_colors
@p.use_colors_file
@p.use_experimental_parser
@p.version
@p.version_check
@global_flags
@p.warn_error
@p.warn_error_options
@p.write_json
@p.log_format
@p.show_resource_report
def cli(ctx, **kwargs):
"""An ELT tool for managing your SQL transformations and data models.
For more documentation on these commands, visit: docs.getdbt.com
@@ -165,13 +179,14 @@ def cli(ctx, **kwargs):
# dbt build
@cli.command("build")
@click.pass_context
@global_flags
@p.defer
@p.deprecated_defer
@p.exclude
@p.fail_fast
@p.favor_state
@p.deprecated_favor_state
@p.full_refresh
@p.include_saved_query
@p.indirect_selection
@p.profile
@p.profiles_dir
@@ -188,7 +203,6 @@ def cli(ctx, **kwargs):
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile
@@ -211,6 +225,8 @@ def build(ctx, **kwargs):
# dbt clean
@cli.command("clean")
@click.pass_context
@global_flags
@p.clean_project_files_only
@p.profile
@p.profiles_dir
@p.project_dir
@@ -233,6 +249,7 @@ def clean(ctx, **kwargs):
# dbt docs
@cli.group()
@click.pass_context
@global_flags
def docs(ctx, **kwargs):
"""Generate or serve the documentation website for your project"""
@@ -240,6 +257,7 @@ def docs(ctx, **kwargs):
# dbt docs generate
@docs.command("generate")
@click.pass_context
@global_flags
@p.compile_docs
@p.defer
@p.deprecated_defer
@@ -252,6 +270,7 @@ def docs(ctx, **kwargs):
@p.select
@p.selector
@p.empty_catalog
@p.static
@p.state
@p.defer_state
@p.deprecated_state
@@ -259,7 +278,6 @@ def docs(ctx, **kwargs):
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile
@@ -282,7 +300,9 @@ def docs_generate(ctx, **kwargs):
# dbt docs serve
@docs.command("serve")
@click.pass_context
@global_flags
@p.browser
@p.host
@p.port
@p.profile
@p.profiles_dir
@@ -310,6 +330,7 @@ def docs_serve(ctx, **kwargs):
# dbt compile
@cli.command("compile")
@click.pass_context
@global_flags
@p.defer
@p.deprecated_defer
@p.exclude
@@ -328,11 +349,11 @@ def docs_serve(ctx, **kwargs):
@p.state
@p.defer_state
@p.deprecated_state
@p.compile_inject_ephemeral_ctes
@p.target
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile
@@ -356,6 +377,7 @@ def compile(ctx, **kwargs):
# dbt show
@cli.command("show")
@click.pass_context
@global_flags
@p.defer
@p.deprecated_defer
@p.exclude
@@ -379,7 +401,6 @@ def compile(ctx, **kwargs):
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile
@@ -403,6 +424,7 @@ def show(ctx, **kwargs):
# dbt debug
@cli.command("debug")
@click.pass_context
@global_flags
@p.debug_connection
@p.config_dir
@p.profile
@@ -410,7 +432,6 @@ def show(ctx, **kwargs):
@p.project_dir
@p.target
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
def debug(ctx, **kwargs):
@@ -429,18 +450,46 @@ def debug(ctx, **kwargs):
# dbt deps
@cli.command("deps")
@click.pass_context
@global_flags
@p.profile
@p.profiles_dir_exists_false
@p.project_dir
@p.target
@p.vars
@p.source
@p.dry_run
@p.lock
@p.upgrade
@p.add_package
@requires.postflight
@requires.preflight
@requires.unset_profile
@requires.project
def deps(ctx, **kwargs):
"""Pull the most recent version of the dependencies listed in packages.yml"""
task = DepsTask(ctx.obj["flags"], ctx.obj["project"])
"""Install dbt packages specified.
In the following case, a new `package-lock.yml` will be generated and the packages are installed:
- user updated the packages.yml
- user specify the flag --update, which means for packages that are specified as a
range, dbt-core will try to install the newer version
Otherwise, deps will use `package-lock.yml` as source of truth to install packages.
There is a way to add new packages by providing an `--add-package` flag to deps command
which will allow user to specify a package they want to add in the format of packagename@version.
"""
flags = ctx.obj["flags"]
if flags.ADD_PACKAGE:
if not flags.ADD_PACKAGE["version"] and flags.SOURCE != "local":
raise BadOptionUsage(
message=f"Version is required in --add-package when a package when source is {flags.SOURCE}",
option_name="--add-package",
)
else:
if flags.DRY_RUN:
raise BadOptionUsage(
message="Invalid flag `--dry-run` when not using `--add-package`.",
option_name="--dry-run",
)
task = DepsTask(flags, ctx.obj["project"])
results = task.run()
success = task.interpret_results(results)
return results, success
@@ -449,6 +498,7 @@ def deps(ctx, **kwargs):
# dbt init
@cli.command("init")
@click.pass_context
@global_flags
# for backwards compatibility, accept 'project_name' as an optional positional argument
@click.argument("project_name", required=False)
@p.profile
@@ -471,6 +521,7 @@ def init(ctx, **kwargs):
# dbt list
@cli.command("list")
@click.pass_context
@global_flags
@p.exclude
@p.indirect_selection
@p.models
@@ -516,6 +567,7 @@ cli.add_command(ls, "ls")
# dbt parse
@cli.command("parse")
@click.pass_context
@global_flags
@p.profile
@p.profiles_dir
@p.project_dir
@@ -523,7 +575,6 @@ cli.add_command(ls, "ls")
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile
@@ -533,19 +584,18 @@ cli.add_command(ls, "ls")
def parse(ctx, **kwargs):
"""Parses the project and provides information on performance"""
# manifest generation and writing happens in @requires.manifest
return ctx.obj["manifest"], True
# dbt run
@cli.command("run")
@click.pass_context
@global_flags
@p.defer
@p.deprecated_defer
@p.favor_state
@p.deprecated_favor_state
@p.exclude
@p.fail_fast
@p.full_refresh
@p.profile
@p.profiles_dir
@@ -559,7 +609,6 @@ def parse(ctx, **kwargs):
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile
@@ -582,26 +631,27 @@ def run(ctx, **kwargs):
# dbt retry
@cli.command("retry")
@click.pass_context
@global_flags
@p.project_dir
@p.profiles_dir
@p.vars
@p.profile
@p.target
@p.state
@p.target
@p.target_path
@p.threads
@p.fail_fast
@p.full_refresh
@requires.postflight
@requires.preflight
@requires.profile
@requires.project
@requires.runtime_config
@requires.manifest
def retry(ctx, **kwargs):
"""Retry the nodes that failed in the previous run."""
# Retry will parse manifest inside the task after we consolidate the flags
task = RetryTask(
ctx.obj["flags"],
ctx.obj["runtime_config"],
ctx.obj["manifest"],
)
results = task.run()
@@ -612,6 +662,7 @@ def retry(ctx, **kwargs):
# dbt clone
@cli.command("clone")
@click.pass_context
@global_flags
@p.defer_state
@p.exclude
@p.full_refresh
@@ -626,7 +677,6 @@ def retry(ctx, **kwargs):
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.preflight
@requires.profile
@requires.project
@@ -649,6 +699,7 @@ def clone(ctx, **kwargs):
# dbt run operation
@cli.command("run-operation")
@click.pass_context
@global_flags
@click.argument("macro")
@p.args
@p.profile
@@ -680,6 +731,7 @@ def run_operation(ctx, **kwargs):
# dbt seed
@cli.command("seed")
@click.pass_context
@global_flags
@p.exclude
@p.full_refresh
@p.profile
@@ -695,7 +747,6 @@ def run_operation(ctx, **kwargs):
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile
@@ -717,6 +768,7 @@ def seed(ctx, **kwargs):
# dbt snapshot
@cli.command("snapshot")
@click.pass_context
@global_flags
@p.defer
@p.deprecated_defer
@p.exclude
@@ -756,6 +808,7 @@ def snapshot(ctx, **kwargs):
# dbt source
@cli.group()
@click.pass_context
@global_flags
def source(ctx, **kwargs):
"""Manage your project's sources"""
@@ -763,6 +816,7 @@ def source(ctx, **kwargs):
# dbt source freshness
@source.command("freshness")
@click.pass_context
@global_flags
@p.exclude
@p.output_path # TODO: Is this ok to re-use? We have three different output params, how much can we consolidate?
@p.profile
@@ -805,10 +859,10 @@ cli.commands["source"].add_command(snapshot_freshness, "snapshot-freshness") #
# dbt test
@cli.command("test")
@click.pass_context
@global_flags
@p.defer
@p.deprecated_defer
@p.exclude
@p.fail_fast
@p.favor_state
@p.deprecated_favor_state
@p.indirect_selection
@@ -825,7 +879,6 @@ cli.commands["source"].add_command(snapshot_freshness, "snapshot-freshness") #
@p.target_path
@p.threads
@p.vars
@p.version_check
@requires.postflight
@requires.preflight
@requires.profile

View File

@@ -22,6 +22,26 @@ class YAML(ParamType):
self.fail(f"String '{value}' is not valid YAML", param, ctx)
class Package(ParamType):
"""The Click STRING type. Converts string into dict with package name and version.
Example package:
package-name@1.0.0
package-name
"""
name = "NewPackage"
def convert(self, value, param, ctx):
# assume non-string values are a problem
if not isinstance(value, str):
self.fail(f"Cannot load Package from type {type(value)}", param, ctx)
try:
package_name, package_version = value.split("@")
return {"name": package_name, "version": package_version}
except ValueError:
return {"name": value, "version": None}
class WarnErrorOptionsType(YAML):
"""The Click WarnErrorOptions type. Converts YAML strings into objects."""

View File

@@ -2,19 +2,21 @@ import click
import inspect
import typing as t
from click import Context
from click.parser import OptionParser, ParsingState
from dbt.cli.option_types import ChoiceTuple
# Implementation from: https://stackoverflow.com/a/48394004
# Note MultiOption options must be specified with type=tuple or type=ChoiceTuple (https://github.com/pallets/click/issues/2012)
class MultiOption(click.Option):
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
self.save_other_options = kwargs.pop("save_other_options", True)
nargs = kwargs.pop("nargs", -1)
assert nargs == -1, "nargs, if set, must be -1 not {}".format(nargs)
super(MultiOption, self).__init__(*args, **kwargs)
self._previous_parser_process = None
self._eat_all_parser = None
# this makes mypy happy, setting these to None causes mypy failures
self._previous_parser_process = lambda *args, **kwargs: None
self._eat_all_parser = lambda *args, **kwargs: None
# validate that multiple=True
multiple = kwargs.pop("multiple", None)
@@ -29,34 +31,35 @@ class MultiOption(click.Option):
else:
assert isinstance(option_type, ChoiceTuple), msg
def add_to_parser(self, parser, ctx):
def parser_process(value, state):
def add_to_parser(self, parser: OptionParser, ctx: Context):
def parser_process(value: str, state: ParsingState):
# method to hook to the parser.process
done = False
value = [value]
value_list = str.split(value, " ")
if self.save_other_options:
# grab everything up to the next option
while state.rargs and not done:
for prefix in self._eat_all_parser.prefixes:
for prefix in self._eat_all_parser.prefixes: # type: ignore[attr-defined]
if state.rargs[0].startswith(prefix):
done = True
if not done:
value.append(state.rargs.pop(0))
value_list.append(state.rargs.pop(0))
else:
# grab everything remaining
value += state.rargs
value_list += state.rargs
state.rargs[:] = []
value = tuple(value)
value_tuple = tuple(value_list)
# call the actual process
self._previous_parser_process(value, state)
self._previous_parser_process(value_tuple, state)
retval = super(MultiOption, self).add_to_parser(parser, ctx)
for name in self.opts:
our_parser = parser._long_opt.get(name) or parser._short_opt.get(name)
if our_parser:
self._eat_all_parser = our_parser
self._eat_all_parser = our_parser # type: ignore[assignment]
self._previous_parser_process = our_parser.process
our_parser.process = parser_process
# mypy doesnt like assingment to a method see https://github.com/python/mypy/issues/708
our_parser.process = parser_process # type: ignore[method-assign]
break
return retval

View File

@@ -2,10 +2,16 @@ from pathlib import Path
import click
from dbt.cli.options import MultiOption
from dbt.cli.option_types import YAML, ChoiceTuple, WarnErrorOptionsType
from dbt.cli.option_types import YAML, ChoiceTuple, WarnErrorOptionsType, Package
from dbt.cli.resolvers import default_project_dir, default_profiles_dir
from dbt.version import get_version_information
add_package = click.option(
"--add-package",
help="Add a package to current package spec, specify it as package-name@version. Change the source with --source flag.",
envvar=None,
type=Package(),
)
args = click.option(
"--args",
envvar=None,
@@ -40,6 +46,14 @@ compile_docs = click.option(
default=True,
)
compile_inject_ephemeral_ctes = click.option(
"--inject-ephemeral-ctes/--no-inject-ephemeral-ctes",
envvar=None,
help="Internal flag controlling injection of referenced ephemeral models' CTEs during `compile`.",
hidden=True,
default=True,
)
config_dir = click.option(
"--config-dir",
envvar=None,
@@ -69,6 +83,14 @@ deprecated_defer = click.option(
hidden=True,
)
dry_run = click.option(
"--dry-run",
envvar=None,
help="Option to run `dbt deps --add-package` without updating package-lock.yml file.",
is_flag=True,
)
enable_legacy_logger = click.option(
"--enable-legacy-logger/--no-enable-legacy-logger",
envvar="DBT_ENABLE_LEGACY_LOGGER",
@@ -111,6 +133,14 @@ full_refresh = click.option(
is_flag=True,
)
host = click.option(
"--host",
envvar="DBT_HOST",
help="host to serve dbt docs on",
type=click.STRING,
default="127.0.0.1",
)
indirect_selection = click.option(
"--indirect-selection",
envvar="DBT_INDIRECT_SELECTION",
@@ -119,6 +149,13 @@ indirect_selection = click.option(
default="eager",
)
lock = click.option(
"--lock",
envvar=None,
help="Generate the package-lock.yml file without install the packages.",
is_flag=True,
)
log_cache_events = click.option(
"--log-cache-events/--no-log-cache-events",
help="Enable verbose logging for relational cache events to help when debugging.",
@@ -171,6 +208,15 @@ use_colors_file = click.option(
default=True,
)
log_file_max_bytes = click.option(
"--log-file-max-bytes",
envvar="DBT_LOG_FILE_MAX_BYTES",
help="Configure the max file size in bytes for a single dbt.log file, before rolling over. 0 means no limit.",
default=10 * 1024 * 1024, # 10mb
type=click.INT,
hidden=True,
)
log_path = click.option(
"--log-path",
envvar="DBT_LOG_PATH",
@@ -248,6 +294,14 @@ partial_parse_file_path = click.option(
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
)
partial_parse_file_diff = click.option(
"--partial-parse-file-diff/--no-partial-parse-file-diff",
envvar="DBT_PARTIAL_PARSE_FILE_DIFF",
help="Internal flag for whether to compute a file diff during partial parsing.",
hidden=True,
default=True,
)
populate_cache = click.option(
"--populate-cache/--no-populate-cache",
envvar="DBT_POPULATE_CACHE",
@@ -290,7 +344,7 @@ printer_width = click.option(
profile = click.option(
"--profile",
envvar=None,
help="Which profile to load. Overrides setting in dbt_project.yml.",
help="Which existing profile to load. Overrides setting in dbt_project.yml.",
)
profiles_dir = click.option(
@@ -343,6 +397,8 @@ resource_type = click.option(
type=ChoiceTuple(
[
"metric",
"semantic_model",
"saved_query",
"source",
"analysis",
"model",
@@ -360,6 +416,14 @@ resource_type = click.option(
default=(),
)
include_saved_query = click.option(
"--include-saved-query/--no-include-saved-query",
envvar="DBT_INCLUDE_SAVED_QUERY",
help="Include saved queries in the list of resources to be selected for build command",
is_flag=True,
hidden=True,
)
model_decls = ("-m", "--models", "--model")
select_decls = ("-s", "--select")
select_attrs = {
@@ -380,9 +444,9 @@ inline = click.option(
# Most CLI arguments should use the combined `select` option that aliases `--models` to `--select`.
# However, if you need to split out these separators (like `dbt ls`), use the `models` and `raw_select` options instead.
# See https://github.com/dbt-labs/dbt-core/pull/6774#issuecomment-1408476095 for more info.
models = click.option(*model_decls, **select_attrs)
raw_select = click.option(*select_decls, **select_attrs)
select = click.option(*select_decls, *model_decls, **select_attrs)
models = click.option(*model_decls, **select_attrs) # type: ignore[arg-type]
raw_select = click.option(*select_decls, **select_attrs) # type: ignore[arg-type]
select = click.option(*select_decls, *model_decls, **select_attrs) # type: ignore[arg-type]
selector = click.option(
"--selector",
@@ -397,6 +461,13 @@ send_anonymous_usage_stats = click.option(
default=True,
)
clean_project_files_only = click.option(
"--clean-project-files-only / --no-clean-project-files-only",
envvar="DBT_CLEAN_PROJECT_FILES_ONLY",
help="If disabled, dbt clean will delete all paths specified in clean-paths, even if they're outside the dbt project.",
default=True,
)
show = click.option(
"--show",
envvar=None,
@@ -432,6 +503,21 @@ empty_catalog = click.option(
is_flag=True,
)
source = click.option(
"--source",
envvar=None,
help="Source to download page from, must be one of hub, git, or local. Defaults to hub.",
type=click.Choice(["hub", "git", "local"], case_sensitive=True),
default="hub",
)
static = click.option(
"--static",
help="Generate an additional static_index.html with manifest and catalog built-in.",
default=False,
is_flag=True,
)
state = click.option(
"--state",
envvar="DBT_STATE",
@@ -500,6 +586,13 @@ target_path = click.option(
type=click.Path(),
)
upgrade = click.option(
"--upgrade",
envvar=None,
help="Upgrade packages to the latest version.",
is_flag=True,
)
debug_connection = click.option(
"--connection",
envvar=None,
@@ -581,3 +674,10 @@ write_json = click.option(
help="Whether or not to write the manifest.json and run_results.json files to the target directory",
default=True,
)
show_resource_report = click.option(
"--show-resource-report/--no-show-resource-report",
default=False,
envvar="DBT_SHOW_RESOURCE_REPORT",
hidden=True,
)

View File

@@ -1,6 +1,6 @@
import dbt.tracking
from dbt.version import installed as installed_version
from dbt.adapters.factory import adapter_management, register_adapter
from dbt.adapters.factory import adapter_management, register_adapter, get_adapter
from dbt.flags import set_flags, get_flag_dict
from dbt.cli.exceptions import (
ExceptionExit,
@@ -9,24 +9,28 @@ from dbt.cli.exceptions import (
from dbt.cli.flags import Flags
from dbt.config import RuntimeConfig
from dbt.config.runtime import load_project, load_profile, UnsetProfile
from dbt.events.base_types import EventLevel
from dbt.events.functions import fire_event, LOG_VERSION, set_invocation_id, setup_event_logger
from dbt.events.types import (
CommandCompleted,
MainReportVersion,
MainReportArgs,
MainTrackingUserState,
ResourceReport,
)
from dbt.events.helpers import get_json_string_utcnow
from dbt.events.types import MainEncounteredError, MainStackTrace
from dbt.exceptions import Exception as DbtException, DbtProjectError, FailFastError
from dbt.parser.manifest import ManifestLoader, write_manifest
from dbt.parser.manifest import parse_manifest
from dbt.profiler import profiler
from dbt.tracking import active_user, initialize_from_flags, track_run
from dbt.utils import cast_dict_to_dict_of_strings
from dbt.plugins import set_up_plugin_manager, get_plugin_manager
from dbt.plugins import set_up_plugin_manager
from click import Context
from functools import update_wrapper
import importlib.util
import time
import traceback
@@ -96,6 +100,28 @@ def postflight(func):
fire_event(MainStackTrace(stack_trace=traceback.format_exc()))
raise ExceptionExit(e)
finally:
# Fire ResourceReport, but only on systems which support the resource
# module. (Skip it on Windows).
if importlib.util.find_spec("resource") is not None:
import resource
rusage = resource.getrusage(resource.RUSAGE_SELF)
fire_event(
ResourceReport(
command_name=ctx.command.name,
command_success=success,
command_wall_clock_time=time.perf_counter() - start_func,
process_user_time=rusage.ru_utime,
process_kernel_time=rusage.ru_stime,
process_mem_max_rss=rusage.ru_maxrss,
process_in_blocks=rusage.ru_inblock,
process_out_blocks=rusage.ru_oublock,
),
EventLevel.INFO
if "flags" in ctx.obj and ctx.obj["flags"].SHOW_RESOURCE_REPORT
else None,
)
fire_event(
CommandCompleted(
command=ctx.command_path,
@@ -239,23 +265,16 @@ def manifest(*args0, write=True, write_perf_info=False):
raise DbtProjectError("profile, project, and runtime_config required for manifest")
runtime_config = ctx.obj["runtime_config"]
register_adapter(runtime_config)
# a manifest has already been set on the context, so don't overwrite it
if ctx.obj.get("manifest") is None:
manifest = ManifestLoader.get_full_manifest(
runtime_config,
write_perf_info=write_perf_info,
ctx.obj["manifest"] = parse_manifest(
runtime_config, write_perf_info, write, ctx.obj["flags"].write_json
)
ctx.obj["manifest"] = manifest
if write and ctx.obj["flags"].write_json:
write_manifest(manifest, runtime_config.project_target_path)
pm = get_plugin_manager(runtime_config.project_name)
plugin_artifacts = pm.get_manifest_artifacts(manifest)
for path, plugin_artifact in plugin_artifacts.items():
plugin_artifact.write(path)
else:
register_adapter(runtime_config)
adapter = get_adapter(runtime_config)
adapter.connections.set_query_header(ctx.obj["manifest"])
return func(*args, **kwargs)
return update_wrapper(wrapper, func)

View File

@@ -9,10 +9,23 @@ from typing import Iterable, List, Dict, Union, Optional, Any
from dbt.exceptions import DbtRuntimeError
BOM = BOM_UTF8.decode("utf-8") # '\ufeff'
class Integer(agate.data_types.DataType):
def cast(self, d):
# by default agate will cast none as a Number
# but we need to cast it as an Integer to preserve
# the type when merging and unioning tables
if type(d) == int or d is None:
return d
else:
raise agate.exceptions.CastError('Can not parse value "%s" as Integer.' % d)
def jsonify(self, d):
return d
class Number(agate.data_types.Number):
# undo the change in https://github.com/wireservice/agate/pull/733
# i.e. do not cast True and False to numeric 1 and 0
@@ -48,6 +61,7 @@ def build_type_tester(
) -> agate.TypeTester:
types = [
Integer(null_values=("null", "")),
Number(null_values=("null", "")),
agate.data_types.Date(null_values=("null", ""), date_format="%Y-%m-%d"),
agate.data_types.DateTime(null_values=("null", ""), datetime_format="%Y-%m-%d %H:%M:%S"),
@@ -135,12 +149,12 @@ def as_matrix(table):
return [r.values() for r in table.rows.values()]
def from_csv(abspath, text_columns):
def from_csv(abspath, text_columns, delimiter=","):
type_tester = build_type_tester(text_columns=text_columns)
with open(abspath, encoding="utf-8") as fp:
if fp.read(1) != BOM:
fp.seek(0)
return agate.Table.from_csv(fp, column_types=type_tester)
return agate.Table.from_csv(fp, column_types=type_tester, delimiter=delimiter)
class _NullMarker:
@@ -151,7 +165,7 @@ NullableAgateType = Union[agate.data_types.DataType, _NullMarker]
class ColumnTypeBuilder(Dict[str, NullableAgateType]):
def __init__(self):
def __init__(self) -> None:
super().__init__()
def __setitem__(self, key, value):
@@ -166,6 +180,13 @@ class ColumnTypeBuilder(Dict[str, NullableAgateType]):
elif isinstance(value, _NullMarker):
# use the existing value
return
# when one table column is Number while another is Integer, force the column to Number on merge
elif isinstance(value, Integer) and isinstance(existing_type, agate.data_types.Number):
# use the existing value
return
elif isinstance(existing_type, Integer) and isinstance(value, agate.data_types.Number):
# overwrite
super().__setitem__(key, value)
elif not isinstance(value, type(existing_type)):
# actual type mismatch!
raise DbtRuntimeError(
@@ -177,8 +198,9 @@ class ColumnTypeBuilder(Dict[str, NullableAgateType]):
result: Dict[str, agate.data_types.DataType] = {}
for key, value in self.items():
if isinstance(value, _NullMarker):
# this is what agate would do.
result[key] = agate.data_types.Number()
# agate would make it a Number but we'll make it Integer so that if this column
# gets merged with another Integer column, it won't get forced to a Number
result[key] = Integer()
else:
result[key] = value
return result
@@ -218,3 +240,12 @@ def merge_tables(tables: List[agate.Table]) -> agate.Table:
rows.append(agate.Row(data, column_names))
# _is_fork to tell agate that we already made things into `Row`s.
return agate.Table(rows, column_names, column_types, _is_fork=True)
def get_column_value_uncased(column_name: str, row: agate.Row) -> Any:
"""Get the value of a column in this row, ignoring the casing of the column name."""
for key, value in row.items():
if key.casefold() == column_name.casefold():
return value
raise KeyError

View File

@@ -111,7 +111,7 @@ def checkout(cwd, repo, revision=None):
def get_current_sha(cwd):
out, err = run_cmd(cwd, ["git", "rev-parse", "HEAD"], env={"LC_ALL": "C"})
return out.decode("utf-8")
return out.decode("utf-8").strip()
def remove_remote(cwd):

View File

@@ -191,7 +191,7 @@ NativeSandboxEnvironment.template_class = NativeSandboxTemplate # type: ignore
class TemplateCache:
def __init__(self):
def __init__(self) -> None:
self.file_cache: Dict[str, jinja2.Template] = {}
def get_node_template(self, node) -> jinja2.Template:

View File

@@ -4,7 +4,6 @@ import json
import networkx as nx # type: ignore
import os
import pickle
import sqlparse
from collections import defaultdict
from typing import List, Dict, Any, Tuple, Optional
@@ -36,6 +35,7 @@ from dbt.node_types import NodeType, ModelLanguage
from dbt.events.format import pluralize
import dbt.tracking
import dbt.task.list as list_task
import sqlparse
graph_file_name = "graph.gpickle"
@@ -125,7 +125,7 @@ def _get_tests_for_node(manifest: Manifest, unique_id: UniqueID) -> List[UniqueI
class Linker:
def __init__(self, data=None):
def __init__(self, data=None) -> None:
if data is None:
data = {}
self.graph = nx.DiGraph(**data)
@@ -183,14 +183,16 @@ class Linker:
def link_graph(self, manifest: Manifest):
for source in manifest.sources.values():
self.add_node(source.unique_id)
for semantic_model in manifest.semantic_models.values():
self.add_node(semantic_model.unique_id)
for node in manifest.nodes.values():
self.link_node(node, manifest)
for semantic_model in manifest.semantic_models.values():
self.link_node(semantic_model, manifest)
for exposure in manifest.exposures.values():
self.link_node(exposure, manifest)
for metric in manifest.metrics.values():
self.link_node(metric, manifest)
for saved_query in manifest.saved_queries.values():
self.link_node(saved_query, manifest)
cycle = self.find_cycles()
@@ -274,7 +276,7 @@ class Linker:
class Compiler:
def __init__(self, config):
def __init__(self, config) -> None:
self.config = config
def initialize(self):
@@ -320,6 +322,10 @@ class Compiler:
if model.compiled_code is None:
raise DbtRuntimeError("Cannot inject ctes into an uncompiled node", model)
# tech debt: safe flag/arg access (#6259)
if not getattr(self.config.args, "inject_ephemeral_ctes", True):
return (model, [])
# extra_ctes_injected flag says that we've already recursively injected the ctes
if model.extra_ctes_injected:
return (model, model.extra_ctes)
@@ -378,16 +384,16 @@ class Compiler:
_add_prepended_cte(prepended_ctes, InjectedCTE(id=cte.id, sql=sql))
injected_sql = inject_ctes_into_sql(
model.compiled_code,
prepended_ctes,
)
# Check again before updating for multi-threading
if not model.extra_ctes_injected:
injected_sql = inject_ctes_into_sql(
model.compiled_code,
prepended_ctes,
)
model.extra_ctes_injected = True
model._pre_injected_sql = model.compiled_code
model.compiled_code = injected_sql
model.extra_ctes = prepended_ctes
model.extra_ctes_injected = True
# if model.extra_ctes is not set to prepended ctes, something went wrong
return model, model.extra_ctes
@@ -523,6 +529,12 @@ class Compiler:
the node's raw_code into compiled_code, and then calls the
recursive method to "prepend" the ctes.
"""
# Make sure Lexer for sqlparse 0.4.4 is initialized
from sqlparse.lexer import Lexer # type: ignore
if hasattr(Lexer, "get_default_instance"):
Lexer.get_default_instance()
node = self._compile_code(node, manifest, extra_context)
node, _ = self._recursively_prepend_ctes(node, manifest, extra_context)

View File

@@ -1,4 +1,4 @@
# all these are just exports, they need "noqa" so flake8 will not complain.
from .profile import Profile, read_user_config # noqa
from .profile import Profile # noqa
from .project import Project, IsFQNResource, PartialProject # noqa
from .runtime import RuntimeConfig # noqa

View File

@@ -8,7 +8,7 @@ from dbt.flags import get_flags
from dbt.clients.system import load_file_contents
from dbt.clients.yaml_helper import load_yaml_text
from dbt.contracts.connection import Credentials, HasCredentials
from dbt.contracts.project import ProfileConfig, UserConfig
from dbt.contracts.project import ProfileConfig
from dbt.exceptions import (
CompilationError,
DbtProfileError,
@@ -19,7 +19,6 @@ from dbt.exceptions import (
)
from dbt.events.types import MissingProfileTarget
from dbt.events.functions import fire_event
from dbt.utils import coerce_dict_str
from .renderer import ProfileRenderer
@@ -51,19 +50,6 @@ def read_profile(profiles_dir: str) -> Dict[str, Any]:
return {}
def read_user_config(directory: str) -> UserConfig:
try:
profile = read_profile(directory)
if profile:
user_config = coerce_dict_str(profile.get("config", {}))
if user_config is not None:
UserConfig.validate(user_config)
return UserConfig.from_dict(user_config)
except (DbtRuntimeError, ValidationError):
pass
return UserConfig()
# The Profile class is included in RuntimeConfig, so any attribute
# additions must also be set where the RuntimeConfig class is created
# `init=False` is a workaround for https://bugs.python.org/issue45081
@@ -71,7 +57,6 @@ def read_user_config(directory: str) -> UserConfig:
class Profile(HasCredentials):
profile_name: str
target_name: str
user_config: UserConfig
threads: int
credentials: Credentials
profile_env_vars: Dict[str, Any]
@@ -80,16 +65,14 @@ class Profile(HasCredentials):
self,
profile_name: str,
target_name: str,
user_config: UserConfig,
threads: int,
credentials: Credentials,
):
) -> None:
"""Explicitly defining `__init__` to work around bug in Python 3.9.7
https://bugs.python.org/issue45081
"""
self.profile_name = profile_name
self.target_name = target_name
self.user_config = user_config
self.threads = threads
self.credentials = credentials
self.profile_env_vars = {} # never available on init
@@ -106,12 +89,10 @@ class Profile(HasCredentials):
result = {
"profile_name": self.profile_name,
"target_name": self.target_name,
"user_config": self.user_config,
"threads": self.threads,
"credentials": self.credentials,
}
if serialize_credentials:
result["user_config"] = self.user_config.to_dict(omit_none=True)
result["credentials"] = self.credentials.to_dict(omit_none=True)
return result
@@ -124,7 +105,6 @@ class Profile(HasCredentials):
"name": self.target_name,
"target_name": self.target_name,
"profile_name": self.profile_name,
"config": self.user_config.to_dict(omit_none=True),
}
)
return target
@@ -246,7 +226,6 @@ defined in your profiles.yml file. You can find profiles.yml here:
threads: int,
profile_name: str,
target_name: str,
user_config: Optional[Dict[str, Any]] = None,
) -> "Profile":
"""Create a profile from an existing set of Credentials and the
remaining information.
@@ -255,20 +234,13 @@ defined in your profiles.yml file. You can find profiles.yml here:
:param threads: The number of threads to use for connections.
:param profile_name: The profile name used for this profile.
:param target_name: The target name used for this profile.
:param user_config: The user-level config block from the
raw profiles, if specified.
:raises DbtProfileError: If the profile is invalid.
:returns: The new Profile object.
"""
if user_config is None:
user_config = {}
UserConfig.validate(user_config)
user_config_obj: UserConfig = UserConfig.from_dict(user_config)
profile = cls(
profile_name=profile_name,
target_name=target_name,
user_config=user_config_obj,
threads=threads,
credentials=credentials,
)
@@ -316,7 +288,6 @@ defined in your profiles.yml file. You can find profiles.yml here:
raw_profile: Dict[str, Any],
profile_name: str,
renderer: ProfileRenderer,
user_config: Optional[Dict[str, Any]] = None,
target_override: Optional[str] = None,
threads_override: Optional[int] = None,
) -> "Profile":
@@ -328,8 +299,6 @@ defined in your profiles.yml file. You can find profiles.yml here:
disk as yaml and its values rendered with jinja.
:param profile_name: The profile name used.
:param renderer: The config renderer.
:param user_config: The global config for the user, if it
was present.
:param target_override: The target to use, if provided on
the command line.
:param threads_override: The thread count to use, if
@@ -338,9 +307,6 @@ defined in your profiles.yml file. You can find profiles.yml here:
target could not be found
:returns: The new Profile object.
"""
# user_config is not rendered.
if user_config is None:
user_config = raw_profile.get("config")
# TODO: should it be, and the values coerced to bool?
target_name, profile_data = cls.render_profile(
raw_profile, profile_name, target_override, renderer
@@ -361,7 +327,6 @@ defined in your profiles.yml file. You can find profiles.yml here:
profile_name=profile_name,
target_name=target_name,
threads=threads,
user_config=user_config,
)
@classmethod
@@ -396,13 +361,11 @@ defined in your profiles.yml file. You can find profiles.yml here:
if not raw_profile:
msg = f"Profile {profile_name} in profiles.yml is empty"
raise DbtProfileError(INVALID_PROFILE_MESSAGE.format(error_string=msg))
user_config = raw_profiles.get("config")
return cls.from_raw_profile_info(
raw_profile=raw_profile,
profile_name=profile_name,
renderer=renderer,
user_config=user_config,
target_override=target_override,
threads_override=threads_override,
)

View File

@@ -16,8 +16,13 @@ import os
from dbt.flags import get_flags
from dbt import deprecations
from dbt.constants import DEPENDENCIES_FILE_NAME, PACKAGES_FILE_NAME
from dbt.clients.system import path_exists, resolve_path_from_base, load_file_contents
from dbt.constants import (
DEPENDENCIES_FILE_NAME,
PACKAGES_FILE_NAME,
PACKAGE_LOCK_HASH_KEY,
DBT_PROJECT_FILE_NAME,
)
from dbt.clients.system import path_exists, load_file_contents
from dbt.clients.yaml_helper import load_yaml_text
from dbt.contracts.connection import QueryComment
from dbt.exceptions import (
@@ -31,12 +36,13 @@ from dbt.graph import SelectionSpec
from dbt.helper_types import NoValue
from dbt.semver import VersionSpecifier, versions_compatible
from dbt.version import get_installed_version
from dbt.utils import MultiDict, md5
from dbt.utils import MultiDict, md5, coerce_dict_str
from dbt.node_types import NodeType
from dbt.config.selectors import SelectorDict
from dbt.contracts.project import (
Project as ProjectContract,
SemverString,
ProjectFlags,
)
from dbt.contracts.project import PackageConfig, ProjectPackageMetadata
from dbt.dataclass_schema import ValidationError
@@ -77,8 +83,8 @@ Validator Error:
"""
MISSING_DBT_PROJECT_ERROR = """\
No dbt_project.yml found at expected path {path}
Verify that each entry within packages.yml (and their transitive dependencies) contains a file named dbt_project.yml
No {DBT_PROJECT_FILE_NAME} found at expected path {path}
Verify that each entry within packages.yml (and their transitive dependencies) contains a file named {DBT_PROJECT_FILE_NAME}
"""
@@ -94,16 +100,17 @@ def _load_yaml(path):
return load_yaml_text(contents)
def package_and_project_data_from_root(project_root):
package_filepath = resolve_path_from_base(PACKAGES_FILE_NAME, project_root)
dependencies_filepath = resolve_path_from_base(DEPENDENCIES_FILE_NAME, project_root)
def load_yml_dict(file_path):
ret = {}
if path_exists(file_path):
ret = _load_yaml(file_path) or {}
return ret
packages_yml_dict = {}
dependencies_yml_dict = {}
if path_exists(package_filepath):
packages_yml_dict = _load_yaml(package_filepath) or {}
if path_exists(dependencies_filepath):
dependencies_yml_dict = _load_yaml(dependencies_filepath) or {}
def package_and_project_data_from_root(project_root):
packages_yml_dict = load_yml_dict(f"{project_root}/{PACKAGES_FILE_NAME}")
dependencies_yml_dict = load_yml_dict(f"{project_root}/{DEPENDENCIES_FILE_NAME}")
if "packages" in packages_yml_dict and "packages" in dependencies_yml_dict:
msg = "The 'packages' key cannot be specified in both packages.yml and dependencies.yml"
@@ -123,10 +130,21 @@ def package_and_project_data_from_root(project_root):
return packages_dict, packages_specified_path
def package_config_from_data(packages_data: Dict[str, Any]) -> PackageConfig:
def package_config_from_data(
packages_data: Dict[str, Any],
unrendered_packages_data: Optional[Dict[str, Any]] = None,
) -> PackageConfig:
if not packages_data:
packages_data = {"packages": []}
# this depends on the two lists being in the same order
if unrendered_packages_data:
unrendered_packages_data = deepcopy(unrendered_packages_data)
for i in range(0, len(packages_data.get("packages", []))):
packages_data["packages"][i]["unrendered"] = unrendered_packages_data["packages"][i]
if PACKAGE_LOCK_HASH_KEY in packages_data:
packages_data.pop(PACKAGE_LOCK_HASH_KEY)
try:
PackageConfig.validate(packages_data)
packages = PackageConfig.from_dict(packages_data)
@@ -183,16 +201,20 @@ def value_or(value: Optional[T], default: T) -> T:
def load_raw_project(project_root: str) -> Dict[str, Any]:
project_root = os.path.normpath(project_root)
project_yaml_filepath = os.path.join(project_root, "dbt_project.yml")
project_yaml_filepath = os.path.join(project_root, DBT_PROJECT_FILE_NAME)
# get the project.yml contents
if not path_exists(project_yaml_filepath):
raise DbtProjectError(MISSING_DBT_PROJECT_ERROR.format(path=project_yaml_filepath))
raise DbtProjectError(
MISSING_DBT_PROJECT_ERROR.format(
path=project_yaml_filepath, DBT_PROJECT_FILE_NAME=DBT_PROJECT_FILE_NAME
)
)
project_dict = _load_yaml(project_yaml_filepath)
if not isinstance(project_dict, dict):
raise DbtProjectError("dbt_project.yml does not parse to a dictionary")
raise DbtProjectError(f"{DBT_PROJECT_FILE_NAME} does not parse to a dictionary")
return project_dict
@@ -307,21 +329,21 @@ class PartialProject(RenderComponents):
selectors_dict=rendered_selectors,
)
# Called by Project.from_project_root (not PartialProject.from_project_root!)
# Called by Project.from_project_root which first calls PartialProject.from_project_root
def render(self, renderer: DbtProjectYamlRenderer) -> "Project":
try:
rendered = self.get_rendered(renderer)
return self.create_project(rendered)
except DbtProjectError as exc:
if exc.path is None:
exc.path = os.path.join(self.project_root, "dbt_project.yml")
exc.path = os.path.join(self.project_root, DBT_PROJECT_FILE_NAME)
raise
def render_package_metadata(self, renderer: PackageRenderer) -> ProjectPackageMetadata:
packages_data = renderer.render_data(self.packages_dict)
packages_config = package_config_from_data(packages_data)
packages_config = package_config_from_data(packages_data, self.packages_dict)
if not self.project_name:
raise DbtProjectError("Package dbt_project.yml must have a name!")
raise DbtProjectError(f"Package defined in {DBT_PROJECT_FILE_NAME} must have a name!")
return ProjectPackageMetadata(self.project_name, packages_config.packages)
def check_config_path(
@@ -332,7 +354,7 @@ class PartialProject(RenderComponents):
msg = (
"{deprecated_path} and {expected_path} cannot both be defined. The "
"`{deprecated_path}` config has been deprecated in favor of `{expected_path}`. "
"Please update your `dbt_project.yml` configuration to reflect this "
f"Please update your `{DBT_PROJECT_FILE_NAME}` configuration to reflect this "
"change."
)
raise DbtProjectError(
@@ -404,11 +426,11 @@ class PartialProject(RenderComponents):
docs_paths: List[str] = value_or(cfg.docs_paths, all_source_paths)
asset_paths: List[str] = value_or(cfg.asset_paths, [])
flags = get_flags()
global_flags = get_flags()
flag_target_path = str(flags.TARGET_PATH) if flags.TARGET_PATH else None
flag_target_path = str(global_flags.TARGET_PATH) if global_flags.TARGET_PATH else None
target_path: str = flag_or(flag_target_path, cfg.target_path, "target")
log_path: str = str(flags.LOG_PATH)
log_path: str = str(global_flags.LOG_PATH)
clean_targets: List[str] = value_or(cfg.clean_targets, [target_path])
packages_install_path: str = value_or(cfg.packages_install_path, "dbt_packages")
@@ -426,8 +448,11 @@ class PartialProject(RenderComponents):
sources: Dict[str, Any]
tests: Dict[str, Any]
metrics: Dict[str, Any]
semantic_models: Dict[str, Any]
saved_queries: Dict[str, Any]
exposures: Dict[str, Any]
vars_value: VarProvider
dbt_cloud: Dict[str, Any]
dispatch = cfg.dispatch
models = cfg.models
@@ -436,6 +461,8 @@ class PartialProject(RenderComponents):
sources = cfg.sources
tests = cfg.tests
metrics = cfg.metrics
semantic_models = cfg.semantic_models
saved_queries = cfg.saved_queries
exposures = cfg.exposures
if cfg.vars is None:
vars_dict: Dict[str, Any] = {}
@@ -449,8 +476,9 @@ class PartialProject(RenderComponents):
on_run_end: List[str] = value_or(cfg.on_run_end, [])
query_comment = _query_comment_from_cfg(cfg.query_comment)
packages: PackageConfig = package_config_from_data(rendered.packages_dict)
packages: PackageConfig = package_config_from_data(
rendered.packages_dict, unrendered.packages_dict
)
selectors = selector_config_from_data(rendered.selectors_dict)
manifest_selectors: Dict[str, Any] = {}
if rendered.selectors_dict and rendered.selectors_dict["selectors"]:
@@ -459,6 +487,8 @@ class PartialProject(RenderComponents):
manifest_selectors = SelectorDict.parse_from_selectors_list(
rendered.selectors_dict["selectors"]
)
dbt_cloud = cfg.dbt_cloud
project = Project(
project_name=name,
version=version,
@@ -492,12 +522,15 @@ class PartialProject(RenderComponents):
sources=sources,
tests=tests,
metrics=metrics,
semantic_models=semantic_models,
saved_queries=saved_queries,
exposures=exposures,
vars=vars_value,
config_version=cfg.config_version,
unrendered=unrendered,
project_env_vars=project_env_vars,
restrict_access=cfg.restrict_access,
dbt_cloud=dbt_cloud,
)
# sanity check - this means an internal issue
project.validate()
@@ -541,6 +574,12 @@ class PartialProject(RenderComponents):
packages_specified_path,
) = package_and_project_data_from_root(project_root)
selectors_dict = selector_data_from_root(project_root)
if "flags" in project_dict:
# We don't want to include "flags" in the Project,
# it goes in ProjectFlags
project_dict.pop("flags")
return cls.from_dicts(
project_root=project_root,
project_dict=project_dict,
@@ -598,6 +637,8 @@ class Project:
sources: Dict[str, Any]
tests: Dict[str, Any]
metrics: Dict[str, Any]
semantic_models: Dict[str, Any]
saved_queries: Dict[str, Any]
exposures: Dict[str, Any]
vars: VarProvider
dbt_version: List[VersionSpecifier]
@@ -609,6 +650,7 @@ class Project:
unrendered: RenderComponents
project_env_vars: Dict[str, Any]
restrict_access: bool
dbt_cloud: Dict[str, Any]
@property
def all_source_paths(self) -> List[str]:
@@ -673,11 +715,13 @@ class Project:
"sources": self.sources,
"tests": self.tests,
"metrics": self.metrics,
"semantic-models": self.semantic_models,
"saved-queries": self.saved_queries,
"exposures": self.exposures,
"vars": self.vars.to_dict(),
"require-dbt-version": [v.to_version_string() for v in self.dbt_version],
"config-version": self.config_version,
"restrict-access": self.restrict_access,
"dbt-cloud": self.dbt_cloud,
}
)
if self.query_comment:
@@ -739,3 +783,52 @@ class Project:
def project_target_path(self):
# If target_path is absolute, project_root will not be included
return os.path.join(self.project_root, self.target_path)
def read_project_flags(project_dir: str, profiles_dir: str) -> ProjectFlags:
try:
project_flags: Dict[str, Any] = {}
# Read project_flags from dbt_project.yml first
# Flags are instantiated before the project, so we don't
# want to throw an error for non-existence of dbt_project.yml here
# because it breaks things.
project_root = os.path.normpath(project_dir)
project_yaml_filepath = os.path.join(project_root, DBT_PROJECT_FILE_NAME)
if path_exists(project_yaml_filepath):
try:
project_dict = load_raw_project(project_root)
if "flags" in project_dict:
project_flags = project_dict.pop("flags")
except Exception:
# This is probably a yaml load error.The error will be reported
# later, when the project loads.
pass
from dbt.config.profile import read_profile
profile = read_profile(profiles_dir)
profile_project_flags: Optional[Dict[str, Any]] = {}
if profile:
profile_project_flags = coerce_dict_str(profile.get("config", {}))
if project_flags and profile_project_flags:
raise DbtProjectError(
f"Do not specify both 'config' in profiles.yml and 'flags' in {DBT_PROJECT_FILE_NAME}. "
"Using 'config' in profiles.yml is deprecated."
)
if profile_project_flags:
# This can't use WARN_ERROR or WARN_ERROR_OPTIONS because they're in
# the config that we're loading. Uses special "warn" method.
deprecations.warn("project-flags-moved")
project_flags = profile_project_flags
if project_flags is not None:
ProjectFlags.validate(project_flags)
return ProjectFlags.from_dict(project_flags)
except (DbtProjectError) as exc:
# We don't want to eat the DbtProjectError for UserConfig to ProjectFlags
raise exc
except (DbtRuntimeError, ValidationError):
pass
return ProjectFlags()

View File

@@ -74,7 +74,7 @@ def _list_if_none_or_string(value):
class ProjectPostprocessor(Dict[Keypath, Callable[[Any], Any]]):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self[("on-run-start",)] = _list_if_none_or_string

View File

@@ -20,7 +20,7 @@ from dbt.adapters.factory import get_include_paths, get_relation_class_by_name
from dbt.config.project import load_raw_project
from dbt.contracts.connection import AdapterRequiredConfig, Credentials, HasCredentials
from dbt.contracts.graph.manifest import ManifestMetadata
from dbt.contracts.project import Configuration, UserConfig
from dbt.contracts.project import Configuration
from dbt.contracts.relation import ComponentName
from dbt.dataclass_schema import ValidationError
from dbt.events.functions import warn_or_error
@@ -167,6 +167,8 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
sources=project.sources,
tests=project.tests,
metrics=project.metrics,
semantic_models=project.semantic_models,
saved_queries=project.saved_queries,
exposures=project.exposures,
vars=project.vars,
config_version=project.config_version,
@@ -176,12 +178,12 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
profile_env_vars=profile.profile_env_vars,
profile_name=profile.profile_name,
target_name=profile.target_name,
user_config=profile.user_config,
threads=profile.threads,
credentials=profile.credentials,
args=args,
cli_vars=cli_vars,
dependencies=dependencies,
dbt_cloud=project.dbt_cloud,
)
# Called by 'load_projects' in this class
@@ -322,6 +324,8 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
"sources": self._get_config_paths(self.sources),
"tests": self._get_config_paths(self.tests),
"metrics": self._get_config_paths(self.metrics),
"semantic_models": self._get_config_paths(self.semantic_models),
"saved_queries": self._get_config_paths(self.saved_queries),
"exposures": self._get_config_paths(self.exposures),
}
@@ -404,7 +408,7 @@ class RuntimeConfig(Project, Profile, AdapterRequiredConfig):
class UnsetCredentials(Credentials):
def __init__(self):
def __init__(self) -> None:
super().__init__("", "")
@property
@@ -427,7 +431,6 @@ class UnsetCredentials(Credentials):
class UnsetProfile(Profile):
def __init__(self):
self.credentials = UnsetCredentials()
self.user_config = UserConfig() # This will be read in _get_rendered_profile
self.profile_name = ""
self.target_name = ""
self.threads = -1

View File

@@ -1,4 +1,4 @@
SECRET_ENV_PREFIX = "DBT_ENV_SECRET_"
SECRET_ENV_PREFIX = "DBT_ENV_SECRET"
DEFAULT_ENV_PLACEHOLDER = "DBT_DEFAULT_PLACEHOLDER"
METADATA_ENV_PREFIX = "DBT_ENV_CUSTOM_ENV_"
@@ -9,8 +9,11 @@ PIN_PACKAGE_URL = (
"https://docs.getdbt.com/docs/package-management#section-specifying-package-versions"
)
DBT_PROJECT_FILE_NAME = "dbt_project.yml"
PACKAGES_FILE_NAME = "packages.yml"
DEPENDENCIES_FILE_NAME = "dependencies.yml"
PACKAGE_LOCK_FILE_NAME = "package-lock.yml"
MANIFEST_FILE_NAME = "manifest.json"
SEMANTIC_MANIFEST_FILE_NAME = "semantic_manifest.json"
PARTIAL_PARSE_FILE_NAME = "partial_parse.msgpack"
PACKAGE_LOCK_HASH_KEY = "sha1_hash"

View File

@@ -1,6 +1,8 @@
from __future__ import annotations
import json
import os
from typing import Any, Dict, NoReturn, Optional, Mapping, Iterable, Set, List
from typing import Any, Callable, Dict, NoReturn, Optional, Mapping, Iterable, Set, List
import threading
from dbt.flags import get_flags
@@ -86,33 +88,29 @@ def get_context_modules() -> Dict[str, Dict[str, Any]]:
class ContextMember:
def __init__(self, value, name=None):
def __init__(self, value: Any, name: Optional[str] = None) -> None:
self.name = name
self.inner = value
def key(self, default):
def key(self, default: str) -> str:
if self.name is None:
return default
return self.name
def contextmember(value):
if isinstance(value, str):
return lambda v: ContextMember(v, name=value)
return ContextMember(value)
def contextmember(value: Optional[str] = None) -> Callable:
return lambda v: ContextMember(v, name=value)
def contextproperty(value):
if isinstance(value, str):
return lambda v: ContextMember(property(v), name=value)
return ContextMember(property(value))
def contextproperty(value: Optional[str] = None) -> Callable:
return lambda v: ContextMember(property(v), name=value)
class ContextMeta(type):
def __new__(mcls, name, bases, dct):
context_members = {}
context_attrs = {}
new_dct = {}
def __new__(mcls, name, bases, dct: Dict[str, Any]) -> ContextMeta:
context_members: Dict[str, Any] = {}
context_attrs: Dict[str, Any] = {}
new_dct: Dict[str, Any] = {}
for base in bases:
context_members.update(getattr(base, "_context_members_", {}))
@@ -148,27 +146,28 @@ class Var:
return self._cli_vars
@property
def node_name(self):
def node_name(self) -> str:
if self._node is not None:
return self._node.name
else:
return "<Configuration>"
def get_missing_var(self, var_name):
raise RequiredVarNotFoundError(var_name, self._merged, self._node)
def get_missing_var(self, var_name: str) -> NoReturn:
# TODO function name implies a non exception resolution
raise RequiredVarNotFoundError(var_name, dict(self._merged), self._node)
def has_var(self, var_name: str):
def has_var(self, var_name: str) -> bool:
return var_name in self._merged
def get_rendered_var(self, var_name):
def get_rendered_var(self, var_name: str) -> Any:
raw = self._merged[var_name]
# if bool/int/float/etc are passed in, don't compile anything
if not isinstance(raw, str):
return raw
return get_rendered(raw, self._context)
return get_rendered(raw, dict(self._context))
def __call__(self, var_name, default=_VAR_NOTSET):
def __call__(self, var_name: str, default: Any = _VAR_NOTSET) -> Any:
if self.has_var(var_name):
return self.get_rendered_var(var_name)
elif default is not self._VAR_NOTSET:
@@ -178,13 +177,17 @@ class Var:
class BaseContext(metaclass=ContextMeta):
# subclass is TargetContext
def __init__(self, cli_vars):
self._ctx = {}
self.cli_vars = cli_vars
self.env_vars = {}
# Set by ContextMeta
_context_members_: Dict[str, Any]
_context_attrs_: Dict[str, Any]
def generate_builtins(self):
# subclass is TargetContext
def __init__(self, cli_vars: Dict[str, Any]) -> None:
self._ctx: Dict[str, Any] = {}
self.cli_vars: Dict[str, Any] = cli_vars
self.env_vars: Dict[str, Any] = {}
def generate_builtins(self) -> Dict[str, Any]:
builtins: Dict[str, Any] = {}
for key, value in self._context_members_.items():
if hasattr(value, "__get__"):
@@ -194,14 +197,14 @@ class BaseContext(metaclass=ContextMeta):
return builtins
# no dbtClassMixin so this is not an actual override
def to_dict(self):
def to_dict(self) -> Dict[str, Any]:
self._ctx["context"] = self._ctx
builtins = self.generate_builtins()
self._ctx["builtins"] = builtins
self._ctx.update(builtins)
return self._ctx
@contextproperty
@contextproperty()
def dbt_version(self) -> str:
"""The `dbt_version` variable returns the installed version of dbt that
is currently running. It can be used for debugging or auditing
@@ -221,7 +224,7 @@ class BaseContext(metaclass=ContextMeta):
"""
return dbt_version
@contextproperty
@contextproperty()
def var(self) -> Var:
"""Variables can be passed from your `dbt_project.yml` file into models
during compilation. These variables are useful for configuring packages
@@ -290,7 +293,7 @@ class BaseContext(metaclass=ContextMeta):
"""
return Var(self._ctx, self.cli_vars)
@contextmember
@contextmember()
def env_var(self, var: str, default: Optional[str] = None) -> str:
"""The env_var() function. Return the environment variable named 'var'.
If there is no such environment variable set, return the default.
@@ -318,7 +321,7 @@ class BaseContext(metaclass=ContextMeta):
if os.environ.get("DBT_MACRO_DEBUGGING"):
@contextmember
@contextmember()
@staticmethod
def debug():
"""Enter a debugger at this line in the compiled jinja code."""
@@ -357,7 +360,7 @@ class BaseContext(metaclass=ContextMeta):
"""
raise MacroReturn(data)
@contextmember
@contextmember()
@staticmethod
def fromjson(string: str, default: Any = None) -> Any:
"""The `fromjson` context method can be used to deserialize a json
@@ -378,7 +381,7 @@ class BaseContext(metaclass=ContextMeta):
except ValueError:
return default
@contextmember
@contextmember()
@staticmethod
def tojson(value: Any, default: Any = None, sort_keys: bool = False) -> Any:
"""The `tojson` context method can be used to serialize a Python
@@ -401,7 +404,7 @@ class BaseContext(metaclass=ContextMeta):
except ValueError:
return default
@contextmember
@contextmember()
@staticmethod
def fromyaml(value: str, default: Any = None) -> Any:
"""The fromyaml context method can be used to deserialize a yaml string
@@ -432,7 +435,7 @@ class BaseContext(metaclass=ContextMeta):
# safe_dump defaults to sort_keys=True, but we act like json.dumps (the
# opposite)
@contextmember
@contextmember()
@staticmethod
def toyaml(
value: Any, default: Optional[str] = None, sort_keys: bool = False
@@ -477,7 +480,7 @@ class BaseContext(metaclass=ContextMeta):
except TypeError:
return default
@contextmember
@contextmember()
@staticmethod
def set_strict(value: Iterable[Any]) -> Set[Any]:
"""The `set_strict` context method can be used to convert any iterable
@@ -519,7 +522,7 @@ class BaseContext(metaclass=ContextMeta):
except TypeError:
return default
@contextmember
@contextmember()
@staticmethod
def zip_strict(*args: Iterable[Any]) -> Iterable[Any]:
"""The `zip_strict` context method can be used to used to return
@@ -541,7 +544,7 @@ class BaseContext(metaclass=ContextMeta):
except TypeError as e:
raise ZipStrictWrongTypeError(e)
@contextmember
@contextmember()
@staticmethod
def log(msg: str, info: bool = False) -> str:
"""Logs a line to either the log file or stdout.
@@ -562,7 +565,7 @@ class BaseContext(metaclass=ContextMeta):
fire_event(JinjaLogDebug(msg=msg, node_info=get_node_info()))
return ""
@contextproperty
@contextproperty()
def run_started_at(self) -> Optional[datetime.datetime]:
"""`run_started_at` outputs the timestamp that this run started, e.g.
`2017-04-21 01:23:45.678`. The `run_started_at` variable is a Python
@@ -590,19 +593,19 @@ class BaseContext(metaclass=ContextMeta):
else:
return None
@contextproperty
@contextproperty()
def invocation_id(self) -> Optional[str]:
"""invocation_id outputs a UUID generated for this dbt run (useful for
auditing)
"""
return get_invocation_id()
@contextproperty
@contextproperty()
def thread_id(self) -> str:
"""thread_id outputs an ID for the current thread (useful for auditing)"""
return threading.current_thread().name
@contextproperty
@contextproperty()
def modules(self) -> Dict[str, Any]:
"""The `modules` variable in the Jinja context contains useful Python
modules for operating on data.
@@ -627,7 +630,7 @@ class BaseContext(metaclass=ContextMeta):
""" # noqa
return get_context_modules()
@contextproperty
@contextproperty()
def flags(self) -> Any:
"""The `flags` variable contains true/false values for flags provided
on the command line.
@@ -644,7 +647,7 @@ class BaseContext(metaclass=ContextMeta):
"""
return flags_module.get_flag_obj()
@contextmember
@contextmember()
@staticmethod
def print(msg: str) -> str:
"""Prints a line to stdout.
@@ -662,7 +665,7 @@ class BaseContext(metaclass=ContextMeta):
print(msg)
return ""
@contextmember
@contextmember()
@staticmethod
def diff_of_two_dicts(
dict_a: Dict[str, List[str]], dict_b: Dict[str, List[str]]
@@ -691,7 +694,7 @@ class BaseContext(metaclass=ContextMeta):
dict_diff.update({k: dict_a[k]})
return dict_diff
@contextmember
@contextmember()
@staticmethod
def local_md5(value: str) -> str:
"""Calculates an MD5 hash of the given string.

View File

@@ -19,7 +19,7 @@ class ConfiguredContext(TargetContext):
super().__init__(config.to_target_dict(), config.cli_vars)
self.config = config
@contextproperty
@contextproperty()
def project_name(self) -> str:
return self.config.project_name
@@ -80,11 +80,11 @@ class SchemaYamlContext(ConfiguredContext):
self._project_name = project_name
self.schema_yaml_vars = schema_yaml_vars
@contextproperty
@contextproperty()
def var(self) -> ConfiguredVar:
return ConfiguredVar(self._ctx, self.config, self._project_name)
@contextmember
@contextmember()
def env_var(self, var: str, default: Optional[str] = None) -> str:
return_value = None
if var.startswith(SECRET_ENV_PREFIX):
@@ -113,7 +113,7 @@ class MacroResolvingContext(ConfiguredContext):
def __init__(self, config):
super().__init__(config)
@contextproperty
@contextproperty()
def var(self) -> ConfiguredVar:
return ConfiguredVar(self._ctx, self.config, self.config.project_name)

View File

@@ -45,6 +45,10 @@ class UnrenderedConfig(ConfigSource):
model_configs = unrendered.get("tests")
elif resource_type == NodeType.Metric:
model_configs = unrendered.get("metrics")
elif resource_type == NodeType.SemanticModel:
model_configs = unrendered.get("semantic_models")
elif resource_type == NodeType.SavedQuery:
model_configs = unrendered.get("saved_queries")
elif resource_type == NodeType.Exposure:
model_configs = unrendered.get("exposures")
else:
@@ -70,6 +74,10 @@ class RenderedConfig(ConfigSource):
model_configs = self.project.tests
elif resource_type == NodeType.Metric:
model_configs = self.project.metrics
elif resource_type == NodeType.SemanticModel:
model_configs = self.project.semantic_models
elif resource_type == NodeType.SavedQuery:
model_configs = self.project.saved_queries
elif resource_type == NodeType.Exposure:
model_configs = self.project.exposures
else:
@@ -189,9 +197,21 @@ class ContextConfigGenerator(BaseContextConfigGenerator[C]):
def _update_from_config(self, result: C, partial: Dict[str, Any], validate: bool = False) -> C:
translated = self._active_project.credentials.translate_aliases(partial)
return result.update_from(
translated = self.translate_hook_names(translated)
updated = result.update_from(
translated, self._active_project.credentials.type, validate=validate
)
return updated
def translate_hook_names(self, project_dict):
# This is a kind of kludge because the fix for #6411 specifically allowed misspelling
# the hook field names in dbt_project.yml, which only ever worked because we didn't
# run validate on the dbt_project configs.
if "pre_hook" in project_dict:
project_dict["pre-hook"] = project_dict.pop("pre_hook")
if "post_hook" in project_dict:
project_dict["post-hook"] = project_dict.pop("post_hook")
return project_dict
def calculate_node_config_dict(
self,

View File

@@ -24,7 +24,7 @@ class DocsRuntimeContext(SchemaYamlContext):
self.node = node
self.manifest = manifest
@contextmember
@contextmember()
def doc(self, *args: str) -> str:
"""The `doc` function is used to reference docs blocks in schema.yml
files. It is analogous to the `ref` function. For more information,

View File

@@ -2,7 +2,6 @@ import functools
from typing import NoReturn
from dbt.events.functions import warn_or_error
from dbt.events.helpers import env_secrets, scrub_secrets
from dbt.events.types import JinjaLogWarning
from dbt.exceptions import (
@@ -26,6 +25,8 @@ from dbt.exceptions import (
ContractError,
ColumnTypeMissingError,
FailFastError,
scrub_secrets,
env_secrets,
)

View File

@@ -40,7 +40,7 @@ class MacroResolver:
self._build_internal_packages_namespace()
self._build_macros_by_name()
def _build_internal_packages_namespace(self):
def _build_internal_packages_namespace(self) -> None:
# Iterate in reverse-order and overwrite: the packages that are first
# in the list are the ones we want to "win".
self.internal_packages_namespace: MacroNamespace = {}
@@ -56,7 +56,7 @@ class MacroResolver:
# root package namespace
# non-internal packages (that aren't local or root)
# dbt internal packages
def _build_macros_by_name(self):
def _build_macros_by_name(self) -> None:
macros_by_name = {}
# all internal packages (already in the right order)
@@ -78,7 +78,7 @@ class MacroResolver:
self,
package_namespaces: Dict[str, MacroNamespace],
macro: Macro,
):
) -> None:
if macro.package_name in package_namespaces:
namespace = package_namespaces[macro.package_name]
else:
@@ -89,7 +89,7 @@ class MacroResolver:
raise DuplicateMacroNameError(macro, macro, macro.package_name)
package_namespaces[macro.package_name][macro.name] = macro
def add_macro(self, macro: Macro):
def add_macro(self, macro: Macro) -> None:
macro_name: str = macro.name
# internal macros (from plugins) will be processed separately from
@@ -103,11 +103,11 @@ class MacroResolver:
if macro.package_name == self.root_project_name:
self.root_package_macros[macro_name] = macro
def add_macros(self):
def add_macros(self) -> None:
for macro in self.macros.values():
self.add_macro(macro)
def get_macro(self, local_package, macro_name):
def get_macro(self, local_package, macro_name) -> Optional[Macro]:
local_package_macros = {}
# If the macro is explicitly prefixed with an internal namespace
# (e.g. 'dbt.some_macro'), look there first
@@ -125,7 +125,7 @@ class MacroResolver:
return self.macros_by_name[macro_name]
return None
def get_macro_id(self, local_package, macro_name):
def get_macro_id(self, local_package, macro_name) -> Optional[str]:
macro = self.get_macro(local_package, macro_name)
if macro is None:
return None

View File

@@ -67,7 +67,7 @@ class ManifestContext(ConfiguredContext):
dct.update(self.namespace)
return dct
@contextproperty
@contextproperty()
def context_macro_stack(self):
return self.macro_stack

View File

@@ -60,7 +60,6 @@ from dbt.exceptions import (
MetricArgsError,
MissingConfigError,
OperationsCannotRefEphemeralNodesError,
PackageNotInDepsError,
ParsingError,
RefBadContextError,
RefArgsError,
@@ -618,7 +617,7 @@ class RuntimeMetricResolver(BaseMetricResolver):
target_package=target_package,
)
return ResolvedMetricReference(target_metric, self.manifest, self.Relation)
return ResolvedMetricReference(target_metric, self.manifest)
# `var` implementations.
@@ -638,10 +637,8 @@ class ModelConfiguredVar(Var):
package_name = self._node.package_name
if package_name != self._config.project_name:
if package_name not in dependencies:
# I don't think this is actually reachable
raise PackageNotInDepsError(package_name, node=self._node)
yield dependencies[package_name]
if package_name in dependencies:
yield dependencies[package_name]
yield self._config
def _generate_merged(self) -> Mapping[str, Any]:
@@ -754,19 +751,19 @@ class ProviderContext(ManifestContext):
self.model,
)
@contextproperty
@contextproperty()
def dbt_metadata_envs(self) -> Dict[str, str]:
return get_metadata_vars()
@contextproperty
@contextproperty()
def invocation_args_dict(self):
return args_to_dict(self.config.args)
@contextproperty
@contextproperty()
def _sql_results(self) -> Dict[str, Optional[AttrDict]]:
return self.sql_results
@contextmember
@contextmember()
def load_result(self, name: str) -> Optional[AttrDict]:
if name in self.sql_results:
# handle the special case of "main" macro
@@ -787,7 +784,7 @@ class ProviderContext(ManifestContext):
# Handle trying to load a result that was never stored
return None
@contextmember
@contextmember()
def store_result(
self, name: str, response: Any, agate_table: Optional[agate.Table] = None
) -> str:
@@ -803,7 +800,7 @@ class ProviderContext(ManifestContext):
)
return ""
@contextmember
@contextmember()
def store_raw_result(
self,
name: str,
@@ -815,7 +812,7 @@ class ProviderContext(ManifestContext):
response = AdapterResponse(_message=message, code=code, rows_affected=rows_affected)
return self.store_result(name, response, agate_table)
@contextproperty
@contextproperty()
def validation(self):
def validate_any(*args) -> Callable[[T], None]:
def inner(value: T) -> None:
@@ -836,7 +833,7 @@ class ProviderContext(ManifestContext):
}
)
@contextmember
@contextmember()
def write(self, payload: str) -> str:
# macros/source defs aren't 'writeable'.
if isinstance(self.model, (Macro, SourceDefinition)):
@@ -845,11 +842,11 @@ class ProviderContext(ManifestContext):
self.model.write_node(self.config.project_root, self.model.build_path, payload)
return ""
@contextmember
@contextmember()
def render(self, string: str) -> str:
return get_rendered(string, self._ctx, self.model)
@contextmember
@contextmember()
def try_or_compiler_error(
self, message_if_exception: str, func: Callable, *args, **kwargs
) -> Any:
@@ -858,21 +855,32 @@ class ProviderContext(ManifestContext):
except Exception:
raise CompilationError(message_if_exception, self.model)
@contextmember
@contextmember()
def load_agate_table(self) -> agate.Table:
if not isinstance(self.model, SeedNode):
raise LoadAgateTableNotSeedError(self.model.resource_type, node=self.model)
assert self.model.root_path
path = os.path.join(self.model.root_path, self.model.original_file_path)
# include package_path for seeds defined in packages
package_path = (
os.path.join(self.config.packages_install_path, self.model.package_name)
if self.model.package_name != self.config.project_name
else "."
)
path = os.path.join(self.config.project_root, package_path, self.model.original_file_path)
if not os.path.exists(path):
assert self.model.root_path
path = os.path.join(self.model.root_path, self.model.original_file_path)
column_types = self.model.config.column_types
delimiter = self.model.config.delimiter
try:
table = agate_helper.from_csv(path, text_columns=column_types)
table = agate_helper.from_csv(path, text_columns=column_types, delimiter=delimiter)
except ValueError as e:
raise LoadAgateTableValueError(e, node=self.model)
table.original_abspath = os.path.abspath(path)
return table
@contextproperty
@contextproperty()
def ref(self) -> Callable:
"""The most important function in dbt is `ref()`; it's impossible to
build even moderately complex models without it. `ref()` is how you
@@ -913,11 +921,11 @@ class ProviderContext(ManifestContext):
"""
return self.provider.ref(self.db_wrapper, self.model, self.config, self.manifest)
@contextproperty
@contextproperty()
def source(self) -> Callable:
return self.provider.source(self.db_wrapper, self.model, self.config, self.manifest)
@contextproperty
@contextproperty()
def metric(self) -> Callable:
return self.provider.metric(self.db_wrapper, self.model, self.config, self.manifest)
@@ -978,7 +986,7 @@ class ProviderContext(ManifestContext):
""" # noqa
return self.provider.Config(self.model, self.context_config)
@contextproperty
@contextproperty()
def execute(self) -> bool:
"""`execute` is a Jinja variable that returns True when dbt is in
"execute" mode.
@@ -1039,7 +1047,7 @@ class ProviderContext(ManifestContext):
""" # noqa
return self.provider.execute
@contextproperty
@contextproperty()
def exceptions(self) -> Dict[str, Any]:
"""The exceptions namespace can be used to raise warnings and errors in
dbt userspace.
@@ -1077,15 +1085,15 @@ class ProviderContext(ManifestContext):
""" # noqa
return wrapped_exports(self.model)
@contextproperty
@contextproperty()
def database(self) -> str:
return self.config.credentials.database
@contextproperty
@contextproperty()
def schema(self) -> str:
return self.config.credentials.schema
@contextproperty
@contextproperty()
def var(self) -> ModelConfiguredVar:
return self.provider.Var(
context=self._ctx,
@@ -1102,22 +1110,22 @@ class ProviderContext(ManifestContext):
"""
return self.db_wrapper
@contextproperty
@contextproperty()
def api(self) -> Dict[str, Any]:
return {
"Relation": self.db_wrapper.Relation,
"Column": self.adapter.Column,
}
@contextproperty
@contextproperty()
def column(self) -> Type[Column]:
return self.adapter.Column
@contextproperty
@contextproperty()
def env(self) -> Dict[str, Any]:
return self.target
@contextproperty
@contextproperty()
def graph(self) -> Dict[str, Any]:
"""The `graph` context variable contains information about the nodes in
your dbt project. Models, sources, tests, and snapshots are all
@@ -1226,30 +1234,42 @@ class ProviderContext(ManifestContext):
@contextproperty("model")
def ctx_model(self) -> Dict[str, Any]:
ret = self.model.to_dict(omit_none=True)
model_dct = self.model.to_dict(omit_none=True)
# Maintain direct use of compiled_sql
# TODO add depreciation logic[CT-934]
if "compiled_code" in ret:
ret["compiled_sql"] = ret["compiled_code"]
return ret
if "compiled_code" in model_dct:
model_dct["compiled_sql"] = model_dct["compiled_code"]
@contextproperty
if (
hasattr(self.model, "contract")
and self.model.contract.alias_types is True
and "columns" in model_dct
):
for column in model_dct["columns"].values():
if "data_type" in column:
orig_data_type = column["data_type"]
# translate data_type to value in Column.TYPE_LABELS
new_data_type = self.adapter.Column.translate_type(orig_data_type)
column["data_type"] = new_data_type
return model_dct
@contextproperty()
def pre_hooks(self) -> Optional[List[Dict[str, Any]]]:
return None
@contextproperty
@contextproperty()
def post_hooks(self) -> Optional[List[Dict[str, Any]]]:
return None
@contextproperty
@contextproperty()
def sql(self) -> Optional[str]:
return None
@contextproperty
@contextproperty()
def sql_now(self) -> str:
return self.adapter.date_function()
@contextmember
@contextmember()
def adapter_macro(self, name: str, *args, **kwargs):
"""This was deprecated in v0.18 in favor of adapter.dispatch"""
msg = (
@@ -1261,7 +1281,7 @@ class ProviderContext(ManifestContext):
)
raise CompilationError(msg)
@contextmember
@contextmember()
def env_var(self, var: str, default: Optional[str] = None) -> str:
"""The env_var() function. Return the environment variable named 'var'.
If there is no such environment variable set, return the default.
@@ -1305,7 +1325,7 @@ class ProviderContext(ManifestContext):
else:
raise EnvVarMissingError(var)
@contextproperty
@contextproperty()
def selected_resources(self) -> List[str]:
"""The `selected_resources` variable contains a list of the resources
selected based on the parameters provided to the dbt command.
@@ -1314,7 +1334,7 @@ class ProviderContext(ManifestContext):
"""
return selected_resources.SELECTED_RESOURCES
@contextmember
@contextmember()
def submit_python_job(self, parsed_model: Dict, compiled_code: str) -> AdapterResponse:
# Check macro_stack and that the unique id is for a materialization macro
if not (
@@ -1357,7 +1377,7 @@ class MacroContext(ProviderContext):
class ModelContext(ProviderContext):
model: ManifestNode
@contextproperty
@contextproperty()
def pre_hooks(self) -> List[Dict[str, Any]]:
if self.model.resource_type in [NodeType.Source, NodeType.Test]:
return []
@@ -1366,7 +1386,7 @@ class ModelContext(ProviderContext):
h.to_dict(omit_none=True) for h in self.model.config.pre_hook # type: ignore[union-attr] # noqa
]
@contextproperty
@contextproperty()
def post_hooks(self) -> List[Dict[str, Any]]:
if self.model.resource_type in [NodeType.Source, NodeType.Test]:
return []
@@ -1375,7 +1395,7 @@ class ModelContext(ProviderContext):
h.to_dict(omit_none=True) for h in self.model.config.post_hook # type: ignore[union-attr] # noqa
]
@contextproperty
@contextproperty()
def sql(self) -> Optional[str]:
# only doing this in sql model for backward compatible
if self.model.language == ModelLanguage.sql: # type: ignore[union-attr]
@@ -1392,7 +1412,7 @@ class ModelContext(ProviderContext):
else:
return None
@contextproperty
@contextproperty()
def compiled_code(self) -> Optional[str]:
if getattr(self.model, "defer_relation", None):
# TODO https://github.com/dbt-labs/dbt-core/issues/7976
@@ -1403,15 +1423,15 @@ class ModelContext(ProviderContext):
else:
return None
@contextproperty
@contextproperty()
def database(self) -> str:
return getattr(self.model, "database", self.config.credentials.database)
@contextproperty
@contextproperty()
def schema(self) -> str:
return getattr(self.model, "schema", self.config.credentials.schema)
@contextproperty
@contextproperty()
def this(self) -> Optional[RelationProxy]:
"""`this` makes available schema information about the currently
executing model. It's is useful in any context in which you need to
@@ -1446,7 +1466,7 @@ class ModelContext(ProviderContext):
return None
return self.db_wrapper.Relation.create_from(self.config, self.model)
@contextproperty
@contextproperty()
def defer_relation(self) -> Optional[RelationProxy]:
"""
For commands which add information about this node's corresponding
@@ -1660,7 +1680,7 @@ class TestContext(ProviderContext):
)
self.namespace = macro_namespace
@contextmember
@contextmember()
def env_var(self, var: str, default: Optional[str] = None) -> str:
return_value = None
if var.startswith(SECRET_ENV_PREFIX):

View File

@@ -14,14 +14,14 @@ class SecretContext(BaseContext):
"""This context is used in profiles.yml + packages.yml. It can render secret
env vars that aren't usable elsewhere"""
@contextmember
@contextmember()
def env_var(self, var: str, default: Optional[str] = None) -> str:
"""The env_var() function. Return the environment variable named 'var'.
If there is no such environment variable set, return the default.
If the default is None, raise an exception for an undefined variable.
In this context *only*, env_var will accept env vars prefixed with DBT_ENV_SECRET_.
In this context *only*, env_var will accept env vars prefixed with DBT_ENV_SECRET.
It will return the name of the secret env var, wrapped in 'start' and 'end' identifiers.
The actual value will be subbed in later in SecretRenderer.render_value()
"""

View File

@@ -9,7 +9,7 @@ class TargetContext(BaseContext):
super().__init__(cli_vars=cli_vars)
self.target_dict = target_dict
@contextproperty
@contextproperty()
def target(self) -> Dict[str, Any]:
"""`target` contains information about your connection to the warehouse
(specified in profiles.yml). Some configs are shared between all

View File

@@ -16,26 +16,21 @@ from dbt.utils import translate_aliases, md5
from dbt.events.functions import fire_event
from dbt.events.types import NewConnectionOpening
from dbt.events.contextvars import get_node_info
from typing_extensions import Protocol
from typing_extensions import Protocol, Annotated
from dbt.dataclass_schema import (
dbtClassMixin,
StrEnum,
ExtensibleDbtClassMixin,
HyphenatedDbtClassMixin,
ValidatedStringMixin,
register_pattern,
)
from dbt.contracts.util import Replaceable
from mashumaro.jsonschema.annotations import Pattern
class Identifier(ValidatedStringMixin):
ValidationRegex = r"^[A-Za-z_][A-Za-z0-9_]+$"
# we need register_pattern for jsonschema validation
register_pattern(Identifier, r"^[A-Za-z_][A-Za-z0-9_]+$")
@dataclass
class AdapterResponse(dbtClassMixin):
_message: str
@@ -55,7 +50,8 @@ class ConnectionState(StrEnum):
@dataclass(init=False)
class Connection(ExtensibleDbtClassMixin, Replaceable):
type: Identifier
# Annotated is used by mashumaro for jsonschema generation
type: Annotated[Identifier, Pattern(r"^[A-Za-z_][A-Za-z0-9_]+$")]
name: Optional[str] = None
state: ConnectionState = ConnectionState.INIT
transaction_open: bool = False
@@ -108,7 +104,7 @@ class LazyHandle:
connection, updating the handle on the Connection.
"""
def __init__(self, opener: Callable[[Connection], Connection]):
def __init__(self, opener: Callable[[Connection], Connection]) -> None:
self.opener = opener
def resolve(self, connection: Connection) -> Connection:
@@ -161,6 +157,7 @@ class Credentials(ExtensibleDbtClassMixin, Replaceable, metaclass=abc.ABCMeta):
@classmethod
def __pre_deserialize__(cls, data):
data = super().__pre_deserialize__(data)
# Need to fixup dbname => database, pass => password
data = cls.translate_aliases(data)
return data
@@ -181,17 +178,9 @@ class Credentials(ExtensibleDbtClassMixin, Replaceable, metaclass=abc.ABCMeta):
return dct
class UserConfigContract(Protocol):
send_anonymous_usage_stats: bool
use_colors: Optional[bool] = None
partial_parse: Optional[bool] = None
printer_width: Optional[int] = None
class HasCredentials(Protocol):
credentials: Credentials
profile_name: str
user_config: UserConfigContract
target_name: str
threads: int
@@ -220,10 +209,10 @@ DEFAULT_QUERY_COMMENT = """
@dataclass
class QueryComment(HyphenatedDbtClassMixin):
class QueryComment(dbtClassMixin):
comment: str = DEFAULT_QUERY_COMMENT
append: bool = False
job_label: bool = False
job_label: bool = field(default=False, metadata={"alias": "job-label"})
class AdapterRequiredConfig(HasCredentials, Protocol):

View File

@@ -225,10 +225,13 @@ class SchemaSourceFile(BaseSourceFile):
sources: List[str] = field(default_factory=list)
exposures: List[str] = field(default_factory=list)
metrics: List[str] = field(default_factory=list)
# metrics generated from semantic_model measures
generated_metrics: List[str] = field(default_factory=list)
groups: List[str] = field(default_factory=list)
# node patches contain models, seeds, snapshots, analyses
ndp: List[str] = field(default_factory=list)
semantic_models: List[str] = field(default_factory=list)
saved_queries: List[str] = field(default_factory=list)
# any macro patches in this file by macro unique_id.
mcp: Dict[str, str] = field(default_factory=dict)
# any source patches in this file. The entries are package, name pairs

View File

@@ -20,10 +20,12 @@ from typing import (
Generic,
AbstractSet,
ClassVar,
Iterable,
)
from typing_extensions import Protocol
from uuid import UUID
from dbt.contracts.graph.nodes import (
BaseNode,
Documentation,
@@ -37,6 +39,7 @@ from dbt.contracts.graph.nodes import (
ModelNode,
DeferRelation,
ResultNode,
SavedQuery,
SemanticModel,
SourceDefinition,
UnpatchedSourceDefinition,
@@ -44,7 +47,13 @@ from dbt.contracts.graph.nodes import (
from dbt.contracts.graph.unparsed import SourcePatch, NodeVersion, UnparsedVersion
from dbt.contracts.graph.manifest_upgrade import upgrade_manifest_json
from dbt.contracts.files import SourceFile, SchemaSourceFile, FileHash, AnySourceFile
from dbt.contracts.util import BaseArtifactMetadata, SourceKey, ArtifactMixin, schema_version
from dbt.contracts.util import (
BaseArtifactMetadata,
SourceKey,
ArtifactMixin,
schema_version,
get_artifact_schema_version,
)
from dbt.dataclass_schema import dbtClassMixin
from dbt.exceptions import (
CompilationError,
@@ -59,7 +68,7 @@ from dbt.events.types import MergedFromState, UnpinnedRefNewVersionAvailable
from dbt.events.contextvars import get_node_info
from dbt.node_types import NodeType, AccessType
from dbt.flags import get_flags, MP_CONTEXT
from dbt import tracking
from dbt import tracking, deprecations
import dbt.utils
@@ -88,7 +97,7 @@ def find_unique_id_for_package(storage, key, package: Optional[PackageName]):
class DocLookup(dbtClassMixin):
def __init__(self, manifest: "Manifest"):
def __init__(self, manifest: "Manifest") -> None:
self.storage: Dict[str, Dict[PackageName, UniqueID]] = {}
self.populate(manifest)
@@ -119,7 +128,7 @@ class DocLookup(dbtClassMixin):
class SourceLookup(dbtClassMixin):
def __init__(self, manifest: "Manifest"):
def __init__(self, manifest: "Manifest") -> None:
self.storage: Dict[str, Dict[PackageName, UniqueID]] = {}
self.populate(manifest)
@@ -156,7 +165,7 @@ class RefableLookup(dbtClassMixin):
_lookup_types: ClassVar[set] = set(NodeType.refable())
_versioned_types: ClassVar[set] = set(NodeType.versioned())
def __init__(self, manifest: "Manifest"):
def __init__(self, manifest: "Manifest") -> None:
self.storage: Dict[str, Dict[PackageName, UniqueID]] = {}
self.populate(manifest)
@@ -267,7 +276,7 @@ class RefableLookup(dbtClassMixin):
class MetricLookup(dbtClassMixin):
def __init__(self, manifest: "Manifest"):
def __init__(self, manifest: "Manifest") -> None:
self.storage: Dict[str, Dict[PackageName, UniqueID]] = {}
self.populate(manifest)
@@ -299,6 +308,41 @@ class MetricLookup(dbtClassMixin):
return manifest.metrics[unique_id]
class SavedQueryLookup(dbtClassMixin):
"""Lookup utility for finding SavedQuery nodes"""
def __init__(self, manifest: "Manifest") -> None:
self.storage: Dict[str, Dict[PackageName, UniqueID]] = {}
self.populate(manifest)
def get_unique_id(self, search_name, package: Optional[PackageName]):
return find_unique_id_for_package(self.storage, search_name, package)
def find(self, search_name, package: Optional[PackageName], manifest: "Manifest"):
unique_id = self.get_unique_id(search_name, package)
if unique_id is not None:
return self.perform_lookup(unique_id, manifest)
return None
def add_saved_query(self, saved_query: SavedQuery):
if saved_query.search_name not in self.storage:
self.storage[saved_query.search_name] = {}
self.storage[saved_query.search_name][saved_query.package_name] = saved_query.unique_id
def populate(self, manifest):
for saved_query in manifest.saved_queries.values():
if hasattr(saved_query, "name"):
self.add_saved_query(saved_query)
def perform_lookup(self, unique_id: UniqueID, manifest: "Manifest") -> SavedQuery:
if unique_id not in manifest.saved_queries:
raise dbt.exceptions.DbtInternalError(
f"SavedQUery {unique_id} found in cache but not found in manifest"
)
return manifest.saved_queries[unique_id]
class SemanticModelByMeasureLookup(dbtClassMixin):
"""Lookup utility for finding SemanticModel by measure
@@ -306,7 +350,7 @@ class SemanticModelByMeasureLookup(dbtClassMixin):
the semantic models in a manifest.
"""
def __init__(self, manifest: "Manifest"):
def __init__(self, manifest: "Manifest") -> None:
self.storage: DefaultDict[str, Dict[PackageName, UniqueID]] = defaultdict(dict)
self.populate(manifest)
@@ -331,20 +375,31 @@ class SemanticModelByMeasureLookup(dbtClassMixin):
"""Populate storage with all the measure + package paths to the Manifest's SemanticModels"""
for semantic_model in manifest.semantic_models.values():
self.add(semantic_model=semantic_model)
for disabled in manifest.disabled.values():
for node in disabled:
if isinstance(node, SemanticModel):
self.add(semantic_model=node)
def perform_lookup(self, unique_id: UniqueID, manifest: "Manifest") -> SemanticModel:
"""Tries to get a SemanticModel from the Manifest"""
semantic_model = manifest.semantic_models.get(unique_id)
if semantic_model is None:
enabled_semantic_model: Optional[SemanticModel] = manifest.semantic_models.get(unique_id)
disabled_semantic_model: Optional[List] = manifest.disabled.get(unique_id)
if isinstance(enabled_semantic_model, SemanticModel):
return enabled_semantic_model
elif disabled_semantic_model is not None and isinstance(
disabled_semantic_model[0], SemanticModel
):
return disabled_semantic_model[0]
else:
raise dbt.exceptions.DbtInternalError(
f"Semantic model `{unique_id}` found in cache but not found in manifest"
)
return semantic_model
# This handles both models/seeds/snapshots and sources/metrics/exposures
# This handles both models/seeds/snapshots and sources/metrics/exposures/semantic_models
class DisabledLookup(dbtClassMixin):
def __init__(self, manifest: "Manifest"):
def __init__(self, manifest: "Manifest") -> None:
self.storage: Dict[str, Dict[PackageName, List[Any]]] = {}
self.populate(manifest)
@@ -562,11 +617,29 @@ M = TypeVar("M", bound=MacroCandidate)
class CandidateList(List[M]):
def last(self) -> Optional[Macro]:
def last_candidate(
self, valid_localities: Optional[List[Locality]] = None
) -> Optional[MacroCandidate]:
"""
Obtain the last (highest precedence) MacroCandidate from the CandidateList of any locality in valid_localities.
If valid_localities is not specified, return the last MacroCandidate of any locality.
"""
if not self:
return None
self.sort()
return self[-1].macro
if valid_localities is None:
return self[-1]
for candidate in reversed(self):
if candidate.locality in valid_localities:
return candidate
return None
def last(self) -> Optional[Macro]:
last_candidate = self.last_candidate()
return last_candidate.macro if last_candidate is not None else None
def _get_locality(macro: Macro, root_project_name: str, internal_packages: Set[str]) -> Locality:
@@ -598,6 +671,9 @@ class Disabled(Generic[D]):
MaybeMetricNode = Optional[Union[Metric, Disabled[Metric]]]
MaybeSavedQueryNode = Optional[Union[SavedQuery, Disabled[SavedQuery]]]
MaybeDocumentation = Optional[Documentation]
@@ -742,6 +818,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
disabled: MutableMapping[str, List[GraphMemberNode]] = field(default_factory=dict)
env_vars: MutableMapping[str, str] = field(default_factory=dict)
semantic_models: MutableMapping[str, SemanticModel] = field(default_factory=dict)
saved_queries: MutableMapping[str, SavedQuery] = field(default_factory=dict)
_doc_lookup: Optional[DocLookup] = field(
default=None, metadata={"serialize": lambda x: None, "deserialize": lambda x: None}
@@ -755,6 +832,9 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
_metric_lookup: Optional[MetricLookup] = field(
default=None, metadata={"serialize": lambda x: None, "deserialize": lambda x: None}
)
_saved_query_lookup: Optional[SavedQueryLookup] = field(
default=None, metadata={"serialize": lambda x: None, "deserialize": lambda x: None}
)
_semantic_model_by_measure_lookup: Optional[SemanticModelByMeasureLookup] = field(
default=None, metadata={"serialize": lambda x: None, "deserialize": lambda x: None}
)
@@ -799,6 +879,9 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
"semantic_models": {
k: v.to_dict(omit_none=False) for k, v in self.semantic_models.items()
},
"saved_queries": {
k: v.to_dict(omit_none=False) for k, v in self.saved_queries.items()
},
}
def build_disabled_by_file_id(self):
@@ -850,7 +933,33 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
for specificity, atype in enumerate(self._get_parent_adapter_types(adapter_type))
)
)
return candidates.last()
core_candidates = [
candidate for candidate in candidates if candidate.locality == Locality.Core
]
materialization_candidate = candidates.last_candidate()
# If an imported materialization macro was found that also had a core candidate, fire a deprecation
if (
materialization_candidate is not None
and materialization_candidate.locality == Locality.Imported
and core_candidates
):
# preserve legacy behaviour - allow materialization override
if (
get_flags().require_explicit_package_overrides_for_builtin_materializations
is False
):
deprecations.warn(
"package-materialization-override",
package_name=materialization_candidate.macro.package_name,
materialization_name=materialization_name,
)
else:
materialization_candidate = candidates.last_candidate(
valid_localities=[Locality.Core, Locality.Root]
)
return materialization_candidate.macro if materialization_candidate else None
def get_resource_fqns(self) -> Mapping[str, PathSet]:
resource_fqns: Dict[str, Set[Tuple[str, ...]]] = {}
@@ -860,6 +969,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
self.sources.values(),
self.metrics.values(),
self.semantic_models.values(),
self.saved_queries.values(),
)
for resource in all_resources:
resource_type_plural = resource.resource_type.pluralize()
@@ -895,6 +1005,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
files={k: _deepcopy(v) for k, v in self.files.items()},
state_check=_deepcopy(self.state_check),
semantic_models={k: _deepcopy(v) for k, v in self.semantic_models.items()},
saved_queries={k: _deepcopy(v) for k, v in self.saved_queries.items()},
)
copy.build_flat_graph()
return copy
@@ -907,6 +1018,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
self.exposures.values(),
self.metrics.values(),
self.semantic_models.values(),
self.saved_queries.values(),
)
)
forward_edges, backward_edges = build_node_edges(edge_members)
@@ -927,13 +1039,22 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
groupable_nodes = list(
chain(
self.nodes.values(),
self.saved_queries.values(),
self.semantic_models.values(),
self.metrics.values(),
)
)
group_map = {group.name: [] for group in self.groups.values()}
for node in groupable_nodes:
if node.group is not None:
group_map[node.group].append(node.unique_id)
# group updates are not included with state:modified and
# by ignoring the groups that aren't in the group map we
# can avoid hitting errors for groups that are not getting
# updated. This is a hack but any groups that are not
# valid will be caught in
# parser.manifest.ManifestLoader.check_valid_group_config_node
if node.group in group_map:
group_map[node.group].append(node.unique_id)
self.group_map = group_map
def writable_manifest(self) -> "WritableManifest":
@@ -954,6 +1075,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
parent_map=self.parent_map,
group_map=self.group_map,
semantic_models=self.semantic_models,
saved_queries=self.saved_queries,
)
def write(self, path):
@@ -972,6 +1094,8 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
return self.metrics[unique_id]
elif unique_id in self.semantic_models:
return self.semantic_models[unique_id]
elif unique_id in self.saved_queries:
return self.saved_queries[unique_id]
else:
# something terrible has happened
raise dbt.exceptions.DbtInternalError(
@@ -1008,6 +1132,13 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
self._metric_lookup = MetricLookup(self)
return self._metric_lookup
@property
def saved_query_lookup(self) -> SavedQueryLookup:
"""Retuns a SavedQueryLookup, instantiating it first if necessary."""
if self._saved_query_lookup is None:
self._saved_query_lookup = SavedQueryLookup(self)
return self._saved_query_lookup
@property
def semantic_model_by_measure_lookup(self) -> SemanticModelByMeasureLookup:
"""Gets (and creates if necessary) the lookup utility for getting SemanticModels by measures"""
@@ -1056,8 +1187,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
return resolved_refs
# Called by dbt.parser.manifest._process_refs_for_exposure, _process_refs_for_metric,
# and dbt.parser.manifest._process_refs_for_node
# Called by dbt.parser.manifest._process_refs & ManifestLoader.check_for_model_deprecations
def resolve_ref(
self,
source_node: GraphMemberNode,
@@ -1142,6 +1272,35 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
return Disabled(disabled[0])
return None
def resolve_saved_query(
self,
target_saved_query_name: str,
target_saved_query_package: Optional[str],
current_project: str,
node_package: str,
) -> MaybeSavedQueryNode:
"""Tries to find the SavedQuery by name within the available project and packages.
Will return the first enabled SavedQuery matching the name found while iterating over
the scoped packages. If no enabled SavedQuery node match is found, returns the last
disabled SavedQuery node. Otherwise it returns None.
"""
disabled: Optional[List[SavedQuery]] = None
candidates = _packages_to_search(current_project, node_package, target_saved_query_package)
for pkg in candidates:
saved_query = self.saved_query_lookup.find(target_saved_query_name, pkg, self)
if saved_query is not None and saved_query.config.enabled:
return saved_query
# it's possible that the node is disabled
if disabled is None:
disabled = self.disabled_lookup.find(f"{target_saved_query_name}", pkg)
if disabled:
return Disabled(disabled[0])
return None
def resolve_semantic_model_for_measure(
self,
target_measure_name: str,
@@ -1156,6 +1315,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
semantic_model = self.semantic_model_by_measure_lookup.find(
target_measure_name, pkg, self
)
# need to return it even if it's disabled so know it's not fully missing
if semantic_model is not None:
return semantic_model
@@ -1331,10 +1491,13 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
self.exposures[exposure.unique_id] = exposure
source_file.exposures.append(exposure.unique_id)
def add_metric(self, source_file: SchemaSourceFile, metric: Metric):
def add_metric(self, source_file: SchemaSourceFile, metric: Metric, generated: bool = False):
_check_duplicates(metric, self.metrics)
self.metrics[metric.unique_id] = metric
source_file.metrics.append(metric.unique_id)
if not generated:
source_file.metrics.append(metric.unique_id)
else:
source_file.generated_metrics.append(metric.unique_id)
def add_group(self, source_file: SchemaSourceFile, group: Group):
_check_duplicates(group, self.groups)
@@ -1356,6 +1519,10 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
source_file.add_test(node.unique_id, test_from)
if isinstance(node, Metric):
source_file.metrics.append(node.unique_id)
if isinstance(node, SavedQuery):
source_file.saved_queries.append(node.unique_id)
if isinstance(node, SemanticModel):
source_file.semantic_models.append(node.unique_id)
if isinstance(node, Exposure):
source_file.exposures.append(node.unique_id)
else:
@@ -1371,6 +1538,11 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
self.semantic_models[semantic_model.unique_id] = semantic_model
source_file.semantic_models.append(semantic_model.unique_id)
def add_saved_query(self, source_file: SchemaSourceFile, saved_query: SavedQuery) -> None:
_check_duplicates(saved_query, self.saved_queries)
self.saved_queries[saved_query.unique_id] = saved_query
source_file.saved_queries.append(saved_query.unique_id)
# end of methods formerly in ParseResult
# Provide support for copy.deepcopy() - we just need to avoid the lock!
@@ -1398,6 +1570,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
self.disabled,
self.env_vars,
self.semantic_models,
self.saved_queries,
self._doc_lookup,
self._source_lookup,
self._ref_lookup,
@@ -1410,19 +1583,19 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
class MacroManifest(MacroMethods):
def __init__(self, macros):
def __init__(self, macros) -> None:
self.macros = macros
self.metadata = ManifestMetadata()
# This is returned by the 'graph' context property
# in the ProviderContext class.
self.flat_graph = {}
self.flat_graph: Dict[str, Any] = {}
AnyManifest = Union[Manifest, MacroManifest]
@dataclass
@schema_version("manifest", 10)
@schema_version("manifest", 11)
class WritableManifest(ArtifactMixin):
nodes: Mapping[UniqueID, ManifestNode] = field(
metadata=dict(description=("The nodes defined in the dbt project and its dependencies"))
@@ -1468,6 +1641,9 @@ class WritableManifest(ArtifactMixin):
description="A mapping from group names to their nodes",
)
)
saved_queries: Mapping[UniqueID, SavedQuery] = field(
metadata=dict(description=("The saved queries defined in the dbt project"))
)
semantic_models: Mapping[UniqueID, SemanticModel] = field(
metadata=dict(description=("The semantic models defined in the dbt project"))
)
@@ -1478,7 +1654,7 @@ class WritableManifest(ArtifactMixin):
)
@classmethod
def compatible_previous_versions(self):
def compatible_previous_versions(cls) -> Iterable[Tuple[str, int]]:
return [
("manifest", 4),
("manifest", 5),
@@ -1486,14 +1662,15 @@ class WritableManifest(ArtifactMixin):
("manifest", 7),
("manifest", 8),
("manifest", 9),
("manifest", 10),
]
@classmethod
def upgrade_schema_version(cls, data):
"""This overrides the "upgrade_schema_version" call in VersionedSchema (via
ArtifactMixin) to modify the dictionary passed in from earlier versions of the manifest."""
manifest_schema_version = get_manifest_schema_version(data)
if manifest_schema_version <= 9:
manifest_schema_version = get_artifact_schema_version(data)
if manifest_schema_version <= 10:
data = upgrade_manifest_json(data, manifest_schema_version)
return cls.from_dict(data)
@@ -1506,13 +1683,6 @@ class WritableManifest(ArtifactMixin):
return dct
def get_manifest_schema_version(dct: dict) -> int:
schema_version = dct.get("metadata", {}).get("dbt_schema_version", None)
if not schema_version:
raise ValueError("Manifest doesn't have schema version")
return int(schema_version.split(".")[-2][-1])
def _check_duplicates(value: BaseNode, src: Mapping[str, BaseNode]):
if value.unique_id in src:
raise DuplicateResourceNameError(value, src[value.unique_id])

View File

@@ -62,10 +62,72 @@ def drop_v9_and_prior_metrics(manifest: dict) -> None:
manifest["disabled"] = filtered_disabled_entries
def _convert_dct_with_filter(v10_dct_with_opt_filter):
"""Upgrage the filter object from v10 to v11.
v10 filters from a serialized manifest looked like:
{..., 'filter': {'where_sql_template': '<filter_value>'}}
whereas v11 filters look like:
{..., 'filter': {'where_filters': [{'where_sql_template': '<filter_value>'}, ...]}}
"""
if v10_dct_with_opt_filter is not None and v10_dct_with_opt_filter.get("filter") is not None:
v10_dct_with_opt_filter["filter"] = {"where_filters": [v10_dct_with_opt_filter["filter"]]}
def _convert_metric(v10_metric_dict):
"""Upgrades a v10 metric object to a v11 metric object.
Specifcally the following properties change
1. metric.filter
2. metric.type_params.measure.filter
3. metric.type_params.input_measures[x].filter
4. metric.type_params.numerator.filter
5. metric.type_params.denominator.filter
6. metric.type_params.metrics[x].filter"
"""
# handles top level metric filter
_convert_dct_with_filter(v10_metric_dict)
type_params = v10_metric_dict.get("type_params")
if type_params is not None:
_convert_dct_with_filter(type_params.get("measure"))
_convert_dct_with_filter(type_params.get("numerator"))
_convert_dct_with_filter(type_params.get("denominator"))
# handles metric.type_params.input_measures[x].filter
input_measures = type_params.get("input_measures")
if input_measures is not None:
for input_measure in input_measures:
_convert_dct_with_filter(input_measure)
# handles metric.type_params.metrics[x].filter
metrics = type_params.get("metrics")
if metrics is not None:
for metric in metrics:
_convert_dct_with_filter(metric)
def upgrade_v10_metric_filters(manifest: dict):
"""Handles metric filters changes from v10 to v11."""
metrics = manifest.get("metrics", {})
for metric in metrics.values():
_convert_metric(metric)
disabled_nodes = manifest.get("disabled", {})
for unique_id, nodes in disabled_nodes.items():
if unique_id.split(".")[0] == "metric":
for node in nodes:
_convert_metric(node)
def upgrade_manifest_json(manifest: dict, manifest_schema_version: int) -> dict:
# this should remain 9 while the check in `upgrade_schema_version` may change
if manifest_schema_version <= 9:
drop_v9_and_prior_metrics(manifest=manifest)
elif manifest_schema_version == 10:
upgrade_v10_metric_filters(manifest=manifest)
for node_content in manifest.get("nodes", {}).values():
upgrade_node_content(node_content)
@@ -104,4 +166,6 @@ def upgrade_manifest_json(manifest: dict, manifest_schema_version: int) -> dict:
doc_content["resource_type"] = "doc"
if "semantic_models" not in manifest:
manifest["semantic_models"] = {}
if "saved_queries" not in manifest:
manifest["saved_queries"] = {}
return manifest

View File

@@ -1,8 +1,15 @@
from dbt.node_types import NodeType
from dbt.contracts.graph.manifest import Manifest, Metric
from dbt_semantic_interfaces.type_enums import MetricType
from typing import Any, Dict, Iterator, List
DERIVED_METRICS = [MetricType.DERIVED, MetricType.RATIO]
BASE_METRICS = [MetricType.SIMPLE, MetricType.CUMULATIVE, MetricType.CONVERSION]
class MetricReference(object):
def __init__(self, metric_name, package_name=None):
def __init__(self, metric_name, package_name=None) -> None:
self.metric_name = metric_name
self.package_name = package_name
@@ -17,76 +24,74 @@ class ResolvedMetricReference(MetricReference):
for working with metrics (ie. __str__ and templating functions)
"""
def __init__(self, node, manifest, Relation):
def __init__(self, node: Metric, manifest: Manifest) -> None:
super().__init__(node.name, node.package_name)
self.node = node
self.manifest = manifest
self.Relation = Relation
def __getattr__(self, key):
def __getattr__(self, key) -> Any:
return getattr(self.node, key)
def __str__(self):
def __str__(self) -> str:
return f"{self.node.name}"
@classmethod
def parent_metrics(cls, metric_node, manifest):
def parent_metrics(cls, metric_node: Metric, manifest: Manifest) -> Iterator[Metric]:
"""For a given metric, yeilds all upstream metrics."""
yield metric_node
for parent_unique_id in metric_node.depends_on.nodes:
node = manifest.metrics.get(parent_unique_id)
if node and node.resource_type == NodeType.Metric:
node = manifest.expect(parent_unique_id)
if isinstance(node, Metric):
yield from cls.parent_metrics(node, manifest)
@classmethod
def parent_metrics_names(cls, metric_node, manifest):
yield metric_node.name
for parent_unique_id in metric_node.depends_on.nodes:
node = manifest.metrics.get(parent_unique_id)
if node and node.resource_type == NodeType.Metric:
yield from cls.parent_metrics_names(node, manifest)
def parent_metrics_names(cls, metric_node: Metric, manifest: Manifest) -> Iterator[str]:
"""For a given metric, yeilds all upstream metric names"""
for metric in cls.parent_metrics(metric_node, manifest):
yield metric.name
@classmethod
def reverse_dag_parsing(cls, metric_node, manifest, metric_depth_count):
if metric_node.calculation_method == "derived":
yield {metric_node.name: metric_depth_count}
metric_depth_count = metric_depth_count + 1
def reverse_dag_parsing(
cls, metric_node: Metric, manifest: Manifest, metric_depth_count: int
) -> Iterator[Dict[str, int]]:
"""For the given metric, yeilds dictionaries having {<metric_name>: <depth_from_initial_metric} of upstream derived metrics.
for parent_unique_id in metric_node.depends_on.nodes:
node = manifest.metrics.get(parent_unique_id)
if (
node
and node.resource_type == NodeType.Metric
and node.calculation_method == "derived"
):
yield from cls.reverse_dag_parsing(node, manifest, metric_depth_count)
This function is intended as a helper function for other metric helper functions.
"""
if metric_node.type in DERIVED_METRICS:
yield {metric_node.name: metric_depth_count}
for parent_unique_id in metric_node.depends_on.nodes:
node = manifest.expect(parent_unique_id)
if isinstance(node, Metric):
yield from cls.reverse_dag_parsing(node, manifest, metric_depth_count + 1)
def full_metric_dependency(self):
"""Returns a unique list of all upstream metric names."""
to_return = list(set(self.parent_metrics_names(self.node, self.manifest)))
return to_return
def base_metric_dependency(self):
def base_metric_dependency(self) -> List[str]:
"""Returns a unique list of names for all upstream non-derived metrics."""
in_scope_metrics = list(self.parent_metrics(self.node, self.manifest))
base_metrics = {
metric.name for metric in in_scope_metrics if metric.type not in DERIVED_METRICS
}
to_return = []
for metric in in_scope_metrics:
if metric.calculation_method != "derived" and metric.name not in to_return:
to_return.append(metric.name)
return list(base_metrics)
return to_return
def derived_metric_dependency(self):
def derived_metric_dependency(self) -> List[str]:
"""Returns a unique list of names for all upstream derived metrics."""
in_scope_metrics = list(self.parent_metrics(self.node, self.manifest))
derived_metrics = {
metric.name for metric in in_scope_metrics if metric.type in DERIVED_METRICS
}
to_return = []
for metric in in_scope_metrics:
if metric.calculation_method == "derived" and metric.name not in to_return:
to_return.append(metric.name)
return list(derived_metrics)
return to_return
def derived_metric_dependency_depth(self):
def derived_metric_dependency_depth(self) -> List[Dict[str, int]]:
"""Returns a list of {<metric_name>: <depth_from_initial_metric>} for all upstream metrics."""
metric_depth_count = 1
to_return = list(self.reverse_dag_parsing(self.node, self.manifest, metric_depth_count))

View File

@@ -2,11 +2,11 @@ from dataclasses import field, Field, dataclass
from enum import Enum
from itertools import chain
from typing import Any, List, Optional, Dict, Union, Type, TypeVar, Callable
from typing_extensions import Annotated
from dbt.dataclass_schema import (
dbtClassMixin,
ValidationError,
register_pattern,
StrEnum,
)
from dbt.contracts.graph.unparsed import AdditionalPropertiesAllowed, Docs
@@ -14,7 +14,9 @@ from dbt.contracts.graph.utils import validate_color
from dbt.contracts.util import Replaceable, list_str
from dbt.exceptions import DbtInternalError, CompilationError
from dbt import hooks
from dbt.node_types import NodeType
from dbt.node_types import NodeType, AccessType
from dbt_semantic_interfaces.type_enums.export_destination_type import ExportDestinationType
from mashumaro.jsonschema.annotations import Pattern
M = TypeVar("M", bound="Metadata")
@@ -188,9 +190,6 @@ class Severity(str):
pass
register_pattern(Severity, insensitive_patterns("warn", "error"))
class OnConfigurationChangeOption(StrEnum):
Apply = "apply"
Continue = "continue"
@@ -204,6 +203,7 @@ class OnConfigurationChangeOption(StrEnum):
@dataclass
class ContractConfig(dbtClassMixin, Replaceable):
enforced: bool = False
alias_types: bool = True
@dataclass
@@ -218,7 +218,6 @@ T = TypeVar("T", bound="BaseConfig")
@dataclass
class BaseConfig(AdditionalPropertiesAllowed, Replaceable):
# enable syntax like: config['key']
def __getitem__(self, key):
return self.get(key)
@@ -376,25 +375,50 @@ class BaseConfig(AdditionalPropertiesAllowed, Replaceable):
self.validate(dct)
return self.from_dict(dct)
def replace(self, **kwargs):
dct = self.to_dict(omit_none=True)
mapping = self.field_mapping()
for key, value in kwargs.items():
new_key = mapping.get(key, key)
dct[new_key] = value
return self.from_dict(dct)
@dataclass
class SemanticModelConfig(BaseConfig):
enabled: bool = True
group: Optional[str] = field(
default=None,
metadata=CompareBehavior.Exclude.meta(),
)
meta: Dict[str, Any] = field(
default_factory=dict,
metadata=MergeBehavior.Update.meta(),
)
@dataclass
class SavedQueryConfig(BaseConfig):
"""Where config options for SavedQueries are stored.
This class is much like many other node config classes. It's likely that
this class will expand in the direction of what's in the `NodeAndTestConfig`
class. It might make sense to clean the various *Config classes into one at
some point.
"""
enabled: bool = True
group: Optional[str] = field(
default=None,
metadata=CompareBehavior.Exclude.meta(),
)
meta: Dict[str, Any] = field(
default_factory=dict,
metadata=MergeBehavior.Update.meta(),
)
export_as: Optional[ExportDestinationType] = None
schema: Optional[str] = None
@dataclass
class MetricConfig(BaseConfig):
enabled: bool = True
group: Optional[str] = None
group: Optional[str] = field(
default=None,
metadata=CompareBehavior.Exclude.meta(),
)
@dataclass
@@ -447,11 +471,11 @@ class NodeConfig(NodeAndTestConfig):
persist_docs: Dict[str, Any] = field(default_factory=dict)
post_hook: List[Hook] = field(
default_factory=list,
metadata=MergeBehavior.Append.meta(),
metadata={"merge": MergeBehavior.Append, "alias": "post-hook"},
)
pre_hook: List[Hook] = field(
default_factory=list,
metadata=MergeBehavior.Append.meta(),
metadata={"merge": MergeBehavior.Append, "alias": "pre-hook"},
)
quoting: Dict[str, Any] = field(
default_factory=dict,
@@ -511,39 +535,29 @@ class NodeConfig(NodeAndTestConfig):
@classmethod
def __pre_deserialize__(cls, data):
data = super().__pre_deserialize__(data)
field_map = {"post-hook": "post_hook", "pre-hook": "pre_hook"}
# create a new dict because otherwise it gets overwritten in
# tests
new_dict = {}
for key in data:
new_dict[key] = data[key]
data = new_dict
for key in hooks.ModelHookType:
if key in data:
data[key] = [hooks.get_hook_dict(h) for h in data[key]]
for field_name in field_map:
if field_name in data:
new_name = field_map[field_name]
data[new_name] = data.pop(field_name)
return data
def __post_serialize__(self, dct):
dct = super().__post_serialize__(dct)
field_map = {"post_hook": "post-hook", "pre_hook": "pre-hook"}
for field_name in field_map:
if field_name in dct:
dct[field_map[field_name]] = dct.pop(field_name)
return dct
# this is still used by jsonschema validation
@classmethod
def field_mapping(cls):
return {"post_hook": "post-hook", "pre_hook": "pre-hook"}
@dataclass
class ModelConfig(NodeConfig):
access: AccessType = field(
default=AccessType.Protected,
metadata=MergeBehavior.Update.meta(),
)
@dataclass
class SeedConfig(NodeConfig):
materialized: str = "seed"
delimiter: str = ","
quote_columns: Optional[bool] = None
@classmethod
@@ -553,6 +567,9 @@ class SeedConfig(NodeConfig):
raise ValidationError("A seed must have a materialized value of 'seed'")
SEVERITY_PATTERN = r"^([Ww][Aa][Rr][Nn]|[Ee][Rr][Rr][Oo][Rr])$"
@dataclass
class TestConfig(NodeAndTestConfig):
__test__ = False
@@ -563,14 +580,64 @@ class TestConfig(NodeAndTestConfig):
metadata=CompareBehavior.Exclude.meta(),
)
materialized: str = "test"
severity: Severity = Severity("ERROR")
# Annotated is used by mashumaro for jsonschema generation
severity: Annotated[Severity, Pattern(SEVERITY_PATTERN)] = Severity("ERROR")
store_failures: Optional[bool] = None
store_failures_as: Optional[str] = None
where: Optional[str] = None
limit: Optional[int] = None
fail_calc: str = "count(*)"
warn_if: str = "!= 0"
error_if: str = "!= 0"
def __post_init__(self):
"""
The presence of a setting for `store_failures_as` overrides any existing setting for `store_failures`,
regardless of level of granularity. If `store_failures_as` is not set, then `store_failures` takes effect.
At the time of implementation, `store_failures = True` would always create a table; the user could not
configure this. Hence, if `store_failures = True` and `store_failures_as` is not specified, then it
should be set to "table" to mimic the existing functionality.
A side effect of this overriding functionality is that `store_failures_as="view"` at the project
level cannot be turned off at the model level without setting both `store_failures_as` and
`store_failures`. The former would cascade down and override `store_failures=False`. The proposal
is to include "ephemeral" as a value for `store_failures_as`, which effectively sets
`store_failures=False`.
The exception handling for this is tricky. If we raise an exception here, the entire run fails at
parse time. We would rather well-formed models run successfully, leaving only exceptions to be rerun
if necessary. Hence, the exception needs to be raised in the test materialization. In order to do so,
we need to make sure that we go down the `store_failures = True` route with the invalid setting for
`store_failures_as`. This results in the `.get()` defaulted to `True` below, instead of a normal
dictionary lookup as is done in the `if` block. Refer to the test materialization for the
exception that is raise as a result of an invalid value.
The intention of this block is to behave as if `store_failures_as` is the only setting,
but still allow for backwards compatibility for `store_failures`.
See https://github.com/dbt-labs/dbt-core/issues/6914 for more information.
"""
# if `store_failures_as` is not set, it gets set by `store_failures`
# the settings below mimic existing behavior prior to `store_failures_as`
get_store_failures_as_map = {
True: "table",
False: "ephemeral",
None: None,
}
# if `store_failures_as` is set, it dictates what `store_failures` gets set to
# the settings below overrides whatever `store_failures` is set to by the user
get_store_failures_map = {
"ephemeral": False,
"table": True,
"view": True,
}
if self.store_failures_as is None:
self.store_failures_as = get_store_failures_as_map[self.store_failures]
else:
self.store_failures = get_store_failures_map.get(self.store_failures_as, True)
@classmethod
def same_contents(cls, unrendered: Dict[str, Any], other: Dict[str, Any]) -> bool:
"""This is like __eq__, except it explicitly checks certain fields."""
@@ -582,6 +649,7 @@ class TestConfig(NodeAndTestConfig):
"warn_if",
"error_if",
"store_failures",
"store_failures_as",
]
seen = set()
@@ -619,6 +687,8 @@ class SnapshotConfig(EmptySnapshotConfig):
@classmethod
def validate(cls, data):
super().validate(data)
# Note: currently you can't just set these keys in schema.yml because this validation
# will fail when parsing the snapshot node.
if not data.get("strategy") or not data.get("unique_key") or not data.get("target_schema"):
raise ValidationError(
"Snapshots must be configured with a 'strategy', 'unique_key', "
@@ -649,6 +719,7 @@ class SnapshotConfig(EmptySnapshotConfig):
if data.get("materialized") and data.get("materialized") != "snapshot":
raise ValidationError("A snapshot must have a materialized value of 'snapshot'")
# Called by "calculate_node_config_dict" in ContextConfigGenerator
def finalize_and_validate(self):
data = self.to_dict(omit_none=True)
self.validate(data)
@@ -657,6 +728,8 @@ class SnapshotConfig(EmptySnapshotConfig):
RESOURCE_TYPES: Dict[NodeType, Type[BaseConfig]] = {
NodeType.Metric: MetricConfig,
NodeType.SemanticModel: SemanticModelConfig,
NodeType.SavedQuery: SavedQueryConfig,
NodeType.Exposure: ExposureConfig,
NodeType.Source: SourceConfig,
NodeType.Seed: SeedConfig,

View File

@@ -29,3 +29,12 @@ class ModelNodeArgs:
unique_id = f"{unique_id}.v{self.version}"
return unique_id
@property
def fqn(self) -> List[str]:
fqn = [self.package_name, self.name]
# Test for None explicitly because version can be 0
if self.version is not None:
fqn.append(f"v{self.version}")
return fqn

View File

@@ -6,12 +6,13 @@ from enum import Enum
import hashlib
from mashumaro.types import SerializableType
from typing import Optional, Union, List, Dict, Any, Sequence, Tuple, Iterator
from typing import Optional, Union, List, Dict, Any, Sequence, Tuple, Iterator, Literal
from dbt.dataclass_schema import dbtClassMixin, ExtensibleDbtClassMixin
from dbt.clients.system import write_file
from dbt.contracts.files import FileHash
from dbt.contracts.graph.saved_queries import Export, QueryParams
from dbt.contracts.graph.semantic_models import (
Defaults,
Dimension,
@@ -20,6 +21,7 @@ from dbt.contracts.graph.semantic_models import (
SourceFileMetadata,
)
from dbt.contracts.graph.unparsed import (
ConstantPropertyInput,
Docs,
ExposureType,
ExternalTable,
@@ -36,6 +38,7 @@ from dbt.contracts.graph.unparsed import (
UnparsedColumn,
)
from dbt.contracts.graph.node_args import ModelNodeArgs
from dbt.contracts.graph.semantic_layer_common import WhereFilterIntersection
from dbt.contracts.util import Replaceable, AdditionalPropertiesMixin
from dbt.events.functions import warn_or_error
from dbt.exceptions import ParsingError, ContractBreakingChangeError
@@ -44,23 +47,28 @@ from dbt.events.types import (
SeedExceedsLimitSamePath,
SeedExceedsLimitAndPathChanged,
SeedExceedsLimitChecksumChanged,
UnversionedBreakingChange,
)
from dbt.events.contextvars import set_log_contextvars
from dbt.flags import get_flags
from dbt.node_types import ModelLanguage, NodeType, AccessType
from dbt_semantic_interfaces.call_parameter_sets import FilterCallParameterSets
from dbt_semantic_interfaces.references import (
EntityReference,
MeasureReference,
LinkableElementReference,
SemanticModelReference,
TimeDimensionReference,
)
from dbt_semantic_interfaces.references import MetricReference as DSIMetricReference
from dbt_semantic_interfaces.type_enums import MetricType, TimeGranularity
from dbt_semantic_interfaces.parsing.where_filter_parser import WhereFilterParser
from dbt_semantic_interfaces.type_enums import (
ConversionCalculationType,
MetricType,
TimeGranularity,
)
from .model_config import (
NodeConfig,
ModelConfig,
SeedConfig,
TestConfig,
SourceConfig,
@@ -69,6 +77,7 @@ from .model_config import (
EmptySnapshotConfig,
SnapshotConfig,
SemanticModelConfig,
SavedQueryConfig,
)
@@ -229,6 +238,7 @@ class ColumnInfo(AdditionalPropertiesMixin, ExtensibleDbtClassMixin, Replaceable
@dataclass
class Contract(dbtClassMixin, Replaceable):
enforced: bool = False
alias_types: bool = True
checksum: Optional[str] = None
@@ -554,19 +564,20 @@ class CompiledNode(ParsedNode):
@dataclass
class AnalysisNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Analysis]})
resource_type: Literal[NodeType.Analysis]
@dataclass
class HookNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Operation]})
resource_type: Literal[NodeType.Operation]
index: Optional[int] = None
@dataclass
class ModelNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Model]})
resource_type: Literal[NodeType.Model]
access: AccessType = AccessType.Protected
config: ModelConfig = field(default_factory=ModelConfig)
constraints: List[ModelLevelConstraint] = field(default_factory=list)
version: Optional[NodeVersion] = None
latest_version: Optional[NodeVersion] = None
@@ -589,7 +600,7 @@ class ModelNode(CompiledNode):
name=args.name,
package_name=args.package_name,
unique_id=unique_id,
fqn=[args.package_name, args.name],
fqn=args.fqn,
version=args.version,
latest_version=args.latest_version,
relation_name=args.relation_name,
@@ -603,7 +614,7 @@ class ModelNode(CompiledNode):
path="",
unrendered_config=unrendered_config,
depends_on=DependsOn(nodes=args.depends_on_nodes),
config=NodeConfig(enabled=args.enabled),
config=ModelConfig(enabled=args.enabled),
)
@property
@@ -625,6 +636,18 @@ class ModelNode(CompiledNode):
def materialization_enforces_constraints(self) -> bool:
return self.config.materialized in ["table", "incremental"]
def same_contents(self, old, adapter_type) -> bool:
return super().same_contents(old, adapter_type) and self.same_ref_representation(old)
def same_ref_representation(self, old) -> bool:
return (
# Changing the latest_version may break downstream unpinned refs
self.latest_version == old.latest_version
# Changes to access or deprecation_date may lead to ref-related parsing errors
and self.access == old.access
and self.deprecation_date == old.deprecation_date
)
def build_contract_checksum(self):
# We don't need to construct the checksum if the model does not
# have contract enforced, because it won't be used.
@@ -669,11 +692,11 @@ class ModelNode(CompiledNode):
# These are the categories of breaking changes:
contract_enforced_disabled: bool = False
columns_removed: List[str] = []
column_type_changes: List[Tuple[str, str, str]] = []
enforced_column_constraint_removed: List[Tuple[str, str]] = [] # column, constraint_type
enforced_model_constraint_removed: List[
Tuple[str, List[str]]
] = [] # constraint_type, columns
column_type_changes: List[Dict[str, str]] = []
enforced_column_constraint_removed: List[
Dict[str, str]
] = [] # column_name, constraint_type
enforced_model_constraint_removed: List[Dict[str, Any]] = [] # constraint_type, columns
materialization_changed: List[str] = []
if old.contract.enforced is True and self.contract.enforced is False:
@@ -695,11 +718,11 @@ class ModelNode(CompiledNode):
# Has this column's data type changed?
elif old_value.data_type != self.columns[old_key].data_type:
column_type_changes.append(
(
str(old_value.name),
str(old_value.data_type),
str(self.columns[old_key].data_type),
)
{
"column_name": str(old_value.name),
"previous_column_type": str(old_value.data_type),
"current_column_type": str(self.columns[old_key].data_type),
}
)
# track if there are any column level constraints for the materialization check late
@@ -720,7 +743,11 @@ class ModelNode(CompiledNode):
and constraint_support[old_constraint.type] == ConstraintSupport.ENFORCED
):
enforced_column_constraint_removed.append(
(old_key, str(old_constraint.type))
{
"column_name": old_key,
"constraint_name": old_constraint.name,
"constraint_type": ConstraintType(old_constraint.type),
}
)
# Now compare the model level constraints
@@ -731,7 +758,11 @@ class ModelNode(CompiledNode):
and constraint_support[old_constraint.type] == ConstraintSupport.ENFORCED
):
enforced_model_constraint_removed.append(
(str(old_constraint.type), old_constraint.columns)
{
"constraint_name": old_constraint.name,
"constraint_type": ConstraintType(old_constraint.type),
"columns": old_constraint.columns,
}
)
# Check for relevant materialization changes.
@@ -745,7 +776,8 @@ class ModelNode(CompiledNode):
# If a column has been added, it will be missing in the old.columns, and present in self.columns
# That's a change (caught by the different checksums), but not a breaking change
# Did we find any changes that we consider breaking? If so, that's an error
# Did we find any changes that we consider breaking? If there's an enforced contract, that's
# a warning unless the model is versioned, then it's an error.
if (
contract_enforced_disabled
or columns_removed
@@ -754,32 +786,89 @@ class ModelNode(CompiledNode):
or enforced_column_constraint_removed
or materialization_changed
):
raise (
ContractBreakingChangeError(
contract_enforced_disabled=contract_enforced_disabled,
columns_removed=columns_removed,
column_type_changes=column_type_changes,
enforced_column_constraint_removed=enforced_column_constraint_removed,
enforced_model_constraint_removed=enforced_model_constraint_removed,
materialization_changed=materialization_changed,
breaking_changes = []
if contract_enforced_disabled:
breaking_changes.append(
"Contract enforcement was removed: Previously, this model had an enforced contract. It is no longer configured to enforce its contract, and this is a breaking change."
)
if columns_removed:
columns_removed_str = "\n - ".join(columns_removed)
breaking_changes.append(f"Columns were removed: \n - {columns_removed_str}")
if column_type_changes:
column_type_changes_str = "\n - ".join(
[
f"{c['column_name']} ({c['previous_column_type']} -> {c['current_column_type']})"
for c in column_type_changes
]
)
breaking_changes.append(
f"Columns with data_type changes: \n - {column_type_changes_str}"
)
if enforced_column_constraint_removed:
column_constraint_changes_str = "\n - ".join(
[
f"'{c['constraint_name'] if c['constraint_name'] is not None else c['constraint_type']}' constraint on column {c['column_name']}"
for c in enforced_column_constraint_removed
]
)
breaking_changes.append(
f"Enforced column level constraints were removed: \n - {column_constraint_changes_str}"
)
if enforced_model_constraint_removed:
model_constraint_changes_str = "\n - ".join(
[
f"'{c['constraint_name'] if c['constraint_name'] is not None else c['constraint_type']}' constraint on columns {c['columns']}"
for c in enforced_model_constraint_removed
]
)
breaking_changes.append(
f"Enforced model level constraints were removed: \n - {model_constraint_changes_str}"
)
if materialization_changed:
materialization_changes_str = (
f"{materialization_changed[0]} -> {materialization_changed[1]}"
)
breaking_changes.append(
f"Materialization changed with enforced constraints: \n - {materialization_changes_str}"
)
if self.version is None:
warn_or_error(
UnversionedBreakingChange(
contract_enforced_disabled=contract_enforced_disabled,
columns_removed=columns_removed,
column_type_changes=column_type_changes,
enforced_column_constraint_removed=enforced_column_constraint_removed,
enforced_model_constraint_removed=enforced_model_constraint_removed,
breaking_changes=breaking_changes,
model_name=self.name,
model_file_path=self.original_file_path,
),
node=self,
)
)
else:
raise (
ContractBreakingChangeError(
breaking_changes=breaking_changes,
node=self,
)
)
# Otherwise, though we didn't find any *breaking* changes, the contract has still changed -- same_contract: False
else:
return False
# Otherwise, the contract has changed -- same_contract: False
return False
# TODO: rm?
@dataclass
class RPCNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.RPCCall]})
resource_type: Literal[NodeType.RPCCall]
@dataclass
class SqlNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.SqlOperation]})
resource_type: Literal[NodeType.SqlOperation]
# ====================================
@@ -789,7 +878,7 @@ class SqlNode(CompiledNode):
@dataclass
class SeedNode(ParsedNode): # No SQLDefaults!
resource_type: NodeType = field(metadata={"restrict": [NodeType.Seed]})
resource_type: Literal[NodeType.Seed]
config: SeedConfig = field(default_factory=SeedConfig)
# seeds need the root_path because the contents are not loaded initially
# and we need the root_path to load the seed later
@@ -915,7 +1004,7 @@ class TestShouldStoreFailures:
@dataclass
class SingularTestNode(TestShouldStoreFailures, CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Test]})
resource_type: Literal[NodeType.Test]
# Was not able to make mypy happy and keep the code working. We need to
# refactor the various configs.
config: TestConfig = field(default_factory=TestConfig) # type: ignore
@@ -951,7 +1040,7 @@ class HasTestMetadata(dbtClassMixin):
@dataclass
class GenericTestNode(TestShouldStoreFailures, CompiledNode, HasTestMetadata):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Test]})
resource_type: Literal[NodeType.Test]
column_name: Optional[str] = None
file_key_name: Optional[str] = None
# Was not able to make mypy happy and keep the code working. We need to
@@ -984,13 +1073,13 @@ class IntermediateSnapshotNode(CompiledNode):
# uses a regular node config, which the snapshot parser will then convert
# into a full ParsedSnapshotNode after rendering. Note: it currently does
# not work to set snapshot config in schema files because of the validation.
resource_type: NodeType = field(metadata={"restrict": [NodeType.Snapshot]})
resource_type: Literal[NodeType.Snapshot]
config: EmptySnapshotConfig = field(default_factory=EmptySnapshotConfig)
@dataclass
class SnapshotNode(CompiledNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Snapshot]})
resource_type: Literal[NodeType.Snapshot]
config: SnapshotConfig
defer_relation: Optional[DeferRelation] = None
@@ -1003,7 +1092,7 @@ class SnapshotNode(CompiledNode):
@dataclass
class Macro(BaseNode):
macro_sql: str
resource_type: NodeType = field(metadata={"restrict": [NodeType.Macro]})
resource_type: Literal[NodeType.Macro]
depends_on: MacroDependsOn = field(default_factory=MacroDependsOn)
description: str = ""
meta: Dict[str, Any] = field(default_factory=dict)
@@ -1033,7 +1122,7 @@ class Macro(BaseNode):
@dataclass
class Documentation(BaseNode):
block_contents: str
resource_type: NodeType = field(metadata={"restrict": [NodeType.Documentation]})
resource_type: Literal[NodeType.Documentation]
@property
def search_name(self):
@@ -1064,7 +1153,7 @@ class UnpatchedSourceDefinition(BaseNode):
source: UnparsedSourceDefinition
table: UnparsedSourceTableDefinition
fqn: List[str]
resource_type: NodeType = field(metadata={"restrict": [NodeType.Source]})
resource_type: Literal[NodeType.Source]
patch_path: Optional[str] = None
def get_full_source_name(self):
@@ -1109,7 +1198,7 @@ class ParsedSourceMandatory(GraphNode, HasRelationMetadata):
source_description: str
loader: str
identifier: str
resource_type: NodeType = field(metadata={"restrict": [NodeType.Source]})
resource_type: Literal[NodeType.Source]
@dataclass
@@ -1219,8 +1308,8 @@ class SourceDefinition(NodeInfoMixin, ParsedSourceMandatory):
return []
@property
def has_freshness(self):
return bool(self.freshness) and self.loaded_at_field is not None
def has_freshness(self) -> bool:
return bool(self.freshness)
@property
def search_name(self):
@@ -1236,7 +1325,7 @@ class SourceDefinition(NodeInfoMixin, ParsedSourceMandatory):
class Exposure(GraphNode):
type: ExposureType
owner: Owner
resource_type: NodeType = field(metadata={"restrict": [NodeType.Exposure]})
resource_type: Literal[NodeType.Exposure]
description: str = ""
label: Optional[str] = None
maturity: Optional[MaturityType] = None
@@ -1315,20 +1404,13 @@ class Exposure(GraphNode):
# ====================================
@dataclass
class WhereFilter(dbtClassMixin):
where_sql_template: str
@property
def call_parameter_sets(self) -> FilterCallParameterSets:
return WhereFilterParser.parse_call_parameter_sets(self.where_sql_template)
@dataclass
class MetricInputMeasure(dbtClassMixin):
name: str
filter: Optional[WhereFilter] = None
filter: Optional[WhereFilterIntersection] = None
alias: Optional[str] = None
join_to_timespine: bool = False
fill_nulls_with: Optional[int] = None
def measure_reference(self) -> MeasureReference:
return MeasureReference(element_name=self.name)
@@ -1346,7 +1428,7 @@ class MetricTimeWindow(dbtClassMixin):
@dataclass
class MetricInput(dbtClassMixin):
name: str
filter: Optional[WhereFilter] = None
filter: Optional[WhereFilterIntersection] = None
alias: Optional[str] = None
offset_window: Optional[MetricTimeWindow] = None
offset_to_grain: Optional[TimeGranularity] = None
@@ -1358,6 +1440,16 @@ class MetricInput(dbtClassMixin):
return DSIMetricReference(element_name=self.alias or self.name)
@dataclass
class ConversionTypeParams(dbtClassMixin):
base_measure: MetricInputMeasure
conversion_measure: MetricInputMeasure
entity: str
calculation: ConversionCalculationType = ConversionCalculationType.CONVERSION_RATE
window: Optional[MetricTimeWindow] = None
constant_properties: Optional[List[ConstantPropertyInput]] = None
@dataclass
class MetricTypeParams(dbtClassMixin):
measure: Optional[MetricInputMeasure] = None
@@ -1368,6 +1460,7 @@ class MetricTypeParams(dbtClassMixin):
window: Optional[MetricTimeWindow] = None
grain_to_date: Optional[TimeGranularity] = None
metrics: Optional[List[MetricInput]] = None
conversion_type_params: Optional[ConversionTypeParams] = None
@dataclass
@@ -1383,9 +1476,9 @@ class Metric(GraphNode):
label: str
type: MetricType
type_params: MetricTypeParams
filter: Optional[WhereFilter] = None
filter: Optional[WhereFilterIntersection] = None
metadata: Optional[SourceFileMetadata] = None
resource_type: NodeType = field(metadata={"restrict": [NodeType.Metric]})
resource_type: Literal[NodeType.Metric]
meta: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list)
config: MetricConfig = field(default_factory=MetricConfig)
@@ -1458,6 +1551,12 @@ class Metric(GraphNode):
and True
)
def add_input_measure(self, input_measure: MetricInputMeasure) -> None:
for existing_input_measure in self.type_params.input_measures:
if input_measure == existing_input_measure:
return
self.type_params.input_measures.append(input_measure)
# ====================================
# Group node
@@ -1468,7 +1567,7 @@ class Metric(GraphNode):
class Group(BaseNode):
name: str
owner: Owner
resource_type: NodeType = field(metadata={"restrict": [NodeType.Group]})
resource_type: Literal[NodeType.Group]
# ====================================
@@ -1489,6 +1588,7 @@ class SemanticModel(GraphNode):
model: str
node_relation: Optional[NodeRelation]
description: Optional[str] = None
label: Optional[str] = None
defaults: Optional[Defaults] = None
entities: Sequence[Entity] = field(default_factory=list)
measures: Sequence[Measure] = field(default_factory=list)
@@ -1498,6 +1598,9 @@ class SemanticModel(GraphNode):
refs: List[RefArgs] = field(default_factory=list)
created_at: float = field(default_factory=lambda: time.time())
config: SemanticModelConfig = field(default_factory=SemanticModelConfig)
unrendered_config: Dict[str, Any] = field(default_factory=dict)
primary_entity: Optional[str] = None
group: Optional[str] = None
@property
def entity_references(self) -> List[LinkableElementReference]:
@@ -1568,17 +1671,157 @@ class SemanticModel(GraphNode):
measure is not None
), f"No measure with name ({measure_reference.element_name}) in semantic_model with name ({self.name})"
if self.defaults is not None:
default_agg_time_dimesion = self.defaults.agg_time_dimension
default_agg_time_dimension = (
self.defaults.agg_time_dimension if self.defaults is not None else None
)
agg_time_dimension_name = measure.agg_time_dimension or default_agg_time_dimesion
agg_time_dimension_name = measure.agg_time_dimension or default_agg_time_dimension
assert agg_time_dimension_name is not None, (
f"Aggregation time dimension for measure {measure.name} is not set! This should either be set directly on "
f"the measure specification in the model, or else defaulted to the primary time dimension in the data "
f"source containing the measure."
f"Aggregation time dimension for measure {measure.name} on semantic model {self.name} is not set! "
"To fix this either specify a default `agg_time_dimension` for the semantic model or define an "
"`agg_time_dimension` on the measure directly."
)
return TimeDimensionReference(element_name=agg_time_dimension_name)
@property
def primary_entity_reference(self) -> Optional[EntityReference]:
return (
EntityReference(element_name=self.primary_entity)
if self.primary_entity is not None
else None
)
def same_model(self, old: "SemanticModel") -> bool:
return self.model == old.same_model
def same_node_relation(self, old: "SemanticModel") -> bool:
return self.node_relation == old.node_relation
def same_description(self, old: "SemanticModel") -> bool:
return self.description == old.description
def same_defaults(self, old: "SemanticModel") -> bool:
return self.defaults == old.defaults
def same_entities(self, old: "SemanticModel") -> bool:
return self.entities == old.entities
def same_dimensions(self, old: "SemanticModel") -> bool:
return self.dimensions == old.dimensions
def same_measures(self, old: "SemanticModel") -> bool:
return self.measures == old.measures
def same_config(self, old: "SemanticModel") -> bool:
return self.config == old.config
def same_primary_entity(self, old: "SemanticModel") -> bool:
return self.primary_entity == old.primary_entity
def same_group(self, old: "SemanticModel") -> bool:
return self.group == old.group
def same_contents(self, old: Optional["SemanticModel"]) -> bool:
# existing when it didn't before is a change!
# metadata/tags changes are not "changes"
if old is None:
return True
return (
self.same_model(old)
and self.same_node_relation(old)
and self.same_description(old)
and self.same_defaults(old)
and self.same_entities(old)
and self.same_dimensions(old)
and self.same_measures(old)
and self.same_config(old)
and self.same_primary_entity(old)
and self.same_group(old)
and True
)
# ====================================
# SavedQuery and related classes
# ====================================
@dataclass
class SavedQueryMandatory(GraphNode):
query_params: QueryParams
exports: List[Export]
@dataclass
class SavedQuery(NodeInfoMixin, SavedQueryMandatory):
description: Optional[str] = None
label: Optional[str] = None
metadata: Optional[SourceFileMetadata] = None
config: SavedQueryConfig = field(default_factory=SavedQueryConfig)
unrendered_config: Dict[str, Any] = field(default_factory=dict)
group: Optional[str] = None
depends_on: DependsOn = field(default_factory=DependsOn)
created_at: float = field(default_factory=lambda: time.time())
refs: List[RefArgs] = field(default_factory=list)
@property
def metrics(self) -> List[str]:
return self.query_params.metrics
@property
def depends_on_nodes(self):
return self.depends_on.nodes
def same_metrics(self, old: "SavedQuery") -> bool:
return self.query_params.metrics == old.query_params.metrics
def same_group_by(self, old: "SavedQuery") -> bool:
return self.query_params.group_by == old.query_params.group_by
def same_description(self, old: "SavedQuery") -> bool:
return self.description == old.description
def same_where(self, old: "SavedQuery") -> bool:
return self.query_params.where == old.query_params.where
def same_label(self, old: "SavedQuery") -> bool:
return self.label == old.label
def same_config(self, old: "SavedQuery") -> bool:
return self.config == old.config
def same_group(self, old: "SavedQuery") -> bool:
return self.group == old.group
def same_exports(self, old: "SavedQuery") -> bool:
if len(self.exports) != len(old.exports):
return False
# exports should be in the same order, so we zip them for easy iteration
for (old_export, new_export) in zip(old.exports, self.exports):
if not new_export.same_contents(old_export):
return False
return True
def same_contents(self, old: Optional["SavedQuery"]) -> bool:
# existing when it didn't before is a change!
# metadata/tags changes are not "changes"
if old is None:
return True
return (
self.same_metrics(old)
and self.same_group_by(old)
and self.same_description(old)
and self.same_where(old)
and self.same_label(old)
and self.same_config(old)
and self.same_group(old)
and True
)
# ====================================
# Patches
@@ -1646,6 +1889,7 @@ GraphMemberNode = Union[
ResultNode,
Exposure,
Metric,
SavedQuery,
SemanticModel,
]

View File

@@ -0,0 +1,53 @@
from __future__ import annotations
from dataclasses import dataclass
from dbt.contracts.graph.semantic_layer_common import WhereFilterIntersection
from dbt.dataclass_schema import dbtClassMixin
from dbt_semantic_interfaces.type_enums.export_destination_type import ExportDestinationType
from typing import List, Optional
@dataclass
class ExportConfig(dbtClassMixin):
"""Nested configuration attributes for exports."""
export_as: ExportDestinationType
schema_name: Optional[str] = None
alias: Optional[str] = None
@dataclass
class Export(dbtClassMixin):
"""Configuration for writing query results to a table."""
name: str
config: ExportConfig
def same_name(self, old: Export) -> bool:
return self.name == old.name
def same_export_as(self, old: Export) -> bool:
return self.config.export_as == old.config.export_as
def same_schema_name(self, old: Export) -> bool:
return self.config.schema_name == old.config.schema_name
def same_alias(self, old: Export) -> bool:
return self.config.alias == old.config.alias
def same_contents(self, old: Export) -> bool:
return (
self.same_name(old)
and self.same_export_as(old)
and self.same_schema_name(old)
and self.same_alias(old)
)
@dataclass
class QueryParams(dbtClassMixin):
"""The query parameters for the saved query"""
metrics: List[str]
group_by: List[str]
where: Optional[WhereFilterIntersection]

View File

@@ -0,0 +1,23 @@
from dataclasses import dataclass
from dbt.dataclass_schema import dbtClassMixin
from dbt_semantic_interfaces.call_parameter_sets import FilterCallParameterSets
from dbt_semantic_interfaces.parsing.where_filter.where_filter_parser import WhereFilterParser
from typing import List, Sequence, Tuple
@dataclass
class WhereFilter(dbtClassMixin):
where_sql_template: str
@property
def call_parameter_sets(self) -> FilterCallParameterSets:
return WhereFilterParser.parse_call_parameter_sets(self.where_sql_template)
@dataclass
class WhereFilterIntersection(dbtClassMixin):
where_filters: List[WhereFilter]
@property
def filter_expression_parameter_sets(self) -> Sequence[Tuple[str, FilterCallParameterSets]]:
raise NotImplementedError

View File

@@ -2,6 +2,7 @@ from dbt_semantic_interfaces.implementations.metric import PydanticMetric
from dbt_semantic_interfaces.implementations.project_configuration import (
PydanticProjectConfiguration,
)
from dbt_semantic_interfaces.implementations.saved_query import PydanticSavedQuery
from dbt_semantic_interfaces.implementations.semantic_manifest import PydanticSemanticManifest
from dbt_semantic_interfaces.implementations.semantic_model import PydanticSemanticModel
from dbt_semantic_interfaces.implementations.time_spine_table_configuration import (
@@ -20,7 +21,7 @@ from dbt.exceptions import ParsingError
class SemanticManifest:
def __init__(self, manifest):
def __init__(self, manifest) -> None:
self.manifest = manifest
def validate(self) -> bool:
@@ -71,6 +72,11 @@ class SemanticManifest:
for metric in self.manifest.metrics.values():
pydantic_semantic_manifest.metrics.append(PydanticMetric.parse_obj(metric.to_dict()))
for saved_query in self.manifest.saved_queries.values():
pydantic_semantic_manifest.saved_queries.append(
PydanticSavedQuery.parse_obj(saved_query.to_dict())
)
# Look for time-spine table model and create time spine table configuration
if self.manifest.semantic_models:
# Get model for time_spine_table

View File

@@ -66,6 +66,7 @@ class Dimension(dbtClassMixin):
name: str
type: DimensionType
description: Optional[str] = None
label: Optional[str] = None
is_partition: bool = False
type_params: Optional[DimensionTypeParams] = None
expr: Optional[str] = None
@@ -100,6 +101,7 @@ class Entity(dbtClassMixin):
name: str
type: EntityType
description: Optional[str] = None
label: Optional[str] = None
role: Optional[str] = None
expr: Optional[str] = None
@@ -136,6 +138,7 @@ class Measure(dbtClassMixin):
name: str
agg: AggregationType
description: Optional[str] = None
label: Optional[str] = None
create_metric: bool = False
expr: Optional[str] = None
agg_params: Optional[MeasureAggregationParameters] = None

View File

@@ -19,11 +19,12 @@ import dbt.helper_types # noqa:F401
from dbt.exceptions import CompilationError, ParsingError, DbtInternalError
from dbt.dataclass_schema import dbtClassMixin, StrEnum, ExtensibleDbtClassMixin, ValidationError
from dbt_semantic_interfaces.type_enums import ConversionCalculationType
from dataclasses import dataclass, field
from datetime import timedelta
from pathlib import Path
from typing import Optional, List, Union, Dict, Any, Sequence
from typing import Optional, List, Union, Dict, Any, Sequence, Literal
@dataclass
@@ -49,31 +50,18 @@ class HasCode(dbtClassMixin):
@dataclass
class UnparsedMacro(UnparsedBaseNode, HasCode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Macro]})
resource_type: Literal[NodeType.Macro]
@dataclass
class UnparsedGenericTest(UnparsedBaseNode, HasCode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Macro]})
resource_type: Literal[NodeType.Macro]
@dataclass
class UnparsedNode(UnparsedBaseNode, HasCode):
name: str
resource_type: NodeType = field(
metadata={
"restrict": [
NodeType.Model,
NodeType.Analysis,
NodeType.Test,
NodeType.Snapshot,
NodeType.Operation,
NodeType.Seed,
NodeType.RPCCall,
NodeType.SqlOperation,
]
}
)
resource_type: NodeType
@property
def search_name(self):
@@ -82,7 +70,7 @@ class UnparsedNode(UnparsedBaseNode, HasCode):
@dataclass
class UnparsedRunHook(UnparsedNode):
resource_type: NodeType = field(metadata={"restrict": [NodeType.Operation]})
resource_type: Literal[NodeType.Operation]
index: Optional[int] = None
@@ -163,14 +151,9 @@ class UnparsedVersion(dbtClassMixin):
def __lt__(self, other):
try:
v = type(other.v)(self.v)
return v < other.v
return float(self.v) < float(other.v)
except ValueError:
try:
other_v = type(self.v)(other.v)
return self.v < other_v
except ValueError:
return str(self.v) < str(other.v)
return str(self.v) < str(other.v)
@property
def include_exclude(self) -> dbt.helper_types.IncludeExclude:
@@ -220,7 +203,7 @@ class UnparsedModelUpdate(UnparsedNodeUpdate):
versions: Sequence[UnparsedVersion] = field(default_factory=list)
deprecation_date: Optional[datetime.datetime] = None
def __post_init__(self):
def __post_init__(self) -> None:
if self.latest_version:
version_values = [version.v for version in self.versions]
if self.latest_version not in version_values:
@@ -228,7 +211,7 @@ class UnparsedModelUpdate(UnparsedNodeUpdate):
f"latest_version: {self.latest_version} is not one of model '{self.name}' versions: {version_values} "
)
seen_versions: set[str] = set()
seen_versions = set()
for version in self.versions:
if str(version.v) in seen_versions:
raise ParsingError(
@@ -600,19 +583,39 @@ class MetricTime(dbtClassMixin, Mergeable):
@dataclass
class UnparsedMetricInputMeasure(dbtClassMixin):
name: str
filter: Optional[str] = None
filter: Optional[Union[str, List[str]]] = None
alias: Optional[str] = None
join_to_timespine: bool = False
fill_nulls_with: Optional[int] = None
@dataclass
class UnparsedMetricInput(dbtClassMixin):
name: str
filter: Optional[str] = None
filter: Optional[Union[str, List[str]]] = None
alias: Optional[str] = None
offset_window: Optional[str] = None
offset_to_grain: Optional[str] = None # str is really a TimeGranularity Enum
@dataclass
class ConstantPropertyInput(dbtClassMixin):
base_property: str
conversion_property: str
@dataclass
class UnparsedConversionTypeParams(dbtClassMixin):
base_measure: Union[UnparsedMetricInputMeasure, str]
conversion_measure: Union[UnparsedMetricInputMeasure, str]
entity: str
calculation: str = (
ConversionCalculationType.CONVERSION_RATE.value
) # ConversionCalculationType Enum
window: Optional[str] = None
constant_properties: Optional[List[ConstantPropertyInput]] = None
@dataclass
class UnparsedMetricTypeParams(dbtClassMixin):
measure: Optional[Union[UnparsedMetricInputMeasure, str]] = None
@@ -622,6 +625,7 @@ class UnparsedMetricTypeParams(dbtClassMixin):
window: Optional[str] = None
grain_to_date: Optional[str] = None # str is really a TimeGranularity Enum
metrics: Optional[List[Union[UnparsedMetricInput, str]]] = None
conversion_type_params: Optional[UnparsedConversionTypeParams] = None
@dataclass
@@ -631,7 +635,7 @@ class UnparsedMetric(dbtClassMixin):
type: str
type_params: UnparsedMetricTypeParams
description: str = ""
filter: Optional[str] = None
filter: Optional[Union[str, List[str]]] = None
# metadata: Optional[Unparsedetadata] = None # TODO
meta: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list)
@@ -681,6 +685,7 @@ class UnparsedEntity(dbtClassMixin):
name: str
type: str # EntityType enum
description: Optional[str] = None
label: Optional[str] = None
role: Optional[str] = None
expr: Optional[str] = None
@@ -689,7 +694,7 @@ class UnparsedEntity(dbtClassMixin):
class UnparsedNonAdditiveDimension(dbtClassMixin):
name: str
window_choice: str # AggregationType enum
window_groupings: List[str]
window_groupings: List[str] = field(default_factory=list)
@dataclass
@@ -697,10 +702,12 @@ class UnparsedMeasure(dbtClassMixin):
name: str
agg: str # actually an enum
description: Optional[str] = None
label: Optional[str] = None
expr: Optional[Union[str, bool, int]] = None
agg_params: Optional[MeasureAggregationParameters] = None
non_additive_dimension: Optional[UnparsedNonAdditiveDimension] = None
agg_time_dimension: Optional[str] = None
create_metric: bool = False
@dataclass
@@ -714,6 +721,7 @@ class UnparsedDimension(dbtClassMixin):
name: str
type: str # actually an enum
description: Optional[str] = None
label: Optional[str] = None
is_partition: bool = False
type_params: Optional[UnparsedDimensionTypeParams] = None
expr: Optional[str] = None
@@ -723,11 +731,39 @@ class UnparsedDimension(dbtClassMixin):
class UnparsedSemanticModel(dbtClassMixin):
name: str
model: str # looks like "ref(...)"
config: Dict[str, Any] = field(default_factory=dict)
description: Optional[str] = None
label: Optional[str] = None
defaults: Optional[Defaults] = None
entities: List[UnparsedEntity] = field(default_factory=list)
measures: List[UnparsedMeasure] = field(default_factory=list)
dimensions: List[UnparsedDimension] = field(default_factory=list)
primary_entity: Optional[str] = None
@dataclass
class UnparsedQueryParams(dbtClassMixin):
metrics: List[str] = field(default_factory=list)
group_by: List[str] = field(default_factory=list)
where: Optional[Union[str, List[str]]] = None
@dataclass
class UnparsedExport(dbtClassMixin):
"""Configuration for writing query results to a table."""
name: str
config: Dict[str, Any] = field(default_factory=dict)
@dataclass
class UnparsedSavedQuery(dbtClassMixin):
name: str
query_params: UnparsedQueryParams
description: Optional[str] = None
label: Optional[str] = None
exports: List[UnparsedExport] = field(default_factory=list)
config: Dict[str, Any] = field(default_factory=dict)
def normalize_date(d: Optional[datetime.date]) -> Optional[datetime.datetime]:

View File

@@ -1,16 +1,17 @@
from dbt.contracts.util import Replaceable, Mergeable, list_str, Identifier
from dbt.contracts.connection import QueryComment, UserConfigContract
from dbt.contracts.connection import QueryComment
from dbt.helper_types import NoValue
from dbt.dataclass_schema import (
dbtClassMixin,
ValidationError,
HyphenatedDbtClassMixin,
ExtensibleDbtClassMixin,
register_pattern,
dbtMashConfig,
)
from dataclasses import dataclass, field
from typing import Optional, List, Dict, Union, Any
from typing import Optional, List, Dict, Union, Any, ClassVar
from typing_extensions import Annotated
from mashumaro.types import SerializableType
from mashumaro.jsonschema.annotations import Pattern
DEFAULT_SEND_ANONYMOUS_USAGE_STATS = True
@@ -25,12 +26,8 @@ class SemverString(str, SerializableType):
return SemverString(value)
# this supports full semver,
# but also allows for 2 group version numbers, (allows '1.0').
register_pattern(
SemverString,
r"^(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)?$", # noqa
)
# This supports full semver, but also allows for 2 group version numbers, (allows '1.0').
sem_ver_pattern = r"^(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)?$"
@dataclass
@@ -42,13 +39,14 @@ class Quoting(dbtClassMixin, Mergeable):
@dataclass
class Package(Replaceable, HyphenatedDbtClassMixin):
class Package(dbtClassMixin, Replaceable):
pass
@dataclass
class LocalPackage(Package):
local: str
unrendered: Dict[str, Any] = field(default_factory=dict)
# `float` also allows `int`, according to PEP484 (and jsonschema!)
@@ -59,14 +57,16 @@ RawVersion = Union[str, float]
class TarballPackage(Package):
tarball: str
name: str
unrendered: Dict[str, Any] = field(default_factory=dict)
@dataclass
class GitPackage(Package):
git: str
revision: Optional[RawVersion] = None
warn_unpinned: Optional[bool] = None
warn_unpinned: Optional[bool] = field(default=None, metadata={"alias": "warn-unpinned"})
subdirectory: Optional[str] = None
unrendered: Dict[str, Any] = field(default_factory=dict)
def get_revisions(self) -> List[str]:
if self.revision is None:
@@ -80,6 +80,7 @@ class RegistryPackage(Package):
package: str
version: Union[RawVersion, List[RawVersion]]
install_prerelease: Optional[bool] = False
unrendered: Dict[str, Any] = field(default_factory=dict)
def get_versions(self) -> List[str]:
if isinstance(self.version, list):
@@ -182,10 +183,13 @@ BANNED_PROJECT_NAMES = {
@dataclass
class Project(HyphenatedDbtClassMixin, Replaceable):
name: Identifier
class Project(dbtClassMixin, Replaceable):
_hyphenated: ClassVar[bool] = True
# Annotated is used by mashumaro for jsonschema generation
name: Annotated[Identifier, Pattern(r"^[^\d\W]\w*$")]
config_version: Optional[int] = 2
version: Optional[Union[SemverString, float]] = None
# Annotated is used by mashumaro for jsonschema generation
version: Optional[Union[Annotated[SemverString, Pattern(sem_ver_pattern)], float]] = None
project_root: Optional[str] = None
source_paths: Optional[List[str]] = None
model_paths: Optional[List[str]] = None
@@ -214,6 +218,8 @@ class Project(HyphenatedDbtClassMixin, Replaceable):
sources: Dict[str, Any] = field(default_factory=dict)
tests: Dict[str, Any] = field(default_factory=dict)
metrics: Dict[str, Any] = field(default_factory=dict)
semantic_models: Dict[str, Any] = field(default_factory=dict)
saved_queries: Dict[str, Any] = field(default_factory=dict)
exposures: Dict[str, Any] = field(default_factory=dict)
vars: Optional[Dict[str, Any]] = field(
default=None,
@@ -224,6 +230,36 @@ class Project(HyphenatedDbtClassMixin, Replaceable):
packages: List[PackageSpec] = field(default_factory=list)
query_comment: Optional[Union[QueryComment, NoValue, str]] = field(default_factory=NoValue)
restrict_access: bool = False
dbt_cloud: Optional[Dict[str, Any]] = None
class Config(dbtMashConfig):
# These tell mashumaro to use aliases for jsonschema and for "from_dict"
aliases = {
"config_version": "config-version",
"project_root": "project-root",
"source_paths": "source-paths",
"model_paths": "model-paths",
"macro_paths": "macro-paths",
"data_paths": "data-paths",
"seed_paths": "seed-paths",
"test_paths": "test-paths",
"analysis_paths": "analysis-paths",
"docs_paths": "docs-paths",
"asset_paths": "asset-paths",
"target_path": "target-path",
"snapshot_paths": "snapshot-paths",
"clean_targets": "clean-targets",
"log_path": "log-path",
"packages_install_path": "packages-install-path",
"on_run_start": "on-run-start",
"on_run_end": "on-run-end",
"require_dbt_version": "require-dbt-version",
"query_comment": "query-comment",
"restrict_access": "restrict-access",
"semantic_models": "semantic-models",
"saved_queries": "saved-queries",
"dbt_cloud": "dbt-cloud",
}
@classmethod
def validate(cls, data):
@@ -240,10 +276,14 @@ class Project(HyphenatedDbtClassMixin, Replaceable):
or not isinstance(entry["search_order"], list)
):
raise ValidationError(f"Invalid project dispatch config: {entry}")
if "dbt_cloud" in data and not isinstance(data["dbt_cloud"], dict):
raise ValidationError(
f"Invalid dbt_cloud config. Expected a 'dict' but got '{type(data['dbt_cloud'])}'"
)
@dataclass
class UserConfig(ExtensibleDbtClassMixin, Replaceable, UserConfigContract):
class ProjectFlags(ExtensibleDbtClassMixin, Replaceable):
cache_selected_only: Optional[bool] = None
debug: Optional[bool] = None
fail_fast: Optional[bool] = None
@@ -255,6 +295,7 @@ class UserConfig(ExtensibleDbtClassMixin, Replaceable, UserConfigContract):
partial_parse: Optional[bool] = None
populate_cache: Optional[bool] = None
printer_width: Optional[int] = None
require_explicit_package_overrides_for_builtin_materializations: bool = False
send_anonymous_usage_stats: bool = DEFAULT_SEND_ANONYMOUS_USAGE_STATS
static_parser: Optional[bool] = None
use_colors: Optional[bool] = None
@@ -265,12 +306,17 @@ class UserConfig(ExtensibleDbtClassMixin, Replaceable, UserConfigContract):
warn_error_options: Optional[Dict[str, Union[str, List[str]]]] = None
write_json: Optional[bool] = None
@property
def project_only_flags(self) -> Dict[str, Any]:
return {
"require_explicit_package_overrides_for_builtin_materializations": self.require_explicit_package_overrides_for_builtin_materializations,
}
@dataclass
class ProfileConfig(HyphenatedDbtClassMixin, Replaceable):
profile_name: str = field(metadata={"preserve_underscore": True})
target_name: str = field(metadata={"preserve_underscore": True})
user_config: UserConfig = field(metadata={"preserve_underscore": True})
class ProfileConfig(dbtClassMixin, Replaceable):
profile_name: str
target_name: str
threads: int
# TODO: make this a dynamic union of some kind?
credentials: Optional[Dict[str, Any]]

View File

@@ -19,6 +19,7 @@ class RelationType(StrEnum):
CTE = "cte"
MaterializedView = "materialized_view"
External = "external"
Ephemeral = "ephemeral"
class ComponentName(StrEnum):

View File

@@ -1,13 +1,14 @@
import threading
from dbt.contracts.graph.unparsed import FreshnessThreshold
from dbt.contracts.graph.nodes import SourceDefinition, ResultNode
from dbt.contracts.graph.nodes import CompiledNode, SourceDefinition, ResultNode
from dbt.contracts.util import (
BaseArtifactMetadata,
ArtifactMixin,
VersionedSchema,
Replaceable,
schema_version,
get_artifact_schema_version,
)
from dbt.exceptions import DbtInternalError
from dbt.events.functions import fire_event
@@ -31,6 +32,8 @@ from typing import (
Optional,
Sequence,
Union,
Iterable,
Tuple,
)
from dbt.clients.system import write_json
@@ -59,7 +62,7 @@ class TimingInfo(dbtClassMixin):
# This is a context manager
class collect_timing_info:
def __init__(self, name: str, callback: Callable[[TimingInfo], None]):
def __init__(self, name: str, callback: Callable[[TimingInfo], None]) -> None:
self.timing_info = TimingInfo(name=name)
self.callback = callback
@@ -203,9 +206,15 @@ class RunResultsMetadata(BaseArtifactMetadata):
@dataclass
class RunResultOutput(BaseResult):
unique_id: str
compiled: Optional[bool]
compiled_code: Optional[str]
relation_name: Optional[str]
def process_run_result(result: RunResult) -> RunResultOutput:
compiled = isinstance(result.node, CompiledNode)
return RunResultOutput(
unique_id=result.node.unique_id,
status=result.status,
@@ -215,6 +224,9 @@ def process_run_result(result: RunResult) -> RunResultOutput:
message=result.message,
adapter_response=result.adapter_response,
failures=result.failures,
compiled=result.node.compiled if compiled else None, # type:ignore
compiled_code=result.node.compiled_code if compiled else None, # type:ignore
relation_name=result.node.relation_name if compiled else None, # type:ignore
)
@@ -237,7 +249,7 @@ class RunExecutionResult(
@dataclass
@schema_version("run-results", 4)
@schema_version("run-results", 5)
class RunResultsArtifact(ExecutionResult, ArtifactMixin):
results: Sequence[RunResultOutput]
args: Dict[str, Any] = field(default_factory=dict)
@@ -259,6 +271,27 @@ class RunResultsArtifact(ExecutionResult, ArtifactMixin):
)
return cls(metadata=meta, results=processed_results, elapsed_time=elapsed_time, args=args)
@classmethod
def compatible_previous_versions(cls) -> Iterable[Tuple[str, int]]:
return [
("run-results", 4),
]
@classmethod
def upgrade_schema_version(cls, data):
"""This overrides the "upgrade_schema_version" call in VersionedSchema (via
ArtifactMixin) to modify the dictionary passed in from earlier versions of the run_results."""
run_results_schema_version = get_artifact_schema_version(data)
# If less than the current version (v5), preprocess contents to match latest schema version
if run_results_schema_version <= 5:
# In v5, we added 'compiled' attributes to each result entry
# Going forward, dbt expects these to be populated
for result in data["results"]:
result["compiled"] = False
result["compiled_code"] = ""
result["relation_name"] = ""
return cls.from_dict(data)
def write(self, path: str):
write_json(path, self.to_dict(omit_none=False))

View File

@@ -1,13 +1,26 @@
from pathlib import Path
from .graph.manifest import WritableManifest
from .results import RunResultsArtifact
from .results import FreshnessExecutionResultArtifact
from typing import Optional
from dbt.contracts.graph.manifest import WritableManifest
from dbt.contracts.results import FreshnessExecutionResultArtifact
from dbt.contracts.results import RunResultsArtifact
from dbt.events.functions import fire_event
from dbt.events.types import WarnStateTargetEqual
from dbt.exceptions import IncompatibleSchemaError
def load_result_state(results_path) -> Optional[RunResultsArtifact]:
if results_path.exists() and results_path.is_file():
try:
return RunResultsArtifact.read_and_check_versions(str(results_path))
except IncompatibleSchemaError as exc:
exc.add_filename(str(results_path))
raise
return None
class PreviousState:
def __init__(self, state_path: Path, target_path: Path, project_root: Path):
def __init__(self, state_path: Path, target_path: Path, project_root: Path) -> None:
self.state_path: Path = state_path
self.target_path: Path = target_path
self.project_root: Path = project_root
@@ -16,6 +29,9 @@ class PreviousState:
self.sources: Optional[FreshnessExecutionResultArtifact] = None
self.sources_current: Optional[FreshnessExecutionResultArtifact] = None
if self.state_path == self.target_path:
fire_event(WarnStateTargetEqual(state_path=str(self.state_path)))
# Note: if state_path is absolute, project_root will be ignored.
manifest_path = self.project_root / self.state_path / "manifest.json"
if manifest_path.exists() and manifest_path.is_file():
@@ -26,12 +42,7 @@ class PreviousState:
raise
results_path = self.project_root / self.state_path / "run_results.json"
if results_path.exists() and results_path.is_file():
try:
self.results = RunResultsArtifact.read_and_check_versions(str(results_path))
except IncompatibleSchemaError as exc:
exc.add_filename(str(results_path))
raise
self.results = load_result_state(results_path)
sources_path = self.project_root / self.state_path / "sources.json"
if sources_path.exists() and sources_path.is_file():

View File

@@ -16,8 +16,10 @@ from dbt.dataclass_schema import dbtClassMixin
from dbt.dataclass_schema import (
ValidatedStringMixin,
ValidationError,
register_pattern,
)
from mashumaro.jsonschema import build_json_schema
from mashumaro.jsonschema.dialects import DRAFT_2020_12
import functools
SourceKey = Tuple[str, str]
@@ -90,7 +92,9 @@ class AdditionalPropertiesMixin:
cls_keys = cls._get_field_names()
new_dict = {}
for key, value in data.items():
if key not in cls_keys and key != "_extra":
# The pre-hook/post-hook mess hasn't been converted yet... That happens in
# the super().__pre_deserialize__ below...
if key not in cls_keys and key not in ["_extra", "pre-hook", "post-hook"]:
if "_extra" not in new_dict:
new_dict["_extra"] = {}
new_dict["_extra"][key] = value
@@ -192,11 +196,12 @@ class VersionedSchema(dbtClassMixin):
dbt_schema_version: ClassVar[SchemaVersion]
@classmethod
def json_schema(cls, embeddable: bool = False) -> Dict[str, Any]:
result = super().json_schema(embeddable=embeddable)
if not embeddable:
result["$id"] = str(cls.dbt_schema_version)
return result
@functools.lru_cache
def json_schema(cls) -> Dict[str, Any]:
json_schema_obj = build_json_schema(cls, dialect=DRAFT_2020_12, with_dialect_uri=True)
json_schema = json_schema_obj.to_dict()
json_schema["$id"] = str(cls.dbt_schema_version)
return json_schema
@classmethod
def is_compatible_version(cls, schema_version):
@@ -257,7 +262,29 @@ class ArtifactMixin(VersionedSchema, Writable, Readable):
raise DbtInternalError("Cannot call from_dict with no schema version!")
def get_artifact_schema_version(dct: dict) -> int:
schema_version = dct.get("metadata", {}).get("dbt_schema_version", None)
if not schema_version:
raise ValueError("Artifact is missing schema version")
# schema_version is in this format: https://schemas.getdbt.com/dbt/manifest/v10.json
# What the code below is doing:
# 1. Split on "/" v10.json
# 2. Split on "." v10
# 3. Skip first character 10
# 4. Convert to int
# TODO: If this gets more complicated, turn into a regex
return int(schema_version.split("/")[-1].split(".")[0][1:])
class Identifier(ValidatedStringMixin):
"""Our definition of a valid Identifier is the same as what's valid for an unquoted database table name.
That is:
1. It can contain a-z, A-Z, 0-9, and _
1. It cannot start with a number
"""
ValidationRegex = r"^[^\d\W]\w*$"
@classmethod
@@ -271,6 +298,3 @@ class Identifier(ValidatedStringMixin):
return False
return True
register_pattern(Identifier, r"^[^\d\W]\w*$")

View File

@@ -1,97 +1,123 @@
from typing import (
Type,
ClassVar,
cast,
)
from typing import ClassVar, cast, get_type_hints, List, Tuple, Dict, Any, Optional
import re
from dataclasses import fields
import jsonschema
from dataclasses import fields, Field
from enum import Enum
from datetime import datetime
from dateutil.parser import parse
from hologram import JsonSchemaMixin, FieldEncoder, ValidationError
# type: ignore
from mashumaro import DataClassDictMixin
from mashumaro.config import TO_DICT_ADD_OMIT_NONE_FLAG, BaseConfig as MashBaseConfig
from mashumaro.types import SerializableType, SerializationStrategy
from mashumaro.jsonschema import build_json_schema
import functools
class ValidationError(jsonschema.ValidationError):
pass
class DateTimeSerialization(SerializationStrategy):
def serialize(self, value):
def serialize(self, value) -> str:
out = value.isoformat()
# Assume UTC if timezone is missing
if value.tzinfo is None:
out += "Z"
return out
def deserialize(self, value):
def deserialize(self, value) -> datetime:
return value if isinstance(value, datetime) else parse(cast(str, value))
# This class pulls in both JsonSchemaMixin from Hologram and
# DataClassDictMixin from our fork of Mashumaro. The 'to_dict'
# and 'from_dict' methods come from Mashumaro. Building
# jsonschemas for every class and the 'validate' method
# come from Hologram.
class dbtClassMixin(DataClassDictMixin, JsonSchemaMixin):
class dbtMashConfig(MashBaseConfig):
code_generation_options = [
TO_DICT_ADD_OMIT_NONE_FLAG,
]
serialization_strategy = {
datetime: DateTimeSerialization(),
}
json_schema = {
"additionalProperties": False,
}
serialize_by_alias = True
# This class pulls in DataClassDictMixin from Mashumaro. The 'to_dict'
# and 'from_dict' methods come from Mashumaro.
class dbtClassMixin(DataClassDictMixin):
"""The Mixin adds methods to generate a JSON schema and
convert to and from JSON encodable dicts with validation
against the schema
"""
class Config(MashBaseConfig):
code_generation_options = [
TO_DICT_ADD_OMIT_NONE_FLAG,
]
serialization_strategy = {
datetime: DateTimeSerialization(),
}
_mapped_fields: ClassVar[Optional[Dict[Any, List[Tuple[Field, str]]]]] = None
# Config class used by Mashumaro
class Config(dbtMashConfig):
pass
_hyphenated: ClassVar[bool] = False
ADDITIONAL_PROPERTIES: ClassVar[bool] = False
# This is called by the mashumaro to_dict in order to handle
# nested classes.
# Munges the dict that's returned.
def __post_serialize__(self, dct):
if self._hyphenated:
new_dict = {}
for key in dct:
if "_" in key:
new_key = key.replace("_", "-")
new_dict[new_key] = dct[key]
else:
new_dict[key] = dct[key]
dct = new_dict
return dct
# This is called by the mashumaro _from_dict method, before
# performing the conversion to a dict
# This is called by the mashumaro from_dict in order to handle
# nested classes. We no longer do any munging here, but leaving here
# so that subclasses can leave super() in place for possible future needs.
@classmethod
def __pre_deserialize__(cls, data):
# `data` might not be a dict, e.g. for `query_comment`, which accepts
# a dict or a string; only snake-case for dict values.
if cls._hyphenated and isinstance(data, dict):
new_dict = {}
for key in data:
if "-" in key:
new_key = key.replace("-", "_")
new_dict[new_key] = data[key]
else:
new_dict[key] = data[key]
data = new_dict
return data
# This is used in the hologram._encode_field method, which calls
# a 'to_dict' method which does not have the same parameters in
# hologram and in mashumaro.
def _local_to_dict(self, **kwargs):
args = {}
if "omit_none" in kwargs:
args["omit_none"] = kwargs["omit_none"]
return self.to_dict(**args)
# This is called by the mashumaro to_dict in order to handle
# nested classes. We no longer do any munging here, but leaving here
# so that subclasses can leave super() in place for possible future needs.
def __post_serialize__(self, data):
return data
@classmethod
@functools.lru_cache
def json_schema(cls):
json_schema_obj = build_json_schema(cls)
json_schema = json_schema_obj.to_dict()
return json_schema
@classmethod
def validate(cls, data):
json_schema = cls.json_schema()
validator = jsonschema.Draft7Validator(json_schema)
error = next(iter(validator.iter_errors(data)), None)
if error is not None:
raise ValidationError.create_from(error) from error
# This method was copied from hologram. Used in model_config.py and relation.py
@classmethod
def _get_fields(cls) -> List[Tuple[Field, str]]:
if cls._mapped_fields is None:
cls._mapped_fields = {}
if cls.__name__ not in cls._mapped_fields:
mapped_fields = []
type_hints = get_type_hints(cls)
for f in fields(cls): # type: ignore
# Skip internal fields
if f.name.startswith("_"):
continue
# Note fields() doesn't resolve forward refs
f.type = type_hints[f.name]
# hologram used the "field_mapping" here, but we use the
# the field's metadata "alias". Since this method is mainly
# just used in merging config dicts, it mostly applies to
# pre-hook and post-hook.
field_name = f.metadata.get("alias", f.name)
mapped_fields.append((f, field_name))
cls._mapped_fields[cls.__name__] = mapped_fields
return cls._mapped_fields[cls.__name__]
# copied from hologram. Used in tests
@classmethod
def _get_field_names(cls):
return [element[1] for element in cls._get_fields()]
class ValidatedStringMixin(str, SerializableType):
@@ -130,38 +156,10 @@ class StrEnum(str, SerializableType, Enum):
return cls(value)
class HyphenatedDbtClassMixin(dbtClassMixin):
# used by from_dict/to_dict
_hyphenated: ClassVar[bool] = True
# used by jsonschema validation, _get_fields
@classmethod
def field_mapping(cls):
result = {}
for field in fields(cls):
skip = field.metadata.get("preserve_underscore")
if skip:
continue
if "_" in field.name:
result[field.name] = field.name.replace("_", "-")
return result
class ExtensibleDbtClassMixin(dbtClassMixin):
ADDITIONAL_PROPERTIES = True
# This is used by Hologram in jsonschema validation
def register_pattern(base_type: Type, pattern: str) -> None:
"""base_type should be a typing.NewType that should always have the given
regex pattern. That means that its underlying type ('__supertype__') had
better be a str!
"""
class PatternEncoder(FieldEncoder):
@property
def json_schema(self):
return {"type": "string", "pattern": pattern}
dbtClassMixin.register_field_encoders({base_type: PatternEncoder()})
class Config(dbtMashConfig):
json_schema = {
"additionalProperties": True,
}

Some files were not shown because too many files have changed in this diff Show More