Compare commits

..

3 Commits

Author SHA1 Message Date
Dave Connors
5f43a1c297 resolve merge conflict 2022-09-14 14:51:01 -05:00
Dave Connors
11e155de86 changie entry 2022-09-14 14:05:24 -05:00
Dave Connors
823aa0a575 formatting 2022-09-14 14:04:06 -05:00
165 changed files with 1763 additions and 3629 deletions

View File

@@ -1,7 +0,0 @@
kind: Docs
body: Refer to exposures by their label by default.
time: 2022-09-20T15:20:40.652948-05:00
custom:
Author: emmyoop
Issue: "306"
PR: "307"

View File

@@ -1,7 +0,0 @@
kind: Features
body: merge_exclude_columns for incremental materialization
time: 2022-07-16T14:21:16.592519-05:00
custom:
Author: dave-connors-3
Issue: "5260"
PR: "5457"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Search current working directory for `profiles.yml`
time: 2022-08-25T19:50:23.940417-06:00
custom:
Author: dbeatty10
Issue: "5411"
PR: "5717"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Flags work with new Click CLI
time: 2022-09-08T12:36:50.386978-05:00
custom:
Author: iknox-fa
Issue: "5529"
PR: "5790"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Add metadata env method to ProviderContext class
time: 2022-09-09T20:46:43.889302+01:00
custom:
Author: jared-rimmer
Issue: "5522"
PR: "5794"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Array macros
time: 2022-09-12T22:22:27.475515-06:00
custom:
Author: graciegoheen dbeatty10
Issue: "5520"
PR: "5823"

View File

@@ -0,0 +1,7 @@
kind: Features
body: add fill missing value attribute
time: 2022-09-14T14:05:11.927083-05:00
custom:
Author: dave-connors-3
Issue: "5842"
PR: "5843"

View File

@@ -1,7 +0,0 @@
kind: Features
body: add -fr flag shorthand
time: 2022-09-19T11:29:03.774678-05:00
custom:
Author: dave-connors-3
Issue: "5878"
PR: "5879"

View File

@@ -1,7 +0,0 @@
kind: Features
body: add type_boolean as a data type macro
time: 2022-09-19T23:14:14.9871+01:00
custom:
Author: jpmmcneill
Issue: "5739"
PR: "5875"

View File

@@ -1,7 +0,0 @@
kind: Features
body: Support .dbtignore in project root to ignore certain files being read by dbt
time: 2022-09-21T14:52:22.131627-07:00
custom:
Author: ChenyuLInx
Issue: "5733"
PR: "5897"

View File

@@ -1,9 +0,0 @@
kind: Features
body: This conditionally no-ops warehouse connection at compile depending on an env
var, disabling introspection/queries during compilation only. This is a temporary
solution to more complex permissions requirements for the semantic layer.
time: 2022-09-26T13:06:27.591061-05:00
custom:
Author: racheldaniel
Issue: "5936"
PR: "5926"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: Fix typos of comments in core/dbt/adapters/
time: 2022-08-22T19:42:38.593923+09:00
custom:
Author: yoiki
Issue: "5690"
PR: "5693"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: Prevent event_history from holding references
time: 2022-09-16T09:17:23.273847-04:00
custom:
Author: gshank
Issue: "5848"
PR: "5858"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: ConfigSelectorMethod should check for bools
time: 2022-09-20T18:18:56.630628+01:00
custom:
Author: danielcmessias
Issue: "5890"
PR: "5889"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: shorthand for full refresh should be one character
time: 2022-09-22T08:39:26.948671-05:00
custom:
Author: dave-connors-3
Issue: "5878"
PR: "5908"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: Fix macro resolution order during static analysis for custom generic tests
time: 2022-09-23T14:32:26.857376+02:00
custom:
Author: jtcohen6
Issue: "5720"
PR: "5907"

View File

@@ -1,7 +0,0 @@
kind: Fixes
body: Fix race condition when invoking dbt via lib.py concurrently
time: 2022-09-23T17:45:04.405026-04:00
custom:
Author: drewbanin
Issue: "5919"
PR: "5921"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: remove key as reserved keyword from test_bool_or
time: 2022-09-12T19:03:41.481601+02:00
custom:
Author: sdebruyn
Issue: "5817"
PR: "5818"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Compatibiltiy for metric attribute renaming
time: 2022-09-13T11:17:44.953536+02:00
custom:
Author: jtcohen6 callum-mcdata
Issue: "5807"
PR: "5825"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Add name validation for metrics
time: 2022-09-14T13:26:32.387524-05:00
custom:
Author: emmyoop
Issue: "5456"
PR: "5841"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Validate exposure name and add label
time: 2022-09-14T15:00:58.982982-05:00
custom:
Author: emmyoop
Issue: "5606"
PR: "5844"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: remove source quoting setting in adapter tests
time: 2022-09-14T19:39:33.688385+02:00
custom:
Author: sdebruyn
Issue: "5836"
PR: "5839"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Adding validation for metric expression attribute
time: 2022-09-16T15:47:12.799002-05:00
custom:
Author: callum-mcdata
Issue: "5871"
PR: "5873"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Profiling and Adapter Management work with Click CLI
time: 2022-09-20T14:48:42.070256-05:00
custom:
Author: iknox-fa
Issue: "5531"
PR: "5892"

View File

@@ -1,7 +0,0 @@
kind: Under the Hood
body: Reparse references to deleted metric
time: 2022-09-23T13:35:25.681656-04:00
custom:
Author: gshank
Issue: "5444"
PR: "5920"

View File

@@ -44,7 +44,7 @@ custom:
footerFormat: | footerFormat: |
{{- $contributorDict := dict }} {{- $contributorDict := dict }}
{{- /* any names added to this list should be all lowercase for later matching purposes */}} {{- /* any names added to this list should be all lowercase for later matching purposes */}}
{{- $core_team := list "peterallenwebb" "emmyoop" "nathaniel-may" "gshank" "leahwicz" "chenyulinx" "stu-k" "iknox-fa" "versusfacit" "mcknight-42" "jtcohen6" "dependabot[bot]" "snyk-bot" }} {{- $core_team := list "emmyoop" "nathaniel-may" "gshank" "leahwicz" "chenyulinx" "stu-k" "iknox-fa" "versusfacit" "mcknight-42" "jtcohen6" "dependabot[bot]" "snyk-bot" }}
{{- range $change := .Changes }} {{- range $change := .Changes }}
{{- $authorList := splitList " " $change.Custom.Author }} {{- $authorList := splitList " " $change.Custom.Author }}
{{- /* loop through all authors for a PR */}} {{- /* loop through all authors for a PR */}}

View File

@@ -15,9 +15,6 @@ on:
issues: issues:
types: [closed, deleted, reopened] types: [closed, deleted, reopened]
# no special access is needed
permissions: read-all
jobs: jobs:
call-label-action: call-label-action:
uses: dbt-labs/jira-actions/.github/workflows/jira-transition.yml@main uses: dbt-labs/jira-actions/.github/workflows/jira-transition.yml@main

View File

@@ -20,9 +20,6 @@ on:
description: 'The release version number (i.e. 1.0.0b1)' description: 'The release version number (i.e. 1.0.0b1)'
required: true required: true
permissions:
contents: write # this is the permission that allows creating a new release
defaults: defaults:
run: run:
shell: bash shell: bash

View File

@@ -21,9 +21,6 @@ on:
- "*.latest" - "*.latest"
- "releases/*" - "releases/*"
# no special access is needed
permissions: read-all
env: env:
LATEST_SCHEMA_PATH: ${{ github.workspace }}/new_schemas LATEST_SCHEMA_PATH: ${{ github.workspace }}/new_schemas
SCHEMA_DIFF_ARTIFACT: ${{ github.workspace }}//schema_schanges.txt SCHEMA_DIFF_ARTIFACT: ${{ github.workspace }}//schema_schanges.txt

View File

@@ -3,10 +3,6 @@ on:
schedule: schedule:
- cron: "30 1 * * *" - cron: "30 1 * * *"
permissions:
issues: write
pull-requests: write
jobs: jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -20,10 +20,6 @@ on:
description: 'The version number to bump to (ex. 1.2.0, 1.3.0b1)' description: 'The version number to bump to (ex. 1.2.0, 1.3.0b1)'
required: true required: true
permissions:
contents: write
pull-requests: write
jobs: jobs:
bump: bump:
runs-on: ubuntu-latest runs-on: ubuntu-latest

1
.gitignore vendored
View File

@@ -95,7 +95,6 @@ venv/
# vscode # vscode
.vscode/ .vscode/
*.code-workspace
# poetry # poetry
pyproject.toml pyproject.toml

View File

@@ -6,7 +6,7 @@ exclude: ^test/
# Force all unspecified python hooks to run python 3.8 # Force all unspecified python hooks to run python 3.8
default_language_version: default_language_version:
python: python3 python: python3.8
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks

View File

@@ -12,7 +12,6 @@ class Column:
"TIMESTAMP": "TIMESTAMP", "TIMESTAMP": "TIMESTAMP",
"FLOAT": "FLOAT", "FLOAT": "FLOAT",
"INTEGER": "INT", "INTEGER": "INT",
"BOOLEAN": "BOOLEAN",
} }
column: str column: str
dtype: str dtype: str

View File

@@ -1220,6 +1220,7 @@ class BaseAdapter(metaclass=AdapterMeta):
def default_python_submission_method(self) -> str: def default_python_submission_method(self) -> str:
raise NotImplementedError("default_python_submission_method is not specified") raise NotImplementedError("default_python_submission_method is not specified")
@available.parse_none
@log_code_execution @log_code_execution
def submit_python_job(self, parsed_model: dict, compiled_code: str) -> AdapterResponse: def submit_python_job(self, parsed_model: dict, compiled_code: str) -> AdapterResponse:
submission_method = parsed_model["config"].get( submission_method = parsed_model["config"].get(

View File

@@ -384,7 +384,7 @@ class RelationsCache:
relation = self.relations.pop(old_key) relation = self.relations.pop(old_key)
new_key = new_relation.key() new_key = new_relation.key()
# relation has to rename its innards, so it needs the _CachedRelation. # relaton has to rename its innards, so it needs the _CachedRelation.
relation.rename(new_relation) relation.rename(new_relation)
# update all the relations that refer to it # update all the relations that refer to it
for cached in self.relations.values(): for cached in self.relations.values():

View File

@@ -1,17 +1,23 @@
import threading import threading
from contextlib import contextmanager
from importlib import import_module
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Type from importlib import import_module
from typing import Type, Dict, Any, List, Optional, Set
from dbt.adapters.base.plugin import AdapterPlugin from dbt.exceptions import RuntimeException, InternalException
from dbt.adapters.protocol import AdapterConfig, AdapterProtocol, RelationProtocol from dbt.include.global_project import (
from dbt.contracts.connection import AdapterRequiredConfig, Credentials PACKAGE_PATH as GLOBAL_PROJECT_PATH,
PROJECT_NAME as GLOBAL_PROJECT_NAME,
)
from dbt.events.functions import fire_event from dbt.events.functions import fire_event
from dbt.events.types import AdapterImportError, PluginLoadError from dbt.events.types import AdapterImportError, PluginLoadError
from dbt.exceptions import InternalException, RuntimeException from dbt.contracts.connection import Credentials, AdapterRequiredConfig
from dbt.include.global_project import PACKAGE_PATH as GLOBAL_PROJECT_PATH from dbt.adapters.protocol import (
from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME AdapterProtocol,
AdapterConfig,
RelationProtocol,
)
from dbt.adapters.base.plugin import AdapterPlugin
Adapter = AdapterProtocol Adapter = AdapterProtocol
@@ -58,7 +64,7 @@ class AdapterContainer:
# if we failed to import the target module in particular, inform # if we failed to import the target module in particular, inform
# the user about it via a runtime error # the user about it via a runtime error
if exc.name == "dbt.adapters." + name: if exc.name == "dbt.adapters." + name:
fire_event(AdapterImportError(exc=str(exc))) fire_event(AdapterImportError(exc=exc))
raise RuntimeException(f"Could not find adapter type {name}!") raise RuntimeException(f"Could not find adapter type {name}!")
# otherwise, the error had to have come from some underlying # otherwise, the error had to have come from some underlying
# library. Log the stack trace. # library. Log the stack trace.
@@ -211,12 +217,3 @@ def get_adapter_package_names(name: Optional[str]) -> List[str]:
def get_adapter_type_names(name: Optional[str]) -> List[str]: def get_adapter_type_names(name: Optional[str]) -> List[str]:
return FACTORY.get_adapter_type_names(name) return FACTORY.get_adapter_type_names(name)
@contextmanager
def adapter_management():
reset_adapters()
try:
yield
finally:
cleanup_connections()

View File

@@ -88,7 +88,7 @@ class AdapterProtocol( # type: ignore[misc]
], ],
): ):
# N.B. Technically these are ClassVars, but mypy doesn't support putting type vars in a # N.B. Technically these are ClassVars, but mypy doesn't support putting type vars in a
# ClassVar due to the restrictiveness of PEP-526 # ClassVar due to the restirctiveness of PEP-526
# See: https://github.com/python/mypy/issues/5144 # See: https://github.com/python/mypy/issues/5144
AdapterSpecificConfigs: Type[AdapterConfig_T] AdapterSpecificConfigs: Type[AdapterConfig_T]
Column: Type[Column_T] Column: Type[Column_T]

View File

@@ -1,44 +0,0 @@
# TODO Move this to /core/dbt/flags.py when we're ready to break things
import os
from dataclasses import dataclass
from multiprocessing import get_context
from pprint import pformat as pf
from click import get_current_context
if os.name != "nt":
# https://bugs.python.org/issue41567
import multiprocessing.popen_spawn_posix # type: ignore # noqa: F401
@dataclass(frozen=True)
class Flags:
def __init__(self, ctx=None) -> None:
if ctx is None:
ctx = get_current_context()
def assign_params(ctx):
"""Recursively adds all click params to flag object"""
for param_name, param_value in ctx.params.items():
# N.B. You have to use the base MRO method (object.__setattr__) to set attributes
# when using frozen dataclasses.
# https://docs.python.org/3/library/dataclasses.html#frozen-instances
if hasattr(self, param_name):
raise Exception(f"Duplicate flag names found in click command: {param_name}")
object.__setattr__(self, param_name.upper(), param_value)
if ctx.parent:
assign_params(ctx.parent)
assign_params(ctx)
# Hard coded flags
object.__setattr__(self, "WHICH", ctx.info_name)
object.__setattr__(self, "MP_CONTEXT", get_context("spawn"))
# Support console DO NOT TRACK initiave
if os.getenv("DO_NOT_TRACK", "").lower() in (1, "t", "true", "y", "yes"):
object.__setattr__(self, "ANONYMOUS_USAGE_STATS", False)
def __str__(self) -> str:
return str(pf(self.__dict__))

View File

@@ -1,70 +1,44 @@
import inspect # This is temporary for RAT-ing
from copy import copy
from pprint import pformat as pf # This is temporary for RAT-ing
import click import click
from dbt.adapters.factory import adapter_management
from dbt.cli import params as p from dbt.cli import params as p
from dbt.cli.flags import Flags import sys
from dbt.profiler import profiler
# This is temporary for RAT-ing
def cli_runner(): import inspect
# Alias "list" to "ls" from pprint import pformat as pf
ls = copy(cli.commands["list"])
ls.hidden = True
cli.add_command(ls, "ls")
# Run the cli
cli()
# dbt # dbt
@click.group( @click.group(
context_settings={"help_option_names": ["-h", "--help"]},
invoke_without_command=True, invoke_without_command=True,
no_args_is_help=True, no_args_is_help=True,
epilog="Specify one of these sub-commands and you can find more help from there.", epilog="Specify one of these sub-commands and you can find more help from there.",
) )
@click.pass_context @click.pass_context
@p.anonymous_usage_stats @p.version
@p.cache_selected_only @p.cache_selected_only
@p.debug @p.debug
@p.enable_legacy_logger
@p.event_buffer_size
@p.fail_fast @p.fail_fast
@p.log_cache_events
@p.log_format @p.log_format
@p.macro_debugging
@p.partial_parse @p.partial_parse
@p.print @p.print
@p.printer_width @p.printer_width
@p.quiet @p.quiet
@p.record_timing_info @p.send_anonymous_usage_stats
@p.static_parser @p.static_parser
@p.use_colors @p.use_colors
@p.use_experimental_parser @p.use_experimental_parser
@p.version
@p.version_check @p.version_check
@p.warn_error @p.warn_error
@p.write_json @p.write_json
@p.event_buffer_size
@p.record_timing
def cli(ctx, **kwargs): def cli(ctx, **kwargs):
"""An ELT tool for managing your SQL transformations and data models. """An ELT tool for managing your SQL transformations and data models.
For more documentation on these commands, visit: docs.getdbt.com For more documentation on these commands, visit: docs.getdbt.com
""" """
incomplete_flags = Flags() if kwargs.get("version", False):
# Profiling
if incomplete_flags.RECORD_TIMING_INFO:
ctx.with_resource(profiler(enable=True, outfile=incomplete_flags.RECORD_TIMING_INFO))
# Adapter management
ctx.with_resource(adapter_management())
# Version info
if incomplete_flags.VERSION:
click.echo(f"`version` called\n ctx.params: {pf(ctx.params)}") click.echo(f"`version` called\n ctx.params: {pf(ctx.params)}")
return sys.exit()
else: else:
del ctx.params["version"] del ctx.params["version"]
@@ -72,43 +46,26 @@ def cli(ctx, **kwargs):
# dbt build # dbt build
@cli.command("build") @cli.command("build")
@click.pass_context @click.pass_context
@p.defer
@p.exclude
@p.fail_fast
@p.full_refresh
@p.indirect_selection
@p.log_path
@p.models
@p.profile
@p.profiles_dir
@p.project_dir
@p.selector
@p.show
@p.state
@p.store_failures
@p.target
@p.target_path
@p.threads
@p.vars
@p.version_check
def build(ctx, **kwargs): def build(ctx, **kwargs):
"""Run all Seeds, Models, Snapshots, and tests in DAG order""" """Run all Seeds, Models, Snapshots, and tests in DAG order"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt clean # dbt clean
@cli.command("clean") @cli.command("clean")
@click.pass_context @click.pass_context
@p.profile
@p.profiles_dir
@p.project_dir @p.project_dir
@p.profiles_dir
@p.profile
@p.target @p.target
@p.vars @p.vars
def clean(ctx, **kwargs): def clean(ctx, **kwargs):
"""Delete all folders in the clean-targets list (usually the dbt_packages and target directories.)""" """Delete all folders in the clean-targets list (usually the dbt_packages and target directories.)"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt docs # dbt docs
@@ -121,82 +78,86 @@ def docs(ctx, **kwargs):
# dbt docs generate # dbt docs generate
@docs.command("generate") @docs.command("generate")
@click.pass_context @click.pass_context
@p.version_check
@p.project_dir
@p.profiles_dir
@p.profile
@p.target
@p.vars
@p.compile_docs @p.compile_docs
@p.defer @p.defer
@p.exclude @p.threads
@p.target_path
@p.log_path @p.log_path
@p.models @p.models
@p.profile @p.exclude
@p.profiles_dir
@p.project_dir
@p.selector @p.selector
@p.state @p.state
@p.target
@p.target_path
@p.threads
@p.vars
@p.version_check
def docs_generate(ctx, **kwargs): def docs_generate(ctx, **kwargs):
"""Generate the documentation website for your project""" """Generate the documentation website for your project"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.parent.params)}"
)
# dbt docs serve # dbt docs serve
@docs.command("serve") @docs.command("serve")
@click.pass_context @click.pass_context
@p.browser
@p.port
@p.profile
@p.profiles_dir
@p.project_dir @p.project_dir
@p.profiles_dir
@p.profile
@p.target @p.target
@p.vars @p.vars
@p.port
@p.browser
def docs_serve(ctx, **kwargs): def docs_serve(ctx, **kwargs):
"""Serve the documentation website for your project""" """Serve the documentation website for your project"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.parent.params)}"
)
# dbt compile # dbt compile
@cli.command("compile") @cli.command("compile")
@click.pass_context @click.pass_context
@p.defer @p.version_check
@p.exclude @p.project_dir
@p.full_refresh @p.profiles_dir
@p.profile
@p.target
@p.vars
@p.parse_only
@p.threads
@p.target_path
@p.log_path @p.log_path
@p.models @p.models
@p.parse_only @p.exclude
@p.profile
@p.profiles_dir
@p.project_dir
@p.selector @p.selector
@p.state @p.state
@p.target @p.defer
@p.target_path @p.full_refresh
@p.threads
@p.vars
@p.version_check
def compile(ctx, **kwargs): def compile(ctx, **kwargs):
"""Generates executable SQL from source, model, test, and analysis files. Compiled SQL files are written to the target/ directory.""" """Generates executable SQL from source, model, test, and analysis files. Compiled SQL files are written to the target/ directory."""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt debug # dbt debug
@cli.command("debug") @cli.command("debug")
@click.pass_context @click.pass_context
@p.config_dir @p.version_check
@p.profile
@p.profiles_dir
@p.project_dir @p.project_dir
@p.profiles_dir
@p.profile
@p.target @p.target
@p.vars @p.vars
@p.version_check @p.config_dir
def debug(ctx, **kwargs): def debug(ctx, **kwargs):
"""Show some helpful information about dbt for debugging. Not to be confused with the --debug option which increases verbosity.""" """Show some helpful information about dbt for debugging. Not to be confused with the --debug option which increases verbosity."""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt deps # dbt deps
@@ -209,8 +170,9 @@ def debug(ctx, **kwargs):
@p.vars @p.vars
def deps(ctx, **kwargs): def deps(ctx, **kwargs):
"""Pull the most recent version of the dependencies listed in packages.yml""" """Pull the most recent version of the dependencies listed in packages.yml"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt init # dbt init
@@ -219,139 +181,147 @@ def deps(ctx, **kwargs):
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.skip_profile_setup
@p.target @p.target
@p.vars @p.vars
@p.skip_profile_setup
def init(ctx, **kwargs): def init(ctx, **kwargs):
"""Initialize a new DBT project.""" """Initialize a new DBT project."""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt list # dbt list
# dbt TODO: Figure out aliasing for ls (or just c/p?)
@cli.command("list") @cli.command("list")
@click.pass_context @click.pass_context
@p.exclude
@p.indirect_selection
@p.models
@p.output
@p.output_keys
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.resource_type
@p.selector
@p.state
@p.target @p.target
@p.vars @p.vars
@p.output
@p.ouptut_keys
@p.resource_type
@p.models
@p.indirect_selection
@p.exclude
@p.selector
@p.state
def list(ctx, **kwargs): def list(ctx, **kwargs):
"""List the resources in your project""" """List the resources in your project"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt parse # dbt parse
@cli.command("parse") @cli.command("parse")
@click.pass_context @click.pass_context
@p.compile_parse
@p.log_path
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.target @p.target
@p.target_path
@p.threads
@p.vars @p.vars
@p.version_check
@p.write_manifest @p.write_manifest
@p.compile_parse
@p.threads
@p.target_path
@p.log_path
@p.version_check
def parse(ctx, **kwargs): def parse(ctx, **kwargs):
"""Parses the project and provides information on performance""" """Parses the project and provides information on performance"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt run # dbt run
@cli.command("run") @cli.command("run")
@click.pass_context @click.pass_context
@p.defer
@p.exclude
@p.fail_fast @p.fail_fast
@p.full_refresh @p.version_check
@p.log_path
@p.models
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.selector
@p.state
@p.target @p.target
@p.vars
@p.log_path
@p.target_path @p.target_path
@p.threads @p.threads
@p.vars @p.models
@p.version_check @p.exclude
@p.selector
@p.state
@p.defer
@p.full_refresh
def run(ctx, **kwargs): def run(ctx, **kwargs):
"""Compile SQL and execute against the current target database.""" """Compile SQL and execute against the current target database."""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt run operation # dbt run operation
@cli.command("run-operation") @cli.command("run-operation")
@click.pass_context @click.pass_context
@p.args
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.target @p.target
@p.vars @p.vars
@p.args
def run_operation(ctx, **kwargs): def run_operation(ctx, **kwargs):
"""Run the named macro with any supplied arguments.""" """Run the named macro with any supplied arguments."""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt seed # dbt seed
@cli.command("seed") @cli.command("seed")
@click.pass_context @click.pass_context
@p.exclude @p.version_check
@p.full_refresh
@p.log_path
@p.models
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.selector
@p.show
@p.state
@p.target @p.target
@p.vars
@p.full_refresh
@p.log_path
@p.target_path @p.target_path
@p.threads @p.threads
@p.vars @p.models
@p.version_check @p.exclude
@p.selector
@p.state
@p.show
def seed(ctx, **kwargs): def seed(ctx, **kwargs):
"""Load data from csv files into your data warehouse.""" """Load data from csv files into your data warehouse."""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt snapshot # dbt snapshot
@cli.command("snapshot") @cli.command("snapshot")
@click.pass_context @click.pass_context
@p.defer
@p.exclude
@p.models
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.target
@p.vars
@p.threads
@p.models
@p.exclude
@p.selector @p.selector
@p.state @p.state
@p.target @p.defer
@p.threads
@p.vars
def snapshot(ctx, **kwargs): def snapshot(ctx, **kwargs):
"""Execute snapshots defined in your project""" """Execute snapshots defined in your project"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# dbt source # dbt source
@@ -364,49 +334,51 @@ def source(ctx, **kwargs):
# dbt source freshness # dbt source freshness
@source.command("freshness") @source.command("freshness")
@click.pass_context @click.pass_context
@p.exclude
@p.models
@p.output_path # TODO: Is this ok to re-use? We have three different output params, how much can we consolidate?
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.target
@p.vars
@p.threads
@p.models
@p.exclude
@p.selector @p.selector
@p.state @p.state
@p.target @p.output_path # TODO: Is this ok to re-use? We have three different output params, how much can we consolidate?
@p.threads
@p.vars
def freshness(ctx, **kwargs): def freshness(ctx, **kwargs):
"""Snapshots the current freshness of the project's sources""" """Snapshots the current freshness of the project's sources"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.parent.params)}"
)
# dbt test # dbt test
@cli.command("test") @cli.command("test")
@click.pass_context @click.pass_context
@p.defer
@p.exclude
@p.fail_fast @p.fail_fast
@p.indirect_selection @p.version_check
@p.log_path @p.store_failures
@p.models
@p.profile @p.profile
@p.profiles_dir @p.profiles_dir
@p.project_dir @p.project_dir
@p.selector
@p.state
@p.store_failures
@p.target @p.target
@p.vars
@p.indirect_selection
@p.log_path
@p.target_path @p.target_path
@p.threads @p.threads
@p.vars @p.models
@p.version_check @p.exclude
@p.selector
@p.state
@p.defer
def test(ctx, **kwargs): def test(ctx, **kwargs):
"""Runs tests on data in deployed models. Run this after `dbt run`""" """Runs tests on data in deployed models. Run this after `dbt run`"""
flags = Flags() click.echo(
click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {flags}") f"`{inspect.stack()[0][3]}` called\n kwargs: {kwargs}\n ctx: {pf(ctx.parent.params)}"
)
# Support running as a module # Support running as a module
if __name__ == "__main__": if __name__ == "__main__":
cli_runner() cli()

View File

@@ -1,33 +0,0 @@
from click import ParamType
import yaml
class YAML(ParamType):
"""The Click YAML type. Converts YAML strings into objects."""
name = "YAML"
def convert(self, value, param, ctx):
# assume non-string values are a problem
if not isinstance(value, str):
self.fail(f"Cannot load YAML from type {type(value)}", param, ctx)
try:
return yaml.load(value, Loader=yaml.Loader)
except yaml.parser.ParserError:
self.fail(f"String '{value}' is not valid YAML", param, ctx)
class Truthy(ParamType):
"""The Click Truthy type. Converts strings into a "truthy" type"""
name = "TRUTHY"
def convert(self, value, param, ctx):
# assume non-string / non-None values are a problem
if not isinstance(value, (str, None)):
self.fail(f"Cannot load TRUTHY from type {type(value)}", param, ctx)
if value is None or value.lower() in ("0", "false", "f"):
return None
else:
return value

View File

@@ -1,58 +1,56 @@
from pathlib import Path, PurePath
import click import click
from dbt.cli.option_types import YAML import yaml
from dbt.cli.resolvers import default_project_dir, default_profiles_dir from pathlib import Path, PurePath
from click import ParamType
# TODO: The name (reflected in flags) is a correction! class YAML(ParamType):
# The original name was `SEND_ANONYMOUS_USAGE_STATS` and used an env var called "DBT_SEND_ANONYMOUS_USAGE_STATS" """The Click YAML type. Converts YAML strings into objects."""
# Both of which break existing naming conventions (doesn't match param flag).
# This will need to be fixed before use in the main codebase and communicated as a change to the community! name = "YAML"
anonymous_usage_stats = click.option(
"--anonymous-usage-stats/--no-anonymous-usage-stats", def convert(self, value, param, ctx):
envvar="DBT_ANONYMOUS_USAGE_STATS", # assume non-string values are a problem
help="Send anonymous usage stats to dbt Labs.", if not isinstance(value, str):
default=True, self.fail(f"Cannot load YAML from type {type(value)}", param, ctx)
) try:
return yaml.load(value, Loader=yaml.Loader)
except yaml.parser.ParserError:
self.fail(f"String '{value}' is not valid YAML", param, ctx)
args = click.option( args = click.option(
"--args", "--args",
envvar=None,
help="Supply arguments to the macro. This dictionary will be mapped to the keyword arguments defined in the selected macro. This argument should be a YAML string, eg. '{my_variable: my_value}'", help="Supply arguments to the macro. This dictionary will be mapped to the keyword arguments defined in the selected macro. This argument should be a YAML string, eg. '{my_variable: my_value}'",
type=YAML(), type=YAML(),
) )
browser = click.option( browser = click.option(
"--browser/--no-browser", "--browser/--no-browser",
envvar=None,
help="Wether or not to open a local web browser after starting the server", help="Wether or not to open a local web browser after starting the server",
default=True, default=True,
) )
cache_selected_only = click.option( cache_selected_only = click.option(
"--cache-selected-only/--no-cache-selected-only", "--cache-selected-only/--no-cache-selected-only",
envvar="DBT_CACHE_SELECTED_ONLY",
help="Pre cache database objects relevant to selected resource only.", help="Pre cache database objects relevant to selected resource only.",
default=False,
) )
compile_docs = click.option( compile_docs = click.option(
"--compile/--no-compile", "--compile/--no-compile",
envvar=None,
help="Wether or not to run 'dbt compile' as part of docs generation", help="Wether or not to run 'dbt compile' as part of docs generation",
default=True, default=True,
) )
compile_parse = click.option( compile_parse = click.option(
"--compile/--no-compile", "--compile/--no-compile",
envvar=None,
help="TODO: No help text currently available", help="TODO: No help text currently available",
default=True, default=True,
) )
config_dir = click.option( config_dir = click.option(
"--config-dir", "--config-dir",
envvar=None,
help="If specified, DBT will show path information for this project", help="If specified, DBT will show path information for this project",
type=click.STRING, type=click.STRING,
) )
@@ -60,68 +58,44 @@ config_dir = click.option(
debug = click.option( debug = click.option(
"--debug/--no-debug", "--debug/--no-debug",
"-d/ ", "-d/ ",
envvar="DBT_DEBUG",
help="Display debug logging during dbt execution. Useful for debugging and making bug reports.", help="Display debug logging during dbt execution. Useful for debugging and making bug reports.",
default=False,
) )
# TODO: The env var and name (reflected in flags) are corrections!
# The original name was `DEFER_MODE` and used an env var called "DBT_DEFER_TO_STATE"
# Both of which break existing naming conventions.
# This will need to be fixed before use in the main codebase and communicated as a change to the community!
defer = click.option( defer = click.option(
"--defer/--no-defer", "--defer/--no-defer",
envvar="DBT_DEFER",
help="If set, defer to the state variable for resolving unselected nodes.", help="If set, defer to the state variable for resolving unselected nodes.",
) default=True,
enable_legacy_logger = click.option(
"--enable-legacy-logger/--no-enable-legacy-logger",
envvar="DBT_ENABLE_LEGACY_LOGGER",
hidden=True,
) )
event_buffer_size = click.option( event_buffer_size = click.option(
"--event-buffer-size", "--event-buffer-size",
envvar="DBT_EVENT_BUFFER_SIZE",
help="Sets the max number of events to buffer in EVENT_HISTORY.", help="Sets the max number of events to buffer in EVENT_HISTORY.",
default=100000, default=100000,
type=click.INT, type=click.INT,
) )
exclude = click.option("--exclude", envvar=None, help="Specify the nodes to exclude.") exclude = click.option("--exclude", help="Specify the nodes to exclude.")
fail_fast = click.option( fail_fast = click.option(
"--fail-fast/--no-fail-fast", "--fail-fast/--no-fail-fast", "-x/ ", help="Stop execution on first failure.", default=False
"-x/ ",
envvar="DBT_FAIL_FAST",
help="Stop execution on first failure.",
) )
full_refresh = click.option( full_refresh = click.option(
"--full-refresh", "--full-refresh",
"-f",
envvar="DBT_FULL_REFRESH",
help="If specified, dbt will drop incremental models and fully-recalculate the incremental table from the model definition.", help="If specified, dbt will drop incremental models and fully-recalculate the incremental table from the model definition.",
is_flag=True, is_flag=True,
) )
indirect_selection = click.option( indirect_selection = click.option(
"--indirect-selection", "--indirect_selection",
envvar="DBT_INDIRECT_SELECTION",
help="Select all tests that are adjacent to selected resources, even if they those resources have been explicitly selected.", help="Select all tests that are adjacent to selected resources, even if they those resources have been explicitly selected.",
type=click.Choice(["eager", "cautious"], case_sensitive=False), type=click.Choice(["eager", "cautious"], case_sensitive=False),
default="eager", default="eager",
) )
log_cache_events = click.option(
"--log-cache-events/--no-log-cache-events",
help="Enable verbose adapter cache logging.",
envvar="DBT_LOG_CACHE_EVENTS",
)
log_format = click.option( log_format = click.option(
"--log-format", "--log-format",
envvar="DBT_LOG_FORMAT",
help="Specify the log format, overriding the command's default.", help="Specify the log format, overriding the command's default.",
type=click.Choice(["text", "json", "default"], case_sensitive=False), type=click.Choice(["text", "json", "default"], case_sensitive=False),
default="default", default="default",
@@ -129,42 +103,28 @@ log_format = click.option(
log_path = click.option( log_path = click.option(
"--log-path", "--log-path",
envvar="DBT_LOG_PATH",
help="Configure the 'log-path'. Only applies this setting for the current run. Overrides the 'DBT_LOG_PATH' if it is set.", help="Configure the 'log-path'. Only applies this setting for the current run. Overrides the 'DBT_LOG_PATH' if it is set.",
type=click.Path(), type=click.Path(),
) )
macro_debugging = click.option( models = click.option("-m", "-s", help="Specify the nodes to include.", multiple=True)
"--macro-debugging/--no-macro-debugging",
envvar="DBT_MACRO_DEBUGGING",
hidden=True,
)
models = click.option(
"-m",
"-s",
"models",
envvar=None,
help="Specify the nodes to include.",
multiple=True,
)
output = click.option( output = click.option(
"--output", "--output",
envvar=None,
help="TODO: No current help text", help="TODO: No current help text",
type=click.Choice(["json", "name", "path", "selector"], case_sensitive=False), type=click.Choice(["json", "name", "path", "selector"], case_sensitive=False),
default="name", default="name",
) )
output_keys = click.option( ouptut_keys = click.option(
"--output-keys", envvar=None, help="TODO: No current help text", type=click.STRING "--output-keys",
help="TODO: No current help text",
default=False,
) )
output_path = click.option( output_path = click.option(
"--output", "--output",
"-o", "-o",
envvar=None,
help="Specify the output path for the json report. By default, outputs to 'target/sources.json'", help="Specify the output path for the json report. By default, outputs to 'target/sources.json'",
type=click.Path(file_okay=True, dir_okay=False, writable=True), type=click.Path(file_okay=True, dir_okay=False, writable=True),
default=PurePath.joinpath(Path.cwd(), "target/sources.json"), default=PurePath.joinpath(Path.cwd(), "target/sources.json"),
@@ -172,84 +132,65 @@ output_path = click.option(
parse_only = click.option( parse_only = click.option(
"--parse-only", "--parse-only",
envvar=None,
help="TODO: No help text currently available", help="TODO: No help text currently available",
is_flag=True, is_flag=True,
) )
partial_parse = click.option( partial_parse = click.option(
"--partial-parse/--no-partial-parse", "--partial-parse/--no-partial-parse",
envvar="DBT_PARTIAL_PARSE",
help="Allow for partial parsing by looking for and writing to a pickle file in the target directory. This overrides the user configuration file.", help="Allow for partial parsing by looking for and writing to a pickle file in the target directory. This overrides the user configuration file.",
default=True, default=True,
) )
port = click.option( port = click.option(
"--port", "--port", help="Specify the port number for the docs server", default=8080, type=click.INT
envvar=None,
help="Specify the port number for the docs server",
default=8080,
type=click.INT,
) )
# TODO: The env var and name (reflected in flags) are corrections!
# The original name was `NO_PRINT` and used the env var `DBT_NO_PRINT`.
# Both of which break existing naming conventions.
# This will need to be fixed before use in the main codebase and communicated as a change to the community!
print = click.option( print = click.option(
"--print/--no-print", "--print/--no-print", help="Output all {{ print() }} macro calls.", default=True
envvar="DBT_PRINT",
help="Output all {{ print() }} macro calls.",
default=True,
) )
printer_width = click.option( printer_width = click.option(
"--printer-width", "--printer_width", help="Sets the width of terminal output", type=click.INT, default=80
envvar="DBT_PRINTER_WIDTH",
help="Sets the width of terminal output",
type=click.INT,
default=80,
) )
profile = click.option( profile = click.option(
"--profile", "--profile",
envvar=None,
help="Which profile to load. Overrides setting in dbt_project.yml.", help="Which profile to load. Overrides setting in dbt_project.yml.",
) )
profiles_dir = click.option( profiles_dir = click.option(
"--profiles-dir", "--profiles-dir",
envvar="DBT_PROFILES_DIR", help=f"Which directory to look in for the profiles.yml file. Default = {PurePath.joinpath(Path.home(), '.dbt')}",
help="Which directory to look in for the profiles.yml file. If not set, dbt will look in the current working directory first, then HOME/.dbt/", default=PurePath.joinpath(Path.home(), ".dbt"),
default=default_profiles_dir(), type=click.Path(
type=click.Path(exists=True), exists=True,
),
) )
project_dir = click.option( project_dir = click.option(
"--project-dir", "--project-dir",
envvar=None,
help="Which directory to look in for the dbt_project.yml file. Default is the current working directory and its parents.", help="Which directory to look in for the dbt_project.yml file. Default is the current working directory and its parents.",
default=default_project_dir(), default=Path.cwd(),
type=click.Path(exists=True), type=click.Path(exists=True),
) )
quiet = click.option( quiet = click.option(
"--quiet/--no-quiet", "--quiet/--no-quiet",
envvar="DBT_QUIET",
help="Suppress all non-error logging to stdout. Does not affect {{ print() }} macro calls.", help="Suppress all non-error logging to stdout. Does not affect {{ print() }} macro calls.",
default=False,
) )
record_timing_info = click.option( record_timing = click.option(
"--record-timing-info",
"-r", "-r",
envvar=None, "--record-timing-info",
help="When this option is passed, dbt will output low-level timing stats to the specified file. Example: `--record-timing-info output.profile`", help="When this option is passed, dbt will output low-level timing stats to the specified file. Example: `--record-timing-info output.profile`",
type=click.Path(exists=False), is_flag=True,
default=False,
) )
resource_type = click.option( resource_type = click.option(
"--resource-type", "--resource-type",
envvar=None,
help="TODO: No current help text", help="TODO: No current help text",
type=click.Choice( type=click.Choice(
[ [
@@ -269,63 +210,50 @@ resource_type = click.option(
default="default", default="default",
) )
selector = click.option( selector = click.option("--selector", help="The selector name to use, as defined in selectors.yml")
"--selector", envvar=None, help="The selector name to use, as defined in selectors.yml"
)
show = click.option( send_anonymous_usage_stats = click.option(
"--show", envvar=None, help="Show a sample of the loaded data in the terminal", is_flag=True "--anonymous-usage-stats/--no-anonymous-usage-stats",
) help="Send anonymous usage stats to dbt Labs.",
skip_profile_setup = click.option(
"--skip-profile-setup", "-s", envvar=None, help="Skip interative profile setup.", is_flag=True
)
# TODO: The env var and name (reflected in flags) are corrections!
# The original name was `ARTIFACT_STATE_PATH` and used the env var `DBT_ARTIFACT_STATE_PATH`.
# Both of which break existing naming conventions.
# This will need to be fixed before use in the main codebase and communicated as a change to the community!
state = click.option(
"--state",
envvar="DBT_STATE",
help="If set, use the given directory as the source for json files to compare with this project.",
type=click.Path(
dir_okay=True,
exists=True,
file_okay=False,
readable=True,
resolve_path=True,
),
)
static_parser = click.option(
"--static-parser/--no-static-parser",
envvar="DBT_STATIC_PARSER",
help="Use the static parser.",
default=True, default=True,
) )
store_failures = click.option( show = click.option(
"--store-failures", "--show",
envvar="DBT_STORE_FAILURES", help="Show a sample of the loaded data in the terminal",
help="Store test results (failing rows) in the database", default=False,
is_flag=True,
) )
target = click.option( skip_profile_setup = click.option(
"--target", "-t", envvar=None, help="Which target to load for the given profile" "--skip-profile-setup",
"-s",
help="Skip interative profile setup.",
default=False,
) )
state = click.option(
"--state",
help="If set, use the given directory as the source for json files to compare with this project.",
)
static_parser = click.option(
"--static-parser/--no-static-parser", help="Use the static parser.", default=True
)
store_failures = click.option(
"--store-failures", help="Store test results (failing rows) in the database", default=False
)
target = click.option("-t", "--target", help="Which target to load for the given profile")
target_path = click.option( target_path = click.option(
"--target-path", "--target-path",
envvar="DBT_TARGET_PATH",
help="Configure the 'target-path'. Only applies this setting for the current run. Overrides the 'DBT_TARGET_PATH' if it is set.", help="Configure the 'target-path'. Only applies this setting for the current run. Overrides the 'DBT_TARGET_PATH' if it is set.",
type=click.Path(), type=click.Path(),
) )
threads = click.option( threads = click.option(
"--threads", "--threads",
envvar=None,
help="Specify number of threads to use while executing models. Overrides settings in profiles.yml.", help="Specify number of threads to use while executing models. Overrides settings in profiles.yml.",
default=1, default=1,
type=click.INT, type=click.INT,
@@ -333,54 +261,44 @@ threads = click.option(
use_colors = click.option( use_colors = click.option(
"--use-colors/--no-use-colors", "--use-colors/--no-use-colors",
envvar="DBT_USE_COLORS",
help="Output is colorized by default and may also be set in a profile or at the command line.", help="Output is colorized by default and may also be set in a profile or at the command line.",
default=True, default=True,
) )
use_experimental_parser = click.option( use_experimental_parser = click.option(
"--use-experimental-parser/--no-use-experimental-parser", "--use-experimental-parser/--no-use-experimental-parser",
envvar="DBT_USE_EXPERIMENTAL_PARSER",
help="Enable experimental parsing features.", help="Enable experimental parsing features.",
default=False,
) )
vars = click.option( vars = click.option(
"--vars", "--vars",
envvar=None,
help="Supply variables to the project. This argument overrides variables defined in your dbt_project.yml file. This argument should be a YAML string, eg. '{my_variable: my_value}'", help="Supply variables to the project. This argument overrides variables defined in your dbt_project.yml file. This argument should be a YAML string, eg. '{my_variable: my_value}'",
type=YAML(), type=YAML(),
) )
version = click.option( version = click.option("--version", help="Show version information", is_flag=True, default=False)
"--version",
envvar=None,
help="Show version information",
is_flag=True,
)
version_check = click.option( version_check = click.option(
"--version-check/--no-version-check", "--version-check/--no-version-check",
envvar="DBT_VERSION_CHECK",
help="Ensure dbt's version matches the one specified in the dbt_project.yml file ('require-dbt-version')", help="Ensure dbt's version matches the one specified in the dbt_project.yml file ('require-dbt-version')",
default=True, default=True,
) )
warn_error = click.option( warn_error = click.option(
"--warn-error/--no-warn-error", "--warn-error/--no-warn-error",
envvar="DBT_WARN_ERROR",
help="If dbt would normally warn, instead raise an exception. Examples include --models that selects nothing, deprecations, configurations with no associated models, invalid test configurations, and missing sources/refs in tests.", help="If dbt would normally warn, instead raise an exception. Examples include --models that selects nothing, deprecations, configurations with no associated models, invalid test configurations, and missing sources/refs in tests.",
default=False,
) )
write_json = click.option( write_json = click.option(
"--write-json/--no-write-json", "--write-json/--no-write-json",
envvar="DBT_WRITE_JSON",
help="Writing the manifest and run_results.json files to disk", help="Writing the manifest and run_results.json files to disk",
default=True, default=True,
) )
write_manifest = click.option( write_manifest = click.option(
"--write-manifest/--no-write-manifest", "--write-manifest/--no-write-manifest",
envvar=None,
help="TODO: No help text currently available", help="TODO: No help text currently available",
default=True, default=True,
) )

View File

@@ -1,11 +0,0 @@
from pathlib import Path
def default_project_dir():
paths = list(Path.cwd().parents)
paths.insert(0, Path.cwd())
return next((x for x in paths if (x / "dbt_project.yml").exists()), Path.cwd())
def default_profiles_dir():
return Path.cwd() if (Path.cwd() / "profiles.yml").exists() else Path.home() / ".dbt"

View File

@@ -27,7 +27,6 @@ from dbt.utils import (
from dbt.clients._jinja_blocks import BlockIterator, BlockData, BlockTag from dbt.clients._jinja_blocks import BlockIterator, BlockData, BlockTag
from dbt.contracts.graph.compiled import CompiledGenericTestNode from dbt.contracts.graph.compiled import CompiledGenericTestNode
from dbt.contracts.graph.parsed import ParsedGenericTestNode from dbt.contracts.graph.parsed import ParsedGenericTestNode
from dbt.exceptions import ( from dbt.exceptions import (
InternalException, InternalException,
raise_compiler_error, raise_compiler_error,
@@ -306,13 +305,13 @@ class MacroGenerator(BaseMacroGenerator):
@contextmanager @contextmanager
def track_call(self): def track_call(self):
# This is only called from __call__ # This is only called from __call__
if self.stack is None: if self.stack is None or self.node is None:
yield yield
else: else:
unique_id = self.macro.unique_id unique_id = self.macro.unique_id
depth = self.stack.depth depth = self.stack.depth
# only mark depth=0 as a dependency, when creating this dependency we don't pass in stack # only mark depth=0 as a dependency
if depth == 0 and self.node: if depth == 0:
self.node.depends_on.add_macro(unique_id) self.node.depends_on.add_macro(unique_id)
self.stack.push(unique_id) self.stack.push(unique_id)
try: try:

View File

@@ -12,7 +12,6 @@ import tarfile
import requests import requests
import stat import stat
from typing import Type, NoReturn, List, Optional, Dict, Any, Tuple, Callable, Union from typing import Type, NoReturn, List, Optional, Dict, Any, Tuple, Callable, Union
from pathspec import PathSpec # type: ignore
from dbt.events.functions import fire_event from dbt.events.functions import fire_event
from dbt.events.types import ( from dbt.events.types import (
@@ -37,7 +36,6 @@ def find_matching(
root_path: str, root_path: str,
relative_paths_to_search: List[str], relative_paths_to_search: List[str],
file_pattern: str, file_pattern: str,
ignore_spec: Optional[PathSpec] = None,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
""" """
Given an absolute `root_path`, a list of relative paths to that Given an absolute `root_path`, a list of relative paths to that
@@ -59,30 +57,19 @@ def find_matching(
reobj = re.compile(regex, re.IGNORECASE) reobj = re.compile(regex, re.IGNORECASE)
for relative_path_to_search in relative_paths_to_search: for relative_path_to_search in relative_paths_to_search:
# potential speedup for ignore_spec
# if ignore_spec.matches(relative_path_to_search):
# continue
absolute_path_to_search = os.path.join(root_path, relative_path_to_search) absolute_path_to_search = os.path.join(root_path, relative_path_to_search)
walk_results = os.walk(absolute_path_to_search) walk_results = os.walk(absolute_path_to_search)
for current_path, subdirectories, local_files in walk_results: for current_path, subdirectories, local_files in walk_results:
# potential speedup for ignore_spec
# relative_dir = os.path.relpath(current_path, root_path) + os.sep
# if ignore_spec.match(relative_dir):
# continue
for local_file in local_files: for local_file in local_files:
absolute_path = os.path.join(current_path, local_file) absolute_path = os.path.join(current_path, local_file)
relative_path = os.path.relpath(absolute_path, absolute_path_to_search) relative_path = os.path.relpath(absolute_path, absolute_path_to_search)
relative_path_to_root = os.path.join(relative_path_to_search, relative_path)
modification_time = 0.0 modification_time = 0.0
try: try:
modification_time = os.path.getmtime(absolute_path) modification_time = os.path.getmtime(absolute_path)
except OSError: except OSError:
fire_event(SystemErrorRetrievingModTime(path=absolute_path)) fire_event(SystemErrorRetrievingModTime(path=absolute_path))
if reobj.match(local_file) and ( if reobj.match(local_file):
not ignore_spec or not ignore_spec.match_file(relative_path_to_root)
):
matching.append( matching.append(
{ {
"searched_path": relative_path_to_search, "searched_path": relative_path_to_search,
@@ -177,7 +164,7 @@ def write_file(path: str, contents: str = "") -> bool:
reason = "Path was possibly too long" reason = "Path was possibly too long"
# all our hard work and the path was still too long. Log and # all our hard work and the path was still too long. Log and
# continue. # continue.
fire_event(SystemCouldNotWrite(path=path, reason=reason, exc=str(exc))) fire_event(SystemCouldNotWrite(path=path, reason=reason, exc=exc))
else: else:
raise raise
return True return True

View File

@@ -183,7 +183,6 @@ class Compiler:
context = generate_runtime_model_context(node, self.config, manifest) context = generate_runtime_model_context(node, self.config, manifest)
context.update(extra_context) context.update(extra_context)
if isinstance(node, CompiledGenericTestNode): if isinstance(node, CompiledGenericTestNode):
# for test nodes, add a special keyword args value to the context # for test nodes, add a special keyword args value to the context
jinja.add_rendered_test_kwargs(context, node) jinja.add_rendered_test_kwargs(context, node)
@@ -370,6 +369,11 @@ class Compiler:
compiled_node = _compiled_type_for(node).from_dict(data) compiled_node = _compiled_type_for(node).from_dict(data)
if compiled_node.language == ModelLanguage.python: if compiled_node.language == ModelLanguage.python:
# TODO could we also 'minify' this code at all? just aesthetic, not functional
# quoating seems like something very specific to sql so far
# for all python implementations we are seeing there's no quating.
# TODO try to find better way to do this, given that
original_quoting = self.config.quoting original_quoting = self.config.quoting
self.config.quoting = {key: False for key in original_quoting.keys()} self.config.quoting = {key: False for key in original_quoting.keys()}
context = self._create_node_context(compiled_node, manifest, extra_context) context = self._create_node_context(compiled_node, manifest, extra_context)
@@ -380,19 +384,7 @@ class Compiler:
node, node,
) )
# we should NOT jinja render the python model's 'raw code' # we should NOT jinja render the python model's 'raw code'
compiled_node.compiled_code = f"{node.raw_code}\n\n{postfix}"
# if the user didn't specify an explicit `model(dbt, session)` function,
# we're going to treat the user code as a "script" and wrap it in that function now.
# TODO: this is the jankiest way of doing it, with zero AST magic
if node.meta.get("missing_model_function") is True:
raw_code_lines = node.raw_code.strip().split("\n")
raw_code_lines[-1] = f"return {raw_code_lines[-1]}"
raw_code_indented = "\n ".join(raw_code_lines)
model_code = f"def model(dbt, session):\n {raw_code_indented}"
else:
model_code = node.raw_code
compiled_node.compiled_code = f"{model_code}\n\n{postfix}"
# restore quoting settings in the end since context is lazy evaluated # restore quoting settings in the end since context is lazy evaluated
self.config.quoting = original_quoting self.config.quoting = original_quoting

View File

@@ -23,6 +23,8 @@ from .renderer import ProfileRenderer
DEFAULT_THREADS = 1 DEFAULT_THREADS = 1
DEFAULT_PROFILES_DIR = os.path.join(os.path.expanduser("~"), ".dbt")
INVALID_PROFILE_MESSAGE = """ INVALID_PROFILE_MESSAGE = """
dbt encountered an error while trying to read your profiles.yml file. dbt encountered an error while trying to read your profiles.yml file.
@@ -42,7 +44,7 @@ defined in your profiles.yml file. You can find profiles.yml here:
{profiles_file}/profiles.yml {profiles_file}/profiles.yml
""".format( """.format(
profiles_file=flags.DEFAULT_PROFILES_DIR profiles_file=DEFAULT_PROFILES_DIR
) )

View File

@@ -109,15 +109,9 @@ class MacroResolver:
def get_macro(self, local_package, macro_name): def get_macro(self, local_package, macro_name):
local_package_macros = {} local_package_macros = {}
# If the macro is explicitly prefixed with an internal namespace
# (e.g. 'dbt.some_macro'), look there first
if local_package in self.internal_package_names:
local_package_macros = self.internal_packages[local_package]
# If the macro is explicitly prefixed with a different package name
# (e.g. 'dbt_utils.some_macro'), look there first
if local_package not in self.internal_package_names and local_package in self.packages: if local_package not in self.internal_package_names and local_package in self.packages:
local_package_macros = self.packages[local_package] local_package_macros = self.packages[local_package]
# First: search the specified package for this macro # First: search the local packages for this macro
if macro_name in local_package_macros: if macro_name in local_package_macros:
return local_package_macros[macro_name] return local_package_macros[macro_name]
# Now look up in the standard search order # Now look up in the standard search order

View File

@@ -4,7 +4,6 @@ from dbt.clients.jinja import MacroStack
from dbt.contracts.connection import AdapterRequiredConfig from dbt.contracts.connection import AdapterRequiredConfig
from dbt.contracts.graph.manifest import Manifest from dbt.contracts.graph.manifest import Manifest
from dbt.context.macro_resolver import TestMacroNamespace from dbt.context.macro_resolver import TestMacroNamespace
from .base import contextproperty
from .configured import ConfiguredContext from .configured import ConfiguredContext
@@ -67,10 +66,6 @@ class ManifestContext(ConfiguredContext):
dct.update(self.namespace) dct.update(self.namespace)
return dct return dct
@contextproperty
def context_macro_stack(self):
return self.macro_stack
class QueryHeaderContext(ManifestContext): class QueryHeaderContext(ManifestContext):
def __init__(self, config: AdapterRequiredConfig, manifest: Manifest) -> None: def __init__(self, config: AdapterRequiredConfig, manifest: Manifest) -> None:

View File

@@ -41,7 +41,6 @@ from dbt.contracts.graph.parsed import (
ParsedSourceDefinition, ParsedSourceDefinition,
) )
from dbt.contracts.graph.metrics import MetricReference, ResolvedMetricReference from dbt.contracts.graph.metrics import MetricReference, ResolvedMetricReference
from dbt.contracts.util import get_metadata_env
from dbt.exceptions import ( from dbt.exceptions import (
CompilationException, CompilationException,
ParsingException, ParsingException,
@@ -711,10 +710,6 @@ class ProviderContext(ManifestContext):
self.model, self.model,
) )
@contextproperty
def dbt_metadata_envs(self) -> Dict[str, str]:
return get_metadata_env()
@contextproperty @contextproperty
def invocation_args_dict(self): def invocation_args_dict(self):
return args_to_dict(self.config.args) return args_to_dict(self.config.args)
@@ -1249,19 +1244,6 @@ class ProviderContext(ManifestContext):
""" """
return selected_resources.SELECTED_RESOURCES return selected_resources.SELECTED_RESOURCES
@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 (
self.context_macro_stack.depth == 2
and self.context_macro_stack.call_stack[1] == "macro.dbt.statement"
and "materialization" in self.context_macro_stack.call_stack[0]
):
raise RuntimeException(
f"submit_python_job is not intended to be called here, at model {parsed_model['alias']}, with macro call_stack {self.context_macro_stack.call_stack}."
)
return self.adapter.submit_python_job(parsed_model, compiled_code)
class MacroContext(ProviderContext): class MacroContext(ProviderContext):
"""Internally, macros can be executed like nodes, with some restrictions: """Internally, macros can be executed like nodes, with some restrictions:

View File

@@ -114,34 +114,25 @@ class FileHash(dbtClassMixin):
@dataclass @dataclass
class RemoteFile(dbtClassMixin): class RemoteFile(dbtClassMixin):
def __init__(self, language) -> None:
if language == "sql":
self.path_end = ".sql"
elif language == "python":
self.path_end = ".py"
else:
raise RuntimeError(f"Invalid language for remote File {language}")
self.path = f"from remote system{self.path_end}"
@property @property
def searched_path(self) -> str: def searched_path(self) -> str:
return self.path return "from remote system"
@property @property
def relative_path(self) -> str: def relative_path(self) -> str:
return self.path return "from remote system"
@property @property
def absolute_path(self) -> str: def absolute_path(self) -> str:
return self.path return "from remote system"
@property @property
def original_file_path(self): def original_file_path(self):
return self.path return "from remote system"
@property @property
def modification_time(self): def modification_time(self):
return self.path return "from remote system"
@dataclass @dataclass
@@ -211,9 +202,9 @@ class SourceFile(BaseSourceFile):
# TODO: do this a different way. This remote file kludge isn't going # TODO: do this a different way. This remote file kludge isn't going
# to work long term # to work long term
@classmethod @classmethod
def remote(cls, contents: str, project_name: str, language: str) -> "SourceFile": def remote(cls, contents: str, project_name: str) -> "SourceFile":
self = cls( self = cls(
path=RemoteFile(language), path=RemoteFile(),
checksum=FileHash.from_contents(contents), checksum=FileHash.from_contents(contents),
project_name=project_name, project_name=project_name,
contents=contents, contents=contents,
@@ -277,13 +268,11 @@ class SchemaSourceFile(BaseSourceFile):
self.tests[key][name] = [] self.tests[key][name] = []
self.tests[key][name].append(node_unique_id) self.tests[key][name].append(node_unique_id)
# this is only used in unit tests
def remove_tests(self, yaml_key, name): def remove_tests(self, yaml_key, name):
if yaml_key in self.tests: if yaml_key in self.tests:
if name in self.tests[yaml_key]: if name in self.tests[yaml_key]:
del self.tests[yaml_key][name] del self.tests[yaml_key][name]
# this is only used in tests (unit + functional)
def get_tests(self, yaml_key, name): def get_tests(self, yaml_key, name):
if yaml_key in self.tests: if yaml_key in self.tests:
if name in self.tests[yaml_key]: if name in self.tests[yaml_key]:

View File

@@ -33,7 +33,6 @@ from dbt.contracts.graph.parsed import (
ParsedMacro, ParsedMacro,
ParsedDocumentation, ParsedDocumentation,
ParsedSourceDefinition, ParsedSourceDefinition,
ParsedGenericTestNode,
ParsedExposure, ParsedExposure,
ParsedMetric, ParsedMetric,
HasUniqueID, HasUniqueID,
@@ -1113,13 +1112,8 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
def add_disabled(self, source_file: AnySourceFile, node: CompileResultNode, test_from=None): def add_disabled(self, source_file: AnySourceFile, node: CompileResultNode, test_from=None):
self.add_disabled_nofile(node) self.add_disabled_nofile(node)
if isinstance(source_file, SchemaSourceFile): if isinstance(source_file, SchemaSourceFile):
if isinstance(node, ParsedGenericTestNode):
assert test_from assert test_from
source_file.add_test(node.unique_id, test_from) source_file.add_test(node.unique_id, test_from)
if isinstance(node, ParsedMetric):
source_file.metrics.append(node.unique_id)
if isinstance(node, ParsedExposure):
source_file.exposures.append(node.unique_id)
else: else:
source_file.nodes.append(node.unique_id) source_file.nodes.append(node.unique_id)

View File

@@ -745,7 +745,6 @@ class ParsedExposure(UnparsedBaseNode, HasUniqueID, HasFqn):
owner: ExposureOwner owner: ExposureOwner
resource_type: NodeType = NodeType.Exposure resource_type: NodeType = NodeType.Exposure
description: str = "" description: str = ""
label: Optional[str] = None
maturity: Optional[MaturityType] = None maturity: Optional[MaturityType] = None
meta: Dict[str, Any] = field(default_factory=dict) meta: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list) tags: List[str] = field(default_factory=list)
@@ -771,9 +770,6 @@ class ParsedExposure(UnparsedBaseNode, HasUniqueID, HasFqn):
def same_description(self, old: "ParsedExposure") -> bool: def same_description(self, old: "ParsedExposure") -> bool:
return self.description == old.description return self.description == old.description
def same_label(self, old: "ParsedExposure") -> bool:
return self.label == old.label
def same_maturity(self, old: "ParsedExposure") -> bool: def same_maturity(self, old: "ParsedExposure") -> bool:
return self.maturity == old.maturity return self.maturity == old.maturity
@@ -805,7 +801,6 @@ class ParsedExposure(UnparsedBaseNode, HasUniqueID, HasFqn):
and self.same_maturity(old) and self.same_maturity(old)
and self.same_url(old) and self.same_url(old)
and self.same_description(old) and self.same_description(old)
and self.same_label(old)
and self.same_depends_on(old) and self.same_depends_on(old)
and self.same_config(old) and self.same_config(old)
and True and True
@@ -824,12 +819,13 @@ class ParsedMetric(UnparsedBaseNode, HasUniqueID, HasFqn):
description: str description: str
label: str label: str
calculation_method: str calculation_method: str
timestamp: str
expression: str expression: str
timestamp: Optional[str]
filters: List[MetricFilter] filters: List[MetricFilter]
time_grains: List[str] time_grains: List[str]
dimensions: List[str] dimensions: List[str]
window: Optional[MetricTime] = None window: Optional[MetricTime]
fill_missing_values: Optional[bool] = True
model: Optional[str] = None model: Optional[str] = None
model_unique_id: Optional[str] = None model_unique_id: Optional[str] = None
resource_type: NodeType = NodeType.Metric resource_type: NodeType = NodeType.Metric
@@ -857,6 +853,9 @@ class ParsedMetric(UnparsedBaseNode, HasUniqueID, HasFqn):
def same_window(self, old: "ParsedMetric") -> bool: def same_window(self, old: "ParsedMetric") -> bool:
return self.window == old.window return self.window == old.window
def same_fill_missing_values(self, old: "ParsedMetric") -> bool:
return self.fill_missing_values == old.fill_missing_values
def same_dimensions(self, old: "ParsedMetric") -> bool: def same_dimensions(self, old: "ParsedMetric") -> bool:
return self.dimensions == old.dimensions return self.dimensions == old.dimensions
@@ -896,6 +895,7 @@ class ParsedMetric(UnparsedBaseNode, HasUniqueID, HasFqn):
return ( return (
self.same_model(old) self.same_model(old)
and self.same_window(old) and self.same_window(old)
and self.same_fill_missing_values(old)
and self.same_dimensions(old) and self.same_dimensions(old)
and self.same_filters(old) and self.same_filters(old)
and self.same_description(old) and self.same_description(old)

View File

@@ -1,13 +1,5 @@
import re
from dbt import deprecations
from dbt.node_types import NodeType from dbt.node_types import NodeType
from dbt.contracts.util import ( from dbt.contracts.util import AdditionalPropertiesMixin, Mergeable, Replaceable
AdditionalPropertiesMixin,
Mergeable,
Replaceable,
rename_metric_attr,
)
# trigger the PathEncoder # trigger the PathEncoder
import dbt.helper_types # noqa:F401 import dbt.helper_types # noqa:F401
@@ -437,7 +429,6 @@ class UnparsedExposure(dbtClassMixin, Replaceable):
type: ExposureType type: ExposureType
owner: ExposureOwner owner: ExposureOwner
description: str = "" description: str = ""
label: Optional[str] = None
maturity: Optional[MaturityType] = None maturity: Optional[MaturityType] = None
meta: Dict[str, Any] = field(default_factory=dict) meta: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list) tags: List[str] = field(default_factory=list)
@@ -445,14 +436,6 @@ class UnparsedExposure(dbtClassMixin, Replaceable):
depends_on: List[str] = field(default_factory=list) depends_on: List[str] = field(default_factory=list)
config: Dict[str, Any] = field(default_factory=dict) config: Dict[str, Any] = field(default_factory=dict)
@classmethod
def validate(cls, data):
super(UnparsedExposure, cls).validate(data)
if "name" in data:
# name can only contain alphanumeric chars and underscores
if not (re.match(r"[\w-]+$", data["name"])):
deprecations.warn("exposure-name", exposure=data["name"])
@dataclass @dataclass
class MetricFilter(dbtClassMixin, Replaceable): class MetricFilter(dbtClassMixin, Replaceable):
@@ -487,11 +470,12 @@ class UnparsedMetric(dbtClassMixin, Replaceable):
label: str label: str
calculation_method: str calculation_method: str
timestamp: str timestamp: str
expression: str
description: str = "" description: str = ""
expression: Union[str, int] = ""
time_grains: List[str] = field(default_factory=list) time_grains: List[str] = field(default_factory=list)
dimensions: List[str] = field(default_factory=list) dimensions: List[str] = field(default_factory=list)
window: Optional[MetricTime] = None window: Optional[MetricTime] = None
fill_missing_values: Optional[bool] = True
model: Optional[str] = None model: Optional[str] = None
filters: List[MetricFilter] = field(default_factory=list) filters: List[MetricFilter] = field(default_factory=list)
meta: Dict[str, Any] = field(default_factory=dict) meta: Dict[str, Any] = field(default_factory=dict)
@@ -500,24 +484,13 @@ class UnparsedMetric(dbtClassMixin, Replaceable):
@classmethod @classmethod
def validate(cls, data): def validate(cls, data):
data = rename_metric_attr(data, raise_deprecation_warning=True)
super(UnparsedMetric, cls).validate(data) super(UnparsedMetric, cls).validate(data)
if "name" in data: if "name" in data and " " in data["name"]:
errors = [] raise ParsingException(f"Metrics name '{data['name']}' cannot contain spaces")
if " " in data["name"]:
errors.append("cannot contain spaces")
# This handles failing queries due to too long metric names.
# It only occurs in BigQuery and Snowflake (Postgres/Redshift truncate)
if len(data["name"]) > 250:
errors.append("cannot contain more than 250 characters")
if not (re.match(r"^[A-Za-z]", data["name"])):
errors.append("must begin with a letter")
if not (re.match(r"[\w-]+$", data["name"])):
errors.append("must contain only letters, numbers and underscores")
if errors: if data.get("calculation_method") == "expression":
raise ParsingException( raise ValidationError(
f"The metric name '{data['name']}' is invalid. It {', '.join(e for e in errors)}" "The metric calculation method expression has been deprecated and renamed to derived. Please update"
) )
if data.get("model") is None and data.get("calculation_method") != "derived": if data.get("model") is None and data.get("calculation_method") != "derived":

View File

@@ -236,7 +236,6 @@ class UserConfig(ExtensibleDbtClassMixin, Replaceable, UserConfigContract):
static_parser: Optional[bool] = None static_parser: Optional[bool] = None
indirect_selection: Optional[str] = None indirect_selection: Optional[str] = None
cache_selected_only: Optional[bool] = None cache_selected_only: Optional[bool] = None
event_buffer_size: Optional[int] = None
@dataclass @dataclass

View File

@@ -4,7 +4,6 @@ from datetime import datetime
from typing import List, Tuple, ClassVar, Type, TypeVar, Dict, Any, Optional from typing import List, Tuple, ClassVar, Type, TypeVar, Dict, Any, Optional
from dbt.clients.system import write_json, read_json from dbt.clients.system import write_json, read_json
from dbt import deprecations
from dbt.exceptions import ( from dbt.exceptions import (
InternalException, InternalException,
RuntimeException, RuntimeException,
@@ -208,60 +207,13 @@ def get_manifest_schema_version(dct: dict) -> int:
return int(schema_version.split(".")[-2][-1]) return int(schema_version.split(".")[-2][-1])
# we renamed these properties in v1.3 def upgrade_manifest_json(manifest: dict) -> dict:
# this method allows us to be nice to the early adopters for node_content in manifest.get("nodes", {}).values():
def rename_metric_attr(data: dict, raise_deprecation_warning: bool = False) -> dict:
metric_name = data["name"]
if raise_deprecation_warning and (
"sql" in data.keys()
or "type" in data.keys()
or data.get("calculation_method") == "expression"
):
deprecations.warn("metric-attr-renamed", metric_name=metric_name)
duplicated_attribute_msg = """\n
The metric '{}' contains both the deprecated metric property '{}'
and the up-to-date metric property '{}'. Please remove the deprecated property.
"""
if "sql" in data.keys():
if "expression" in data.keys():
raise ValidationError(
duplicated_attribute_msg.format(metric_name, "sql", "expression")
)
else:
data["expression"] = data.pop("sql")
if "type" in data.keys():
if "calculation_method" in data.keys():
raise ValidationError(
duplicated_attribute_msg.format(metric_name, "type", "calculation_method")
)
else:
calculation_method = data.pop("type")
data["calculation_method"] = calculation_method
# we also changed "type: expression" -> "calculation_method: derived"
if data.get("calculation_method") == "expression":
data["calculation_method"] = "derived"
return data
def rename_sql_attr(node_content: dict) -> dict:
if "raw_sql" in node_content: if "raw_sql" in node_content:
node_content["raw_code"] = node_content.pop("raw_sql") node_content["raw_code"] = node_content.pop("raw_sql")
if "compiled_sql" in node_content: if "compiled_sql" in node_content:
node_content["compiled_code"] = node_content.pop("compiled_sql") node_content["compiled_code"] = node_content.pop("compiled_sql")
node_content["language"] = "sql" node_content["language"] = "sql"
return node_content
def upgrade_manifest_json(manifest: dict) -> dict:
for node_content in manifest.get("nodes", {}).values():
node_content = rename_sql_attr(node_content)
for disabled in manifest.get("disabled", {}).values():
# There can be multiple disabled nodes for the same unique_id
# so make sure all the nodes get the attr renamed
disabled = [rename_sql_attr(n) for n in disabled]
for metric_content in manifest.get("metrics", {}).values():
# handle attr renames + value translation ("expression" -> "derived")
metric_content = rename_metric_attr(metric_content)
return manifest return manifest

View File

@@ -87,30 +87,6 @@ def renamed_method(old_name: str, new_name: str):
deprecations[dep.name] = dep deprecations[dep.name] = dep
class MetricAttributesRenamed(DBTDeprecation):
_name = "metric-attr-renamed"
_description = """\
dbt-core v1.3 renamed attributes for metrics:
\n 'sql' -> 'expression'
\n 'type' -> 'calculation_method'
\n 'type: expression' -> 'calculation_method: derived'
\nThe old metric parameter names will be fully deprecated in v1.4.
\nPlease remove them from the metric definition of metric '{metric_name}'
\nRelevant issue here: https://github.com/dbt-labs/dbt-core/issues/5849
"""
class ExposureNameDeprecation(DBTDeprecation):
_name = "exposure-name"
_description = """\
Starting in v1.3, the 'name' of an exposure should contain only letters, numbers, and underscores.
Exposures support a new property, 'label', which may contain spaces, capital letters, and special characters.
{exposure} does not follow this pattern.
Please update the 'name', and use the 'label' property for a human-friendly title.
This will raise an error in a future version of dbt-core.
"""
def warn(name, *args, **kwargs): def warn(name, *args, **kwargs):
if name not in deprecations: if name not in deprecations:
# this should (hopefully) never happen # this should (hopefully) never happen
@@ -125,12 +101,10 @@ def warn(name, *args, **kwargs):
active_deprecations: Set[str] = set() active_deprecations: Set[str] = set()
deprecations_list: List[DBTDeprecation] = [ deprecations_list: List[DBTDeprecation] = [
ExposureNameDeprecation(),
ConfigSourcePathDeprecation(), ConfigSourcePathDeprecation(),
ConfigDataPathDeprecation(), ConfigDataPathDeprecation(),
PackageInstallPathDeprecation(), PackageInstallPathDeprecation(),
PackageRedirectDeprecation(), PackageRedirectDeprecation(),
MetricAttributesRenamed(),
] ]
deprecations: Dict[str, DBTDeprecation] = {d.name: d for d in deprecations_list} deprecations: Dict[str, DBTDeprecation] = {d.name: d for d in deprecations_list}

View File

@@ -1,7 +1,7 @@
from colorama import Style from colorama import Style
import dbt.events.functions as this # don't worry I hate it too. import dbt.events.functions as this # don't worry I hate it too.
from dbt.events.base_types import NoStdOut, Event, NoFile, ShowException, Cache from dbt.events.base_types import NoStdOut, Event, NoFile, ShowException, Cache
from dbt.events.types import T_Event, MainReportVersion, EmptyLine, EventBufferFull from dbt.events.types import EventBufferFull, T_Event, MainReportVersion, EmptyLine
import dbt.flags as flags import dbt.flags as flags
from dbt.constants import SECRET_ENV_PREFIX from dbt.constants import SECRET_ENV_PREFIX
@@ -22,16 +22,24 @@ import threading
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
from collections import deque from collections import deque
global LOG_VERSION
LOG_VERSION = 2 LOG_VERSION = 2
EVENT_HISTORY = None
# create the global event history buffer with the default max size (10k)
# python 3.7 doesn't support type hints on globals, but mypy requires them. hence the ignore.
# TODO the flags module has not yet been resolved when this is created
global EVENT_HISTORY
EVENT_HISTORY = deque(maxlen=flags.EVENT_BUFFER_SIZE) # type: ignore
# create the global file logger with no configuration # create the global file logger with no configuration
global FILE_LOG
FILE_LOG = logging.getLogger("default_file") FILE_LOG = logging.getLogger("default_file")
null_handler = logging.NullHandler() null_handler = logging.NullHandler()
FILE_LOG.addHandler(null_handler) FILE_LOG.addHandler(null_handler)
# set up logger to go to stdout with defaults # set up logger to go to stdout with defaults
# setup_event_logger will be called once args have been parsed # setup_event_logger will be called once args have been parsed
global STDOUT_LOG
STDOUT_LOG = logging.getLogger("default_stdout") STDOUT_LOG = logging.getLogger("default_stdout")
STDOUT_LOG.setLevel(logging.INFO) STDOUT_LOG.setLevel(logging.INFO)
stdout_handler = logging.StreamHandler(sys.stdout) stdout_handler = logging.StreamHandler(sys.stdout)
@@ -44,6 +52,10 @@ invocation_id: Optional[str] = None
def setup_event_logger(log_path, level_override=None): def setup_event_logger(log_path, level_override=None):
# flags have been resolved, and log_path is known
global EVENT_HISTORY
EVENT_HISTORY = deque(maxlen=flags.EVENT_BUFFER_SIZE) # type: ignore
make_log_dir_if_missing(log_path) make_log_dir_if_missing(log_path)
this.format_json = flags.LOG_FORMAT == "json" this.format_json = flags.LOG_FORMAT == "json"
@@ -259,7 +271,14 @@ def fire_event(e: Event) -> None:
if isinstance(e, Cache) and not flags.LOG_CACHE_EVENTS: if isinstance(e, Cache) and not flags.LOG_CACHE_EVENTS:
return return
add_to_event_history(e) # if and only if the event history deque will be completely filled by this event
# fire warning that old events are now being dropped
global EVENT_HISTORY
if len(EVENT_HISTORY) == (flags.EVENT_BUFFER_SIZE - 1):
EVENT_HISTORY.append(e)
fire_event(EventBufferFull())
else:
EVENT_HISTORY.append(e)
# backwards compatibility for plugins that require old logger (dbt-rpc) # backwards compatibility for plugins that require old logger (dbt-rpc)
if flags.ENABLE_LEGACY_LOGGER: if flags.ENABLE_LEGACY_LOGGER:
@@ -325,20 +344,3 @@ def get_ts_rfc3339() -> str:
ts = get_ts() ts = get_ts()
ts_rfc3339 = ts.strftime("%Y-%m-%dT%H:%M:%S.%fZ") ts_rfc3339 = ts.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
return ts_rfc3339 return ts_rfc3339
def add_to_event_history(event):
if flags.EVENT_BUFFER_SIZE == 0:
return
global EVENT_HISTORY
if EVENT_HISTORY is None:
reset_event_history()
EVENT_HISTORY.append(event)
# We only set the EventBufferFull message for event buffers >= 10,000
if flags.EVENT_BUFFER_SIZE >= 10000 and len(EVENT_HISTORY) == (flags.EVENT_BUFFER_SIZE - 1):
fire_event(EventBufferFull())
def reset_event_history():
global EVENT_HISTORY
EVENT_HISTORY = deque(maxlen=flags.EVENT_BUFFER_SIZE)

View File

@@ -103,11 +103,11 @@ class MainKeyboardInterrupt(InfoLevel):
@dataclass @dataclass
class MainEncounteredError(ErrorLevel): class MainEncounteredError(ErrorLevel):
exc: str e: BaseException
code: str = "Z002" code: str = "Z002"
def message(self) -> str: def message(self) -> str:
return f"Encountered an error:\n{self.exc}" return f"Encountered an error:\n{self.e}"
@dataclass @dataclass
@@ -382,7 +382,7 @@ class SystemErrorRetrievingModTime(ErrorLevel):
class SystemCouldNotWrite(DebugLevel): class SystemCouldNotWrite(DebugLevel):
path: str path: str
reason: str reason: str
exc: str exc: Exception
code: str = "Z005" code: str = "Z005"
def message(self) -> str: def message(self) -> str:
@@ -762,7 +762,7 @@ class DumpAfterRenameSchema(DebugLevel, Cache):
@dataclass @dataclass
class AdapterImportError(InfoLevel): class AdapterImportError(InfoLevel):
exc: str exc: Exception
code: str = "E035" code: str = "E035"
def message(self) -> str: def message(self) -> str:
@@ -1008,7 +1008,7 @@ class PartialParsingNotEnabled(DebugLevel):
@dataclass @dataclass
class ParsedFileLoadFailed(ShowException, DebugLevel): class ParsedFileLoadFailed(ShowException, DebugLevel):
path: str path: str
exc: str exc: Exception
code: str = "I029" code: str = "I029"
def message(self) -> str: def message(self) -> str:
@@ -1223,7 +1223,7 @@ class InvalidRefInTestNode(DebugLevel):
@dataclass @dataclass
class RunningOperationCaughtError(ErrorLevel): class RunningOperationCaughtError(ErrorLevel):
exc: str exc: Exception
code: str = "Q001" code: str = "Q001"
def message(self) -> str: def message(self) -> str:
@@ -1232,7 +1232,7 @@ class RunningOperationCaughtError(ErrorLevel):
@dataclass @dataclass
class RunningOperationUncaughtError(ErrorLevel): class RunningOperationUncaughtError(ErrorLevel):
exc: str exc: Exception
code: str = "FF01" code: str = "FF01"
def message(self) -> str: def message(self) -> str:
@@ -1249,7 +1249,7 @@ class DbtProjectError(ErrorLevel):
@dataclass @dataclass
class DbtProjectErrorException(ErrorLevel): class DbtProjectErrorException(ErrorLevel):
exc: str exc: Exception
code: str = "A010" code: str = "A010"
def message(self) -> str: def message(self) -> str:
@@ -1266,7 +1266,7 @@ class DbtProfileError(ErrorLevel):
@dataclass @dataclass
class DbtProfileErrorException(ErrorLevel): class DbtProfileErrorException(ErrorLevel):
exc: str exc: Exception
code: str = "A012" code: str = "A012"
def message(self) -> str: def message(self) -> str:
@@ -1313,7 +1313,7 @@ https://docs.getdbt.com/docs/configure-your-profile
@dataclass @dataclass
class CatchableExceptionOnRun(ShowException, DebugLevel): class CatchableExceptionOnRun(ShowException, DebugLevel):
exc: str exc: Exception
code: str = "W002" code: str = "W002"
def message(self) -> str: def message(self) -> str:
@@ -1323,7 +1323,7 @@ class CatchableExceptionOnRun(ShowException, DebugLevel):
@dataclass @dataclass
class InternalExceptionOnRun(DebugLevel): class InternalExceptionOnRun(DebugLevel):
build_path: str build_path: str
exc: str exc: Exception
code: str = "W003" code: str = "W003"
def message(self) -> str: def message(self) -> str:
@@ -1352,7 +1352,7 @@ class PrintDebugStackTrace(ShowException, DebugLevel):
class GenericExceptionOnRun(ErrorLevel): class GenericExceptionOnRun(ErrorLevel):
build_path: Optional[str] build_path: Optional[str]
unique_id: str unique_id: str
exc: str exc: Exception
code: str = "W004" code: str = "W004"
def message(self) -> str: def message(self) -> str:
@@ -1366,7 +1366,7 @@ class GenericExceptionOnRun(ErrorLevel):
@dataclass @dataclass
class NodeConnectionReleaseError(ShowException, DebugLevel): class NodeConnectionReleaseError(ShowException, DebugLevel):
node_name: str node_name: str
exc: str exc: Exception
code: str = "W005" code: str = "W005"
def message(self) -> str: def message(self) -> str:
@@ -1700,7 +1700,7 @@ class SQLCompiledPath(InfoLevel):
@dataclass @dataclass
class SQlRunnerException(ShowException, DebugLevel): class SQlRunnerException(ShowException, DebugLevel):
exc: str exc: Exception
code: str = "Q006" code: str = "Q006"
def message(self) -> str: def message(self) -> str:
@@ -2458,7 +2458,7 @@ class GeneralWarningMsg(WarnLevel):
@dataclass @dataclass
class GeneralWarningException(WarnLevel): class GeneralWarningException(WarnLevel):
exc: str exc: Exception
log_fmt: str log_fmt: str
code: str = "Z047" code: str = "Z047"
@@ -2479,7 +2479,7 @@ class EventBufferFull(WarnLevel):
@dataclass @dataclass
class RecordRetryException(DebugLevel): class RecordRetryException(DebugLevel):
exc: str exc: Exception
code: str = "M021" code: str = "M021"
def message(self) -> str: def message(self) -> str:
@@ -2495,7 +2495,7 @@ class RecordRetryException(DebugLevel):
if 1 == 0: if 1 == 0:
MainReportVersion(v="") MainReportVersion(v="")
MainKeyboardInterrupt() MainKeyboardInterrupt()
MainEncounteredError(exc="") MainEncounteredError(e=BaseException(""))
MainStackTrace(stack_trace="") MainStackTrace(stack_trace="")
MainTrackingUserState(user_state="") MainTrackingUserState(user_state="")
ParsingStart() ParsingStart()
@@ -2524,7 +2524,7 @@ if 1 == 0:
RegistryResponseMissingNestedKeys(response=""), RegistryResponseMissingNestedKeys(response=""),
RegistryResponseExtraNestedKeys(response=""), RegistryResponseExtraNestedKeys(response=""),
SystemErrorRetrievingModTime(path="") SystemErrorRetrievingModTime(path="")
SystemCouldNotWrite(path="", reason="", exc="") SystemCouldNotWrite(path="", reason="", exc=Exception(""))
SystemExecutingCmd(cmd=[""]) SystemExecutingCmd(cmd=[""])
SystemStdOutMsg(bmsg=b"") SystemStdOutMsg(bmsg=b"")
SystemStdErrMsg(bmsg=b"") SystemStdErrMsg(bmsg=b"")
@@ -2580,7 +2580,7 @@ if 1 == 0:
DumpAfterAddGraph(Lazy.defer(lambda: dict())) DumpAfterAddGraph(Lazy.defer(lambda: dict()))
DumpBeforeRenameSchema(Lazy.defer(lambda: dict())) DumpBeforeRenameSchema(Lazy.defer(lambda: dict()))
DumpAfterRenameSchema(Lazy.defer(lambda: dict())) DumpAfterRenameSchema(Lazy.defer(lambda: dict()))
AdapterImportError(exc="") AdapterImportError(exc=Exception())
PluginLoadError() PluginLoadError()
SystemReportReturnCode(returncode=0) SystemReportReturnCode(returncode=0)
NewConnectionOpening(connection_state="") NewConnectionOpening(connection_state="")
@@ -2603,7 +2603,7 @@ if 1 == 0:
PartialParsingFailedBecauseNewProjectDependency() PartialParsingFailedBecauseNewProjectDependency()
PartialParsingFailedBecauseHashChanged() PartialParsingFailedBecauseHashChanged()
PartialParsingDeletedMetric(id="") PartialParsingDeletedMetric(id="")
ParsedFileLoadFailed(path="", exc="") ParsedFileLoadFailed(path="", exc=Exception(""))
PartialParseSaveFileNotFound() PartialParseSaveFileNotFound()
StaticParserCausedJinjaRendering(path="") StaticParserCausedJinjaRendering(path="")
UsingExperimentalParser(path="") UsingExperimentalParser(path="")
@@ -2626,20 +2626,20 @@ if 1 == 0:
PartialParsingDeletedExposure(unique_id="") PartialParsingDeletedExposure(unique_id="")
InvalidDisabledSourceInTestNode(msg="") InvalidDisabledSourceInTestNode(msg="")
InvalidRefInTestNode(msg="") InvalidRefInTestNode(msg="")
RunningOperationCaughtError(exc="") RunningOperationCaughtError(exc=Exception(""))
RunningOperationUncaughtError(exc="") RunningOperationUncaughtError(exc=Exception(""))
DbtProjectError() DbtProjectError()
DbtProjectErrorException(exc="") DbtProjectErrorException(exc=Exception(""))
DbtProfileError() DbtProfileError()
DbtProfileErrorException(exc="") DbtProfileErrorException(exc=Exception(""))
ProfileListTitle() ProfileListTitle()
ListSingleProfile(profile="") ListSingleProfile(profile="")
NoDefinedProfiles() NoDefinedProfiles()
ProfileHelpMessage() ProfileHelpMessage()
CatchableExceptionOnRun(exc="") CatchableExceptionOnRun(exc=Exception(""))
InternalExceptionOnRun(build_path="", exc="") InternalExceptionOnRun(build_path="", exc=Exception(""))
GenericExceptionOnRun(build_path="", unique_id="", exc="") GenericExceptionOnRun(build_path="", unique_id="", exc=Exception(""))
NodeConnectionReleaseError(node_name="", exc="") NodeConnectionReleaseError(node_name="", exc=Exception(""))
CheckCleanPath(path="") CheckCleanPath(path="")
ConfirmCleanPath(path="") ConfirmCleanPath(path="")
ProtectedCleanPath(path="") ProtectedCleanPath(path="")
@@ -2847,6 +2847,6 @@ if 1 == 0:
TrackingInitializeFailure() TrackingInitializeFailure()
RetryExternalCall(attempt=0, max=0) RetryExternalCall(attempt=0, max=0)
GeneralWarningMsg(msg="", log_fmt="") GeneralWarningMsg(msg="", log_fmt="")
GeneralWarningException(exc="", log_fmt="") GeneralWarningException(exc=Exception(""), log_fmt="")
EventBufferFull() EventBufferFull()
RecordRetryException(exc="") RecordRetryException(exc=Exception(""))

View File

@@ -1098,7 +1098,7 @@ def warn_or_raise(exc, log_fmt=None):
if flags.WARN_ERROR: if flags.WARN_ERROR:
raise exc raise exc
else: else:
fire_event(GeneralWarningException(exc=str(exc), log_fmt=log_fmt)) fire_event(GeneralWarningException(exc=exc, log_fmt=log_fmt))
def warn(msg, node=None): def warn(msg, node=None):

View File

@@ -10,13 +10,7 @@ from typing import Optional
# PROFILES_DIR must be set before the other flags # PROFILES_DIR must be set before the other flags
# It also gets set in main.py and in set_from_args because the rpc server # It also gets set in main.py and in set_from_args because the rpc server
# doesn't go through exactly the same main arg processing. # doesn't go through exactly the same main arg processing.
GLOBAL_PROFILES_DIR = os.path.join(os.path.expanduser("~"), ".dbt") DEFAULT_PROFILES_DIR = os.path.join(os.path.expanduser("~"), ".dbt")
LOCAL_PROFILES_DIR = os.getcwd()
# Use the current working directory if there is a profiles.yml file present there
if os.path.exists(Path(LOCAL_PROFILES_DIR) / Path("profiles.yml")):
DEFAULT_PROFILES_DIR = LOCAL_PROFILES_DIR
else:
DEFAULT_PROFILES_DIR = GLOBAL_PROFILES_DIR
PROFILES_DIR = os.path.expanduser(os.getenv("DBT_PROFILES_DIR", DEFAULT_PROFILES_DIR)) PROFILES_DIR = os.path.expanduser(os.getenv("DBT_PROFILES_DIR", DEFAULT_PROFILES_DIR))
STRICT_MODE = False # Only here for backwards compatibility STRICT_MODE = False # Only here for backwards compatibility
@@ -58,7 +52,6 @@ _NON_BOOLEAN_FLAGS = [
_NON_DBT_ENV_FLAGS = ["DO_NOT_TRACK"] _NON_DBT_ENV_FLAGS = ["DO_NOT_TRACK"]
# Global CLI defaults. These flags are set from three places: # Global CLI defaults. These flags are set from three places:
# CLI args, environment variables, and user_config (profiles.yml). # CLI args, environment variables, and user_config (profiles.yml).
# Environment variables use the pattern 'DBT_{flag name}', like DBT_PROFILES_DIR # Environment variables use the pattern 'DBT_{flag name}', like DBT_PROFILES_DIR

View File

@@ -356,20 +356,7 @@ class ConfigSelectorMethod(SelectorMethod):
except AttributeError: except AttributeError:
continue continue
else: else:
if isinstance(value, list): if selector == value:
if (
(selector in value)
or (CaseInsensitive(selector) == "true" and True in value)
or (CaseInsensitive(selector) == "false" and False in value)
):
yield node
else:
if (
(selector == value)
or (CaseInsensitive(selector) == "true" and value is True)
or (CaseInsensitive(selector) == "false")
and value is False
):
yield node yield node

View File

@@ -12,7 +12,7 @@ The macro override naming method (spark__statement) only works for macros which
{%- if language == 'sql'-%} {%- if language == 'sql'-%}
{%- set res, table = adapter.execute(compiled_code, auto_begin=auto_begin, fetch=fetch_result) -%} {%- set res, table = adapter.execute(compiled_code, auto_begin=auto_begin, fetch=fetch_result) -%}
{%- elif language == 'python' -%} {%- elif language == 'python' -%}
{%- set res = submit_python_job(model, compiled_code) -%} {%- set res = adapter.submit_python_job(model, compiled_code) -%}
{#-- TODO: What should table be for python models? --#} {#-- TODO: What should table be for python models? --#}
{%- set table = None -%} {%- set table = None -%}
{%- else -%} {%- else -%}

View File

@@ -50,31 +50,3 @@
{{ return(result) }} {{ return(result) }}
{% endmacro %} {% endmacro %}
{% macro get_merge_update_columns(merge_update_columns, merge_exclude_columns, dest_columns) %}
{{ return(adapter.dispatch('get_merge_update_columns', 'dbt')(merge_update_columns, merge_exclude_columns, dest_columns)) }}
{% endmacro %}
{% macro default__get_merge_update_columns(merge_update_columns, merge_exclude_columns, dest_columns) %}
{%- set default_cols = dest_columns | map(attribute="quoted") | list -%}
{%- if merge_update_columns and merge_exclude_columns -%}
{{ exceptions.raise_compiler_error(
'Model cannot specify merge_update_columns and merge_exclude_columns. Please update model to use only one config'
)}}
{%- elif merge_update_columns -%}
{%- set update_columns = merge_update_columns -%}
{%- elif merge_exclude_columns -%}
{%- set update_columns = [] -%}
{%- for column in dest_columns -%}
{% if column.column | lower not in merge_exclude_columns | map("lower") | list %}
{%- do update_columns.append(column.quoted) -%}
{% endif %}
{%- endfor -%}
{%- else -%}
{%- set update_columns = default_cols -%}
{%- endif -%}
{{ return(update_columns) }}
{% endmacro %}

View File

@@ -5,9 +5,7 @@
{% macro default__get_merge_sql(target, source, unique_key, dest_columns, predicates) -%} {% macro default__get_merge_sql(target, source, unique_key, dest_columns, predicates) -%}
{%- set predicates = [] if predicates is none else [] + predicates -%} {%- set predicates = [] if predicates is none else [] + predicates -%}
{%- set dest_cols_csv = get_quoted_csv(dest_columns | map(attribute="name")) -%} {%- set dest_cols_csv = get_quoted_csv(dest_columns | map(attribute="name")) -%}
{%- set merge_update_columns = config.get('merge_update_columns') -%} {%- set update_columns = config.get('merge_update_columns', default = dest_columns | map(attribute="quoted") | list) -%}
{%- set merge_exclude_columns = config.get('merge_exclude_columns') -%}
{%- set update_columns = get_merge_update_columns(merge_update_columns, merge_exclude_columns, dest_columns) -%}
{%- set sql_header = config.get('sql_header', none) -%} {%- set sql_header = config.get('sql_header', none) -%}
{% if unique_key %} {% if unique_key %}

View File

@@ -30,8 +30,8 @@ def source(*args, dbt_load_df_function):
{% macro build_config_dict(model) %} {% macro build_config_dict(model) %}
{%- set config_dict = {} -%} {%- set config_dict = {} -%}
{%- for key in model.config.config_keys_used -%} {%- for key in model.config.utilized -%}
{# weird type testing with enum, would be much easier to write this logic in Python! #} {# TODO: weird type testing with enum, would be much easier to write this logic in Python! #}
{%- if key == 'language' -%} {%- if key == 'language' -%}
{%- set value = 'python' -%} {%- set value = 'python' -%}
{%- endif -%} {%- endif -%}
@@ -56,8 +56,8 @@ class config:
pass pass
@staticmethod @staticmethod
def get(key, default=None): def get(key):
return config_dict.get(key, default) return config_dict.get(key)
class this: class this:
"""dbt.this() or dbt.this.identifier""" """dbt.this() or dbt.this.identifier"""

View File

@@ -1,8 +0,0 @@
{% macro array_append(array, new_element) -%}
{{ return(adapter.dispatch('array_append', 'dbt')(array, new_element)) }}
{%- endmacro %}
{# new_element must be the same data type as elements in array to match postgres functionality #}
{% macro default__array_append(array, new_element) -%}
array_append({{ array }}, {{ new_element }})
{%- endmacro %}

View File

@@ -1,7 +0,0 @@
{% macro array_concat(array_1, array_2) -%}
{{ return(adapter.dispatch('array_concat', 'dbt')(array_1, array_2)) }}
{%- endmacro %}
{% macro default__array_concat(array_1, array_2) -%}
array_cat({{ array_1 }}, {{ array_2 }})
{%- endmacro %}

View File

@@ -1,12 +0,0 @@
{% macro array_construct(inputs=[], data_type=api.Column.translate_type('integer')) -%}
{{ return(adapter.dispatch('array_construct', 'dbt')(inputs, data_type)) }}
{%- endmacro %}
{# all inputs must be the same data type to match postgres functionality #}
{% macro default__array_construct(inputs, data_type) -%}
{% if inputs|length > 0 %}
array[ {{ inputs|join(' , ') }} ]
{% else %}
array[]::{{data_type}}[]
{% endif %}
{%- endmacro %}

View File

@@ -59,7 +59,7 @@ The TIMESTAMP_* variation associated with TIMESTAMP is specified by the TIMESTAM
{{ return(api.Column.translate_type("float")) }} {{ return(api.Column.translate_type("float")) }}
{% endmacro %} {% endmacro %}
{# numeric ------------------------------------------------- #} {# numeric ------------------------------------------------ #}
{%- macro type_numeric() -%} {%- macro type_numeric() -%}
{{ return(adapter.dispatch('type_numeric', 'dbt')()) }} {{ return(adapter.dispatch('type_numeric', 'dbt')()) }}
@@ -115,15 +115,3 @@ the precision and scale explicitly.)
-- returns 'int' everywhere, except BigQuery, where it returns 'int64' -- returns 'int' everywhere, except BigQuery, where it returns 'int64'
-- (but BigQuery also now accepts 'int' as a valid alias for 'int64') -- (but BigQuery also now accepts 'int' as a valid alias for 'int64')
{# bool ------------------------------------------------- #}
{%- macro type_boolean() -%}
{{ return(adapter.dispatch('type_boolean', 'dbt')()) }}
{%- endmacro -%}
{%- macro default__type_boolean() -%}
{{ return(api.Column.translate_type("boolean")) }}
{%- endmacro -%}
-- returns 'boolean' everywhere. BigQuery accepts 'boolean' as a valid alias for 'bool'

File diff suppressed because one or more lines are too long

View File

@@ -1,64 +1,10 @@
# TODO: this file is one big TODO
import os import os
from dbt.contracts.results import RunningStatus, collect_timing_info
from dbt.events.functions import fire_event
from dbt.events.types import NodeCompiling, NodeExecuting
from dbt.exceptions import RuntimeException from dbt.exceptions import RuntimeException
from dbt import flags from dbt import flags
from dbt.task.sql import SqlCompileRunner from collections import namedtuple
from dataclasses import dataclass
RuntimeArgs = namedtuple("RuntimeArgs", "project_dir profiles_dir single_threaded profile target")
@dataclass
class RuntimeArgs:
project_dir: str
profiles_dir: str
single_threaded: bool
profile: str
target: str
class SqlCompileRunnerNoIntrospection(SqlCompileRunner):
def compile_and_execute(self, manifest, ctx):
"""
This version of this method does not connect to the data warehouse.
As a result, introspective queries at compilation will not be supported
and will throw an error.
TODO: This is a temporary solution to more complex permissions requirements
for the semantic layer, and thus largely duplicates the code in the parent class
method. Once conditional credential usage is enabled, this should be removed.
"""
result = None
ctx.node._event_status["node_status"] = RunningStatus.Compiling
fire_event(
NodeCompiling(
node_info=ctx.node.node_info,
unique_id=ctx.node.unique_id,
)
)
with collect_timing_info("compile") as timing_info:
# if we fail here, we still have a compiled node to return
# this has the benefit of showing a build path for the errant
# model
ctx.node = self.compile(manifest)
ctx.timing.append(timing_info)
# for ephemeral nodes, we only want to compile, not run
if not ctx.node.is_ephemeral_model:
ctx.node._event_status["node_status"] = RunningStatus.Executing
fire_event(
NodeExecuting(
node_info=ctx.node.node_info,
unique_id=ctx.node.unique_id,
)
)
with collect_timing_info("execute") as timing_info:
result = self.run(ctx.node, manifest)
ctx.node = result.node
ctx.timing.append(timing_info)
return result
def get_dbt_config(project_dir, args=None, single_threaded=False): def get_dbt_config(project_dir, args=None, single_threaded=False):
@@ -69,32 +15,29 @@ def get_dbt_config(project_dir, args=None, single_threaded=False):
if os.getenv("DBT_PROFILES_DIR"): if os.getenv("DBT_PROFILES_DIR"):
profiles_dir = os.getenv("DBT_PROFILES_DIR") profiles_dir = os.getenv("DBT_PROFILES_DIR")
else: else:
profiles_dir = flags.DEFAULT_PROFILES_DIR profiles_dir = os.path.expanduser("~/.dbt")
runtime_args = RuntimeArgs( profile = args.profile if hasattr(args, "profile") else None
project_dir=project_dir, target = args.target if hasattr(args, "target") else None
profiles_dir=profiles_dir,
single_threaded=single_threaded, # Construct a phony config
profile=getattr(args, "profile", None), config = RuntimeConfig.from_args(
target=getattr(args, "target", None), RuntimeArgs(project_dir, profiles_dir, single_threaded, profile, target)
) )
# Clear previously registered adapters--
# Construct a RuntimeConfig from phony args # this fixes cacheing behavior on the dbt-server
config = RuntimeConfig.from_args(runtime_args)
# Set global flags from arguments
flags.set_from_args(args, config) flags.set_from_args(args, config)
dbt.adapters.factory.reset_adapters()
# This is idempotent, so we can call it repeatedly # Load the relevant adapter
dbt.adapters.factory.register_adapter(config) dbt.adapters.factory.register_adapter(config)
# Set invocation id
# Make sure we have a valid invocation_id
dbt.events.functions.set_invocation_id() dbt.events.functions.set_invocation_id()
return config return config
def get_task_by_type(type): def get_task_by_type(type):
# TODO: we need to tell dbt-server what tasks are available
from dbt.task.run import RunTask from dbt.task.run import RunTask
from dbt.task.list import ListTask from dbt.task.list import ListTask
from dbt.task.seed import SeedTask from dbt.task.seed import SeedTask
@@ -127,13 +70,16 @@ def create_task(type, args, manifest, config):
def no_op(*args, **kwargs): def no_op(*args, **kwargs):
pass pass
# TODO: yuck, let's rethink tasks a little
task = task(args, config) task = task(args, config)
# Wow! We can monkeypatch taskCls.load_manifest to return _our_ manifest
task.load_manifest = no_op task.load_manifest = no_op
task.manifest = manifest task.manifest = manifest
return task return task
def _get_operation_node(manifest, project_path, sql, node_name): def _get_operation_node(manifest, project_path, sql):
from dbt.parser.manifest import process_node from dbt.parser.manifest import process_node
from dbt.parser.sql import SqlBlockParser from dbt.parser.sql import SqlBlockParser
import dbt.adapters.factory import dbt.adapters.factory
@@ -146,33 +92,26 @@ def _get_operation_node(manifest, project_path, sql, node_name):
) )
adapter = dbt.adapters.factory.get_adapter(config) adapter = dbt.adapters.factory.get_adapter(config)
sql_node = block_parser.parse_remote(sql, node_name) # TODO : This needs a real name?
sql_node = block_parser.parse_remote(sql, "name")
process_node(config, manifest, sql_node) process_node(config, manifest, sql_node)
return config, sql_node, adapter return config, sql_node, adapter
def compile_sql(manifest, project_path, sql, node_name="query"): def compile_sql(manifest, project_path, sql):
config, node, adapter = _get_operation_node(manifest, project_path, sql, node_name) from dbt.task.sql import SqlCompileRunner
allow_introspection = str(os.environ.get("__DBT_ALLOW_INTROSPECTION", "1")).lower() in (
"true",
"1",
"on",
)
if allow_introspection: config, node, adapter = _get_operation_node(manifest, project_path, sql)
runner = SqlCompileRunner(config, adapter, node, 1, 1) runner = SqlCompileRunner(config, adapter, node, 1, 1)
else:
runner = SqlCompileRunnerNoIntrospection(config, adapter, node, 1, 1)
return runner.safe_run(manifest) return runner.safe_run(manifest)
def execute_sql(manifest, project_path, sql, node_name="query"): def execute_sql(manifest, project_path, sql):
from dbt.task.sql import SqlExecuteRunner from dbt.task.sql import SqlExecuteRunner
config, node, adapter = _get_operation_node(manifest, project_path, sql, node_name) config, node, adapter = _get_operation_node(manifest, project_path, sql)
runner = SqlExecuteRunner(config, adapter, node, 1, 1) runner = SqlExecuteRunner(config, adapter, node, 1, 1)
# TODO: use same interface for runner
return runner.safe_run(manifest) return runner.safe_run(manifest)
@@ -189,4 +128,5 @@ def deserialize_manifest(manifest_msgpack):
def serialize_manifest(manifest): def serialize_manifest(manifest):
# TODO: what should this take as an arg?
return manifest.to_msgpack() return manifest.to_msgpack()

View File

@@ -1,5 +1,4 @@
from typing import List from typing import List
from dbt.logger import log_cache_events, log_manager from dbt.logger import log_cache_events, log_manager
import argparse import argparse
@@ -43,13 +42,8 @@ from dbt.adapters.factory import reset_adapters, cleanup_connections
import dbt.tracking import dbt.tracking
from dbt.utils import ExitCodes, args_to_dict from dbt.utils import ExitCodes, args_to_dict
from dbt.config.profile import read_user_config from dbt.config.profile import DEFAULT_PROFILES_DIR, read_user_config
from dbt.exceptions import ( from dbt.exceptions import InternalException, NotImplementedException, FailedToConnectException
Exception as dbtException,
InternalException,
NotImplementedException,
FailedToConnectException,
)
class DBTVersion(argparse.Action): class DBTVersion(argparse.Action):
@@ -148,8 +142,7 @@ def main(args=None):
exit_code = e.code exit_code = e.code
except BaseException as e: except BaseException as e:
fire_event(MainEncounteredError(exc=str(e))) fire_event(MainEncounteredError(e=str(e)))
if not isinstance(e, dbtException):
fire_event(MainStackTrace(stack_trace=traceback.format_exc())) fire_event(MainStackTrace(stack_trace=traceback.format_exc()))
exit_code = ExitCodes.UnhandledError.value exit_code = ExitCodes.UnhandledError.value
@@ -208,7 +201,7 @@ def track_run(task):
yield yield
dbt.tracking.track_invocation_end(config=task.config, args=task.args, result_type="ok") dbt.tracking.track_invocation_end(config=task.config, args=task.args, result_type="ok")
except (NotImplementedException, FailedToConnectException) as e: except (NotImplementedException, FailedToConnectException) as e:
fire_event(MainEncounteredError(exc=str(e))) fire_event(MainEncounteredError(e=str(e)))
dbt.tracking.track_invocation_end(config=task.config, args=task.args, result_type="error") dbt.tracking.track_invocation_end(config=task.config, args=task.args, result_type="error")
except Exception: except Exception:
dbt.tracking.track_invocation_end(config=task.config, args=task.args, result_type="error") dbt.tracking.track_invocation_end(config=task.config, args=task.args, result_type="error")
@@ -265,8 +258,10 @@ def _build_base_subparser():
dest="sub_profiles_dir", # Main cli arg precedes subcommand dest="sub_profiles_dir", # Main cli arg precedes subcommand
type=str, type=str,
help=""" help="""
Which directory to look in for the profiles.yml file. If not set, dbt will look in the current working directory first, then HOME/.dbt/ Which directory to look in for the profiles.yml file. Default = {}
""", """.format(
DEFAULT_PROFILES_DIR
),
) )
base_subparser.add_argument( base_subparser.add_argument(
@@ -625,7 +620,6 @@ def _add_table_mutability_arguments(*subparsers):
for sub in subparsers: for sub in subparsers:
sub.add_argument( sub.add_argument(
"--full-refresh", "--full-refresh",
"-f",
action="store_true", action="store_true",
help=""" help="""
If specified, dbt will drop incremental models and If specified, dbt will drop incremental models and
@@ -1065,8 +1059,10 @@ def parse_args(args, cls=DBTArgumentParser):
dest="profiles_dir", dest="profiles_dir",
type=str, type=str,
help=""" help="""
Which directory to look in for the profiles.yml file. If not set, dbt will look in the current working directory first, then HOME/.dbt/ Which directory to look in for the profiles.yml file. Default = {}
""", """.format(
DEFAULT_PROFILES_DIR
),
) )
p.add_argument( p.add_argument(

View File

@@ -20,7 +20,9 @@ class MacroParser(BaseParser[ParsedMacro]):
# from the normal parsing flow. # from the normal parsing flow.
def get_paths(self) -> List[FilePath]: def get_paths(self) -> List[FilePath]:
return filesystem_search( return filesystem_search(
project=self.project, relative_dirs=self.project.macro_paths, extension=".sql" project=self.project,
relative_dirs=self.project.macro_paths,
extension=".sql",
) )
@property @property

View File

@@ -660,7 +660,7 @@ class ManifestLoader:
manifest.metadata.invocation_id = get_invocation_id() manifest.metadata.invocation_id = get_invocation_id()
return manifest return manifest
except Exception as exc: except Exception as exc:
fire_event(ParsedFileLoadFailed(path=path, exc=str(exc))) fire_event(ParsedFileLoadFailed(path=path, exc=exc))
reparse_reason = ReparseReason.load_file_failure reparse_reason = ReparseReason.load_file_failure
else: else:
fire_event(PartialParseSaveFileNotFound()) fire_event(PartialParseSaveFileNotFound())

View File

@@ -60,8 +60,8 @@ class PythonValidationVisitor(ast.NodeVisitor):
) )
def check_error(self, node): def check_error(self, node):
if self.num_model_def > 1: if self.num_model_def != 1:
raise ParsingException("dbt only allows one model defined per python file", node=node) raise ParsingException("dbt only allow one model defined per python file", node=node)
if len(self.dbt_errors) != 0: if len(self.dbt_errors) != 0:
raise ParsingException("\n".join(self.dbt_errors), node=node) raise ParsingException("\n".join(self.dbt_errors), node=node)
@@ -86,12 +86,11 @@ class PythonParseVisitor(ast.NodeVisitor):
def _safe_eval(self, node): def _safe_eval(self, node):
try: try:
return ast.literal_eval(node) return ast.literal_eval(node)
except (SyntaxError, ValueError, TypeError, MemoryError, RecursionError) as exc: except (SyntaxError, ValueError, TypeError) as exc:
msg = validator_error_message( msg = validator_error_message(exc)
f"Error when trying to literal_eval an arg to dbt.ref(), dbt.source(), dbt.config() or dbt.config.get() \n{exc}\n" raise ParsingException(msg, node=self.dbt_node) from exc
"https://docs.python.org/3/library/ast.html#ast.literal_eval\n" except (MemoryError, RecursionError) as exc:
"In dbt python model, `dbt.ref`, `dbt.source`, `dbt.config`, `dbt.config.get` function args only support Python literal structures" msg = validator_error_message(exc)
)
raise ParsingException(msg, node=self.dbt_node) from exc raise ParsingException(msg, node=self.dbt_node) from exc
def _get_call_literals(self, node): def _get_call_literals(self, node):
@@ -113,7 +112,7 @@ class PythonParseVisitor(ast.NodeVisitor):
return arg_literals, kwarg_literals return arg_literals, kwarg_literals
def visit_Call(self, node: ast.Call) -> None: def visit_Call(self, node: ast.Call) -> None:
# check whether the current call could be a dbt function call # check weather the current call could be a dbt function call
if isinstance(node.func, ast.Attribute) and node.func.attr in dbt_function_key_words: if isinstance(node.func, ast.Attribute) and node.func.attr in dbt_function_key_words:
func_name = self._flatten_attr(node.func) func_name = self._flatten_attr(node.func)
# check weather the current call really is a dbt function call # check weather the current call really is a dbt function call
@@ -204,25 +203,18 @@ class ModelParser(SimpleSQLParser[ParsedModelNode]):
dbtValidator.visit(tree) dbtValidator.visit(tree)
dbtValidator.check_error(node) dbtValidator.check_error(node)
# if the user didn't specify an explicit `model(dbt, session)` function,
# we're going to treat the user code as a "script" to be wrapped in that function at compile time.
# for now, we just need to recognize that fact, and save it to the node.
if dbtValidator.num_model_def == 0:
# TODO: this is silly, put this somewhere better (outside of user space)
node.meta["missing_model_function"] = True
dbtParser = PythonParseVisitor(node) dbtParser = PythonParseVisitor(node)
dbtParser.visit(tree) dbtParser.visit(tree)
config_keys_used = []
for (func, args, kwargs) in dbtParser.dbt_function_calls: for (func, args, kwargs) in dbtParser.dbt_function_calls:
# TODO decide what we want to do with detected packages
# if func == "config":
# kwargs["detected_packages"] = dbtParser.packages
if func == "get": if func == "get":
config_keys_used.append(args[0]) context["config"](utilized=args)
continue continue
context[func](*args, **kwargs) context[func](*args, **kwargs)
if config_keys_used:
# this is being used in macro build_config_dict
context["config"](config_keys_used=config_keys_used)
def render_update(self, node: ParsedModelNode, config: ContextConfig) -> None: def render_update(self, node: ParsedModelNode, config: ContextConfig) -> None:
self.manifest._parsing_info.static_analysis_path_count += 1 self.manifest._parsing_info.static_analysis_path_count += 1

View File

@@ -245,22 +245,6 @@ class PartialParsing:
if "overrides" in source: if "overrides" in source:
self.remove_source_override_target(source) self.remove_source_override_target(source)
def delete_disabled(self, unique_id, file_id):
# This node/metric/exposure is disabled. Find it and remove it from disabled dictionary.
for dis_index, dis_node in enumerate(self.saved_manifest.disabled[unique_id]):
if dis_node.file_id == file_id:
node = dis_node
index = dis_index
break
# Remove node from disabled
del self.saved_manifest.disabled[unique_id][index]
# if all nodes were removed for the unique id, delete the unique_id
# from the disabled dict
if not self.saved_manifest.disabled[unique_id]:
self.saved_manifest.disabled.pop(unique_id)
return node
# Deletes for all non-schema files # Deletes for all non-schema files
def delete_from_saved(self, file_id): def delete_from_saved(self, file_id):
# Look at all things touched by file, remove those # Look at all things touched by file, remove those
@@ -335,7 +319,15 @@ class PartialParsing:
and unique_id in self.saved_manifest.disabled and unique_id in self.saved_manifest.disabled
): ):
# This node is disabled. Find the node and remove it from disabled dictionary. # This node is disabled. Find the node and remove it from disabled dictionary.
node = self.delete_disabled(unique_id, source_file.file_id) for dis_index, dis_node in enumerate(self.saved_manifest.disabled[unique_id]):
if dis_node.file_id == source_file.file_id:
node = dis_node
break
if dis_node:
# Remove node from disabled and unique_id from disabled dict if necessary
del self.saved_manifest.disabled[unique_id][dis_index]
if not self.saved_manifest.disabled[unique_id]:
self.saved_manifest.disabled.pop(unique_id)
else: else:
# Has already been deleted by another action # Has already been deleted by another action
return return
@@ -893,40 +885,34 @@ class PartialParsing:
self.add_to_pp_files(self.saved_files[macro_file_id]) self.add_to_pp_files(self.saved_files[macro_file_id])
# exposures are created only from schema files, so just delete # exposures are created only from schema files, so just delete
# the exposure or the disabled exposure. # the exposure.
def delete_schema_exposure(self, schema_file, exposure_dict): def delete_schema_exposure(self, schema_file, exposure_dict):
exposure_name = exposure_dict["name"] exposure_name = exposure_dict["name"]
exposures = schema_file.exposures.copy() exposures = schema_file.exposures.copy()
for unique_id in exposures: for unique_id in exposures:
if unique_id in self.saved_manifest.exposures:
exposure = self.saved_manifest.exposures[unique_id] exposure = self.saved_manifest.exposures[unique_id]
if unique_id in self.saved_manifest.exposures:
if exposure.name == exposure_name: if exposure.name == exposure_name:
self.deleted_manifest.exposures[unique_id] = self.saved_manifest.exposures.pop( self.deleted_manifest.exposures[unique_id] = self.saved_manifest.exposures.pop(
unique_id unique_id
) )
schema_file.exposures.remove(unique_id) schema_file.exposures.remove(unique_id)
fire_event(PartialParsingDeletedExposure(unique_id=unique_id)) fire_event(PartialParsingDeletedExposure(unique_id=unique_id))
elif unique_id in self.saved_manifest.disabled:
self.delete_disabled(unique_id, schema_file.file_id)
# metrics are created only from schema files, but also can be referred to by other nodes # metric are created only from schema files, so just delete
# the metric.
def delete_schema_metric(self, schema_file, metric_dict): def delete_schema_metric(self, schema_file, metric_dict):
metric_name = metric_dict["name"] metric_name = metric_dict["name"]
metrics = schema_file.metrics.copy() metrics = schema_file.metrics.copy()
for unique_id in metrics: for unique_id in metrics:
if unique_id in self.saved_manifest.metrics:
metric = self.saved_manifest.metrics[unique_id] metric = self.saved_manifest.metrics[unique_id]
if unique_id in self.saved_manifest.metrics:
if metric.name == metric_name: if metric.name == metric_name:
# Need to find everything that referenced this metric and schedule for parsing
if unique_id in self.saved_manifest.child_map:
self.schedule_nodes_for_parsing(self.saved_manifest.child_map[unique_id])
self.deleted_manifest.metrics[unique_id] = self.saved_manifest.metrics.pop( self.deleted_manifest.metrics[unique_id] = self.saved_manifest.metrics.pop(
unique_id unique_id
) )
schema_file.metrics.remove(unique_id) schema_file.metrics.remove(unique_id)
fire_event(PartialParsingDeletedMetric(id=unique_id)) fire_event(PartialParsingDeletedMetric(id=unique_id))
elif unique_id in self.saved_manifest.disabled:
self.delete_disabled(unique_id, schema_file.file_id)
def get_schema_element(self, elem_list, elem_name): def get_schema_element(self, elem_list, elem_name):
for element in elem_list: for element in elem_list:

View File

@@ -1,5 +1,3 @@
import os
import pathspec # type: ignore
import pathlib import pathlib
from dbt.clients.system import load_file_contents from dbt.clients.system import load_file_contents
from dbt.contracts.files import ( from dbt.contracts.files import (
@@ -109,9 +107,9 @@ def load_seed_source_file(match: FilePath, project_name) -> SourceFile:
# Use the FilesystemSearcher to get a bunch of FilePaths, then turn # Use the FilesystemSearcher to get a bunch of FilePaths, then turn
# them into a bunch of FileSource objects # them into a bunch of FileSource objects
def get_source_files(project, paths, extension, parse_file_type, saved_files, ignore_spec): def get_source_files(project, paths, extension, parse_file_type, saved_files):
# file path list # file path list
fp_list = filesystem_search(project, paths, extension, ignore_spec) fp_list = filesystem_search(project, paths, extension)
# file block list # file block list
fb_list = [] fb_list = []
for fp in fp_list: for fp in fp_list:
@@ -131,84 +129,42 @@ def get_source_files(project, paths, extension, parse_file_type, saved_files, ig
return fb_list return fb_list
def read_files_for_parser(project, files, dirs, extensions, parse_ft, saved_files, ignore_spec): def read_files_for_parser(project, files, dirs, extensions, parse_ft, saved_files):
parser_files = [] parser_files = []
for extension in extensions: for extension in extensions:
source_files = get_source_files( source_files = get_source_files(project, dirs, extension, parse_ft, saved_files)
project, dirs, extension, parse_ft, saved_files, ignore_spec
)
for sf in source_files: for sf in source_files:
files[sf.file_id] = sf files[sf.file_id] = sf
parser_files.append(sf.file_id) parser_files.append(sf.file_id)
return parser_files return parser_files
def generate_dbt_ignore_spec(project_root):
ignore_file_path = os.path.join(project_root, ".dbtignore")
ignore_spec = None
if os.path.exists(ignore_file_path):
with open(ignore_file_path) as f:
ignore_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, f)
return ignore_spec
# This needs to read files for multiple projects, so the 'files' # This needs to read files for multiple projects, so the 'files'
# dictionary needs to be passed in. What determines the order of # dictionary needs to be passed in. What determines the order of
# the various projects? Is the root project always last? Do the # the various projects? Is the root project always last? Do the
# non-root projects need to be done separately in order? # non-root projects need to be done separately in order?
def read_files(project, files, parser_files, saved_files): def read_files(project, files, parser_files, saved_files):
dbt_ignore_spec = generate_dbt_ignore_spec(project.project_root)
project_files = {} project_files = {}
project_files["MacroParser"] = read_files_for_parser( project_files["MacroParser"] = read_files_for_parser(
project, project, files, project.macro_paths, [".sql"], ParseFileType.Macro, saved_files
files,
project.macro_paths,
[".sql"],
ParseFileType.Macro,
saved_files,
dbt_ignore_spec,
) )
project_files["ModelParser"] = read_files_for_parser( project_files["ModelParser"] = read_files_for_parser(
project, project, files, project.model_paths, [".sql", ".py"], ParseFileType.Model, saved_files
files,
project.model_paths,
[".sql", ".py"],
ParseFileType.Model,
saved_files,
dbt_ignore_spec,
) )
project_files["SnapshotParser"] = read_files_for_parser( project_files["SnapshotParser"] = read_files_for_parser(
project, project, files, project.snapshot_paths, [".sql"], ParseFileType.Snapshot, saved_files
files,
project.snapshot_paths,
[".sql"],
ParseFileType.Snapshot,
saved_files,
dbt_ignore_spec,
) )
project_files["AnalysisParser"] = read_files_for_parser( project_files["AnalysisParser"] = read_files_for_parser(
project, project, files, project.analysis_paths, [".sql"], ParseFileType.Analysis, saved_files
files,
project.analysis_paths,
[".sql"],
ParseFileType.Analysis,
saved_files,
dbt_ignore_spec,
) )
project_files["SingularTestParser"] = read_files_for_parser( project_files["SingularTestParser"] = read_files_for_parser(
project, project, files, project.test_paths, [".sql"], ParseFileType.SingularTest, saved_files
files,
project.test_paths,
[".sql"],
ParseFileType.SingularTest,
saved_files,
dbt_ignore_spec,
) )
# all generic tests within /tests must be nested under a /generic subfolder # all generic tests within /tests must be nested under a /generic subfolder
@@ -219,27 +175,14 @@ def read_files(project, files, parser_files, saved_files):
[".sql"], [".sql"],
ParseFileType.GenericTest, ParseFileType.GenericTest,
saved_files, saved_files,
dbt_ignore_spec,
) )
project_files["SeedParser"] = read_files_for_parser( project_files["SeedParser"] = read_files_for_parser(
project, project, files, project.seed_paths, [".csv"], ParseFileType.Seed, saved_files
files,
project.seed_paths,
[".csv"],
ParseFileType.Seed,
saved_files,
dbt_ignore_spec,
) )
project_files["DocumentationParser"] = read_files_for_parser( project_files["DocumentationParser"] = read_files_for_parser(
project, project, files, project.docs_paths, [".md"], ParseFileType.Documentation, saved_files
files,
project.docs_paths,
[".md"],
ParseFileType.Documentation,
saved_files,
dbt_ignore_spec,
) )
project_files["SchemaParser"] = read_files_for_parser( project_files["SchemaParser"] = read_files_for_parser(
@@ -249,7 +192,6 @@ def read_files(project, files, parser_files, saved_files):
[".yml", ".yaml"], [".yml", ".yaml"],
ParseFileType.Schema, ParseFileType.Schema,
saved_files, saved_files,
dbt_ignore_spec,
) )
# Store the parser files for this particular project # Store the parser files for this particular project

View File

@@ -67,8 +67,7 @@ class SchemaYamlRenderer(BaseRenderer):
elif self._is_norender_key(keypath[0:]): elif self._is_norender_key(keypath[0:]):
return False return False
elif self.key == "metrics": elif self.key == "metrics":
# back compat: "expression" is new name, "sql" is old name if keypath[0] == "expression":
if keypath[0] in ("expression", "sql"):
return False return False
elif self._is_norender_key(keypath[0:]): elif self._is_norender_key(keypath[0:]):
return False return False

View File

@@ -1010,7 +1010,6 @@ class ExposureParser(YamlReader):
meta=unparsed.meta, meta=unparsed.meta,
tags=unparsed.tags, tags=unparsed.tags,
description=unparsed.description, description=unparsed.description,
label=unparsed.label,
owner=unparsed.owner, owner=unparsed.owner,
maturity=unparsed.maturity, maturity=unparsed.maturity,
config=config, config=config,
@@ -1029,7 +1028,7 @@ class ExposureParser(YamlReader):
if parsed.config.enabled: if parsed.config.enabled:
self.manifest.add_exposure(self.yaml.file, parsed) self.manifest.add_exposure(self.yaml.file, parsed)
else: else:
self.manifest.add_disabled(self.yaml.file, parsed) self.manifest.add_disabled_nofile(parsed)
def _generate_exposure_config( def _generate_exposure_config(
self, target: UnparsedExposure, fqn: List[str], package_name: str, rendered: bool self, target: UnparsedExposure, fqn: List[str], package_name: str, rendered: bool
@@ -1062,7 +1061,6 @@ class ExposureParser(YamlReader):
except (ValidationError, JSONValidationException) as exc: except (ValidationError, JSONValidationException) as exc:
msg = error_context(self.yaml.path, self.key, data, exc) msg = error_context(self.yaml.path, self.key, data, exc)
raise ParsingException(msg) from exc raise ParsingException(msg) from exc
self.parse_exposure(unparsed) self.parse_exposure(unparsed)
@@ -1115,6 +1113,7 @@ class MetricParser(YamlReader):
timestamp=unparsed.timestamp, timestamp=unparsed.timestamp,
dimensions=unparsed.dimensions, dimensions=unparsed.dimensions,
window=unparsed.window, window=unparsed.window,
fill_missing_values=unparsed.fill_missing_values,
time_grains=unparsed.time_grains, time_grains=unparsed.time_grains,
filters=unparsed.filters, filters=unparsed.filters,
meta=unparsed.meta, meta=unparsed.meta,
@@ -1144,7 +1143,7 @@ class MetricParser(YamlReader):
if parsed.config.enabled: if parsed.config.enabled:
self.manifest.add_metric(self.yaml.file, parsed) self.manifest.add_metric(self.yaml.file, parsed)
else: else:
self.manifest.add_disabled(self.yaml.file, parsed) self.manifest.add_disabled_nofile(parsed)
def _generate_metric_config( def _generate_metric_config(
self, target: UnparsedMetric, fqn: List[str], package_name: str, rendered: bool self, target: UnparsedMetric, fqn: List[str], package_name: str, rendered: bool

View File

@@ -1,7 +1,6 @@
import os import os
from dataclasses import dataclass from dataclasses import dataclass
from typing import List, Callable, Iterable, Set, Union, Iterator, TypeVar, Generic, Optional from typing import List, Callable, Iterable, Set, Union, Iterator, TypeVar, Generic
from pathspec import PathSpec # type: ignore
from dbt.clients.jinja import extract_toplevel_blocks, BlockTag from dbt.clients.jinja import extract_toplevel_blocks, BlockTag
from dbt.clients.system import find_matching from dbt.clients.system import find_matching
@@ -62,16 +61,11 @@ class FullBlock(FileBlock):
return self.block.full_block return self.block.full_block
def filesystem_search( def filesystem_search(project: Project, relative_dirs: List[str], extension: str):
project: Project,
relative_dirs: List[str],
extension: str,
ignore_spec: Optional[PathSpec] = None,
):
ext = "[!.#~]*" + extension ext = "[!.#~]*" + extension
root = project.project_root root = project.project_root
file_path_list = [] file_path_list = []
for result in find_matching(root, relative_dirs, ext, ignore_spec): for result in find_matching(root, relative_dirs, ext):
if "searched_path" not in result or "relative_path" not in result: if "searched_path" not in result or "relative_path" not in result:
raise InternalException("Invalid result from find_matching: {}".format(result)) raise InternalException("Invalid result from find_matching: {}".format(result))
file_match = FilePath( file_match = FilePath(

View File

@@ -63,7 +63,7 @@ class SourcePatcher:
self.sources[unpatched.unique_id] = unpatched self.sources[unpatched.unique_id] = unpatched
continue continue
# returns None if there is no patch # returns None if there is no patch
patch = self.get_patch_for(unpatched) patch = self.get_patch_for(unpatched) # type: ignore[unreachable] # CT-564 / GH 5169
# returns unpatched if there is no patch # returns unpatched if there is no patch
patched = self.patch_source(unpatched, patch) patched = self.patch_source(unpatched, patch)
@@ -213,8 +213,8 @@ class SourcePatcher:
self, self,
unpatched: UnpatchedSourceDefinition, unpatched: UnpatchedSourceDefinition,
) -> Optional[SourcePatch]: ) -> Optional[SourcePatch]:
if isinstance(unpatched, ParsedSourceDefinition): if isinstance(unpatched, ParsedSourceDefinition): # type: ignore[unreachable] # CT-564 / GH 5169
return None return None # type: ignore[unreachable] # CT-564 / GH 5169
key = (unpatched.package_name, unpatched.source.name) key = (unpatched.package_name, unpatched.source.name)
patch: Optional[SourcePatch] = self.manifest.source_patches.get(key) patch: Optional[SourcePatch] = self.manifest.source_patches.get(key)
if patch is None: if patch is None:

View File

@@ -43,7 +43,7 @@ class SqlBlockParser(SimpleSQLParser[ParsedSqlNode]):
return os.path.join("sql", block.name) return os.path.join("sql", block.name)
def parse_remote(self, sql: str, name: str) -> ParsedSqlNode: def parse_remote(self, sql: str, name: str) -> ParsedSqlNode:
source_file = SourceFile.remote(sql, self.project.project_name, "sql") source_file = SourceFile.remote(sql, self.project.project_name)
contents = SqlBlock(block_name=name, file=source_file) contents = SqlBlock(block_name=name, file=source_file)
return self.parse_node(contents) return self.parse_node(contents)

View File

@@ -108,13 +108,13 @@ class BaseTask(metaclass=ABCMeta):
config = cls.ConfigType.from_args(args) config = cls.ConfigType.from_args(args)
except dbt.exceptions.DbtProjectError as exc: except dbt.exceptions.DbtProjectError as exc:
fire_event(DbtProjectError()) fire_event(DbtProjectError())
fire_event(DbtProjectErrorException(exc=str(exc))) fire_event(DbtProjectErrorException(exc=exc))
tracking.track_invalid_invocation(args=args, result_type=exc.result_type) tracking.track_invalid_invocation(args=args, result_type=exc.result_type)
raise dbt.exceptions.RuntimeException("Could not run dbt") from exc raise dbt.exceptions.RuntimeException("Could not run dbt") from exc
except dbt.exceptions.DbtProfileError as exc: except dbt.exceptions.DbtProfileError as exc:
fire_event(DbtProfileError()) fire_event(DbtProfileError())
fire_event(DbtProfileErrorException(exc=str(exc))) fire_event(DbtProfileErrorException(exc=exc))
all_profiles = read_profiles(flags.PROFILES_DIR).keys() all_profiles = read_profiles(flags.PROFILES_DIR).keys()
@@ -170,7 +170,6 @@ def get_nearest_project_dir(args):
def move_to_nearest_project_dir(args): def move_to_nearest_project_dir(args):
nearest_project_dir = get_nearest_project_dir(args) nearest_project_dir = get_nearest_project_dir(args)
os.chdir(nearest_project_dir) os.chdir(nearest_project_dir)
return nearest_project_dir
class ConfiguredTask(BaseTask): class ConfiguredTask(BaseTask):
@@ -346,11 +345,11 @@ class BaseRunner(metaclass=ABCMeta):
if e.node is None: if e.node is None:
e.add_node(ctx.node) e.add_node(ctx.node)
fire_event(CatchableExceptionOnRun(exc=str(e))) fire_event(CatchableExceptionOnRun(exc=e))
return str(e) return str(e)
def _handle_internal_exception(self, e, ctx): def _handle_internal_exception(self, e, ctx):
fire_event(InternalExceptionOnRun(build_path=self.node.build_path, exc=str(e))) fire_event(InternalExceptionOnRun(build_path=self.node.build_path, exc=e))
return str(e) return str(e)
def _handle_generic_exception(self, e, ctx): def _handle_generic_exception(self, e, ctx):
@@ -358,7 +357,7 @@ class BaseRunner(metaclass=ABCMeta):
GenericExceptionOnRun( GenericExceptionOnRun(
build_path=self.node.build_path, build_path=self.node.build_path,
unique_id=self.node.unique_id, unique_id=self.node.unique_id,
exc=str(e), exc=str(e), # TODO: unstring this when serialization is fixed
) )
) )
fire_event(PrintDebugStackTrace()) fire_event(PrintDebugStackTrace())
@@ -414,7 +413,7 @@ class BaseRunner(metaclass=ABCMeta):
try: try:
self.adapter.release_connection() self.adapter.release_connection()
except Exception as exc: except Exception as exc:
fire_event(NodeConnectionReleaseError(node_name=self.node.name, exc=str(exc))) fire_event(NodeConnectionReleaseError(node_name=self.node.name, exc=exc))
return str(exc) return str(exc)
return None return None

View File

@@ -21,6 +21,10 @@ from dbt.version import get_installed_version
from dbt.task.base import BaseTask, get_nearest_project_dir from dbt.task.base import BaseTask, get_nearest_project_dir
PROFILE_DIR_MESSAGE = """To view your profiles.yml file, run:
{open_cmd} {profiles_dir}"""
ONLY_PROFILE_MESSAGE = """ ONLY_PROFILE_MESSAGE = """
A `dbt_project.yml` file was not found in this directory. A `dbt_project.yml` file was not found in this directory.
Using the only profile `{}`. Using the only profile `{}`.

View File

@@ -278,9 +278,7 @@ class ModelRunner(CompileRunner):
hook_ctx = self.adapter.pre_model_hook(context_config) hook_ctx = self.adapter.pre_model_hook(context_config)
try: try:
result = MacroGenerator( result = MacroGenerator(materialization_macro, context)()
materialization_macro, context, stack=context["context_macro_stack"]
)()
finally: finally:
self.adapter.post_model_hook(context_config, hook_ctx) self.adapter.post_model_hook(context_config, hook_ctx)

View File

@@ -55,11 +55,11 @@ class RunOperationTask(ManifestTask):
try: try:
self._run_unsafe() self._run_unsafe()
except dbt.exceptions.Exception as exc: except dbt.exceptions.Exception as exc:
fire_event(RunningOperationCaughtError(exc=str(exc))) fire_event(RunningOperationCaughtError(exc=exc))
fire_event(PrintDebugStackTrace()) fire_event(PrintDebugStackTrace())
success = False success = False
except Exception as exc: except Exception as exc:
fire_event(RunningOperationUncaughtError(exc=str(exc))) fire_event(RunningOperationUncaughtError(exc=exc))
fire_event(PrintDebugStackTrace()) fire_event(PrintDebugStackTrace())
success = False success = False
else: else:

View File

@@ -22,7 +22,7 @@ class GenericSqlRunner(CompileRunner, Generic[SQLResult]):
CompileRunner.__init__(self, config, adapter, node, node_index, num_nodes) CompileRunner.__init__(self, config, adapter, node, node_index, num_nodes)
def handle_exception(self, e, ctx): def handle_exception(self, e, ctx):
fire_event(SQlRunnerException(exc=str(e))) fire_event(SQlRunnerException(exc=e))
if isinstance(e, dbt.exceptions.Exception): if isinstance(e, dbt.exceptions.Exception):
if isinstance(e, dbt.exceptions.RuntimeException): if isinstance(e, dbt.exceptions.RuntimeException):
e.add_node(ctx.node) e.add_node(ctx.node)

View File

@@ -6,7 +6,6 @@ import warnings
from datetime import datetime from datetime import datetime
from typing import List from typing import List
from contextlib import contextmanager from contextlib import contextmanager
from dbt.adapters.factory import Adapter
from dbt.main import handle_and_check from dbt.main import handle_and_check
from dbt.logger import log_manager from dbt.logger import log_manager
@@ -216,7 +215,6 @@ class TestProcessingException(Exception):
def run_sql_with_adapter(adapter, sql, fetch=None): def run_sql_with_adapter(adapter, sql, fetch=None):
if sql.strip() == "": if sql.strip() == "":
return return
# substitute schema and database in sql # substitute schema and database in sql
kwargs = { kwargs = {
"schema": adapter.config.credentials.schema, "schema": adapter.config.credentials.schema,
@@ -310,7 +308,7 @@ def check_relation_types(adapter, relation_to_type):
# by doing a separate call for each set of tables/relations. # by doing a separate call for each set of tables/relations.
# Wraps check_relations_equal_with_relations by creating relations # Wraps check_relations_equal_with_relations by creating relations
# from the list of names passed in. # from the list of names passed in.
def check_relations_equal(adapter, relation_names: List, compare_snapshot_cols=False): def check_relations_equal(adapter, relation_names, compare_snapshot_cols=False):
if len(relation_names) < 2: if len(relation_names) < 2:
raise TestProcessingException( raise TestProcessingException(
"Not enough relations to compare", "Not enough relations to compare",
@@ -327,9 +325,7 @@ def check_relations_equal(adapter, relation_names: List, compare_snapshot_cols=F
# adapter.get_columns_in_relation # adapter.get_columns_in_relation
# adapter.get_rows_different_sql # adapter.get_rows_different_sql
# adapter.execute # adapter.execute
def check_relations_equal_with_relations( def check_relations_equal_with_relations(adapter, relations, compare_snapshot_cols=False):
adapter: Adapter, relations: List, compare_snapshot_cols=False
):
with get_connection(adapter): with get_connection(adapter):
basis, compares = relations[0], relations[1:] basis, compares = relations[0], relations[1:]
@@ -338,12 +334,12 @@ def check_relations_equal_with_relations(
# (unless comparing "dbt_" snapshot columns is explicitly enabled) # (unless comparing "dbt_" snapshot columns is explicitly enabled)
column_names = [ column_names = [
c.name c.name
for c in adapter.get_columns_in_relation(basis) # type: ignore for c in adapter.get_columns_in_relation(basis)
if not c.name.lower().startswith("dbt_") or compare_snapshot_cols if not c.name.lower().startswith("dbt_") or compare_snapshot_cols
] ]
for relation in compares: for relation in compares:
sql = adapter.get_rows_different_sql(basis, relation, column_names=column_names) # type: ignore sql = adapter.get_rows_different_sql(basis, relation, column_names=column_names)
_, tbl = adapter.execute(sql, fetch=True) _, tbl = adapter.execute(sql, fetch=True)
num_rows = len(tbl) num_rows = len(tbl)
assert ( assert (

View File

@@ -619,7 +619,7 @@ def _connection_exception_retry(fn, max_attempts: int, attempt: int = 0):
ReadError, ReadError,
) as exc: ) as exc:
if attempt <= max_attempts - 1: if attempt <= max_attempts - 1:
fire_event(RecordRetryException(exc=str(exc))) fire_event(RecordRetryException(exc=exc))
fire_event(RetryExternalCall(attempt=attempt, max=max_attempts)) fire_event(RetryExternalCall(attempt=attempt, max=max_attempts))
time.sleep(1) time.sleep(1)
return _connection_exception_retry(fn, max_attempts, attempt + 1) return _connection_exception_retry(fn, max_attempts, attempt + 1)

View File

@@ -62,7 +62,6 @@ setup(
"dbt-extractor~=0.4.1", "dbt-extractor~=0.4.1",
"typing-extensions>=3.7.4", "typing-extensions>=3.7.4",
"werkzeug>=1,<3", "werkzeug>=1,<3",
"pathspec~=0.9.0",
# the following are all to match snowflake-connector-python # the following are all to match snowflake-connector-python
"requests<3.0.0", "requests<3.0.0",
"idna>=2.5,<4", "idna>=2.5,<4",

View File

@@ -244,7 +244,7 @@
"generated_at": { "generated_at": {
"type": "string", "type": "string",
"format": "date-time", "format": "date-time",
"default": "2022-09-14T20:35:15.346636Z" "default": "2022-09-12T20:41:53.793012Z"
}, },
"invocation_id": { "invocation_id": {
"oneOf": [ "oneOf": [
@@ -255,7 +255,7 @@
"type": "null" "type": "null"
} }
], ],
"default": "c59a8269-533c-4b78-a709-5094045afd4d" "default": "3e1f83a7-c7fd-44ed-bb2f-5e3fc9aee730"
}, },
"env": { "env": {
"type": "object", "type": "object",
@@ -523,7 +523,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.3517282 "default": 1663015313.7976892
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -1074,7 +1074,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.35441 "default": 1663015313.800915
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -1437,7 +1437,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.356541 "default": 1663015313.802708
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -1688,7 +1688,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.3582149 "default": 1663015313.8044102
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -1949,7 +1949,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.359935 "default": 1663015313.806144
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -2200,7 +2200,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.361423 "default": 1663015313.807632
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -2447,7 +2447,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.363411 "default": 1663015313.809396
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -2746,7 +2746,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.366584 "default": 1663015313.8124218
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -3162,7 +3162,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.3682182 "default": 1663015313.814218
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -3409,7 +3409,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.369675 "default": 1663015313.816299
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -3617,7 +3617,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.370925 "default": 1663015313.817742
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -3833,7 +3833,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.372257 "default": 1663015313.819065
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -4059,7 +4059,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.373705 "default": 1663015313.820469
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -4275,7 +4275,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.375131 "default": 1663015313.821846
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -4491,7 +4491,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.376405 "default": 1663015313.823106
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -4703,7 +4703,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.3784761 "default": 1663015313.8250082
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -4940,7 +4940,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.380325 "default": 1663015313.826428
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -5133,7 +5133,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.3832462 "default": 1663015313.8290439
}, },
"config_call_dict": { "config_call_dict": {
"type": "object", "type": "object",
@@ -5518,7 +5518,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.3854342 "default": 1663015313.831739
} }
}, },
"additionalProperties": false, "additionalProperties": false,
@@ -5633,7 +5633,7 @@
"generated_at": { "generated_at": {
"type": "string", "type": "string",
"format": "date-time", "format": "date-time",
"default": "2022-09-14T20:35:15.341700Z" "default": "2022-09-12T20:41:53.788122Z"
}, },
"invocation_id": { "invocation_id": {
"oneOf": [ "oneOf": [
@@ -5644,7 +5644,7 @@
"type": "null" "type": "null"
} }
], ],
"default": "c59a8269-533c-4b78-a709-5094045afd4d" "default": "3e1f83a7-c7fd-44ed-bb2f-5e3fc9aee730"
}, },
"env": { "env": {
"type": "object", "type": "object",
@@ -6002,7 +6002,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.386226 "default": 1663015313.832941
}, },
"supported_languages": { "supported_languages": {
"oneOf": [ "oneOf": [
@@ -6178,16 +6178,6 @@
"type": "string", "type": "string",
"default": "" "default": ""
}, },
"label": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"maturity": { "maturity": {
"oneOf": [ "oneOf": [
{ {
@@ -6263,11 +6253,11 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.3878772 "default": 1663015313.834493
} }
}, },
"additionalProperties": false, "additionalProperties": false,
"description": "ParsedExposure(fqn: List[str], unique_id: str, package_name: str, root_path: str, path: str, original_file_path: str, name: str, type: dbt.contracts.graph.unparsed.ExposureType, owner: dbt.contracts.graph.unparsed.ExposureOwner, resource_type: dbt.node_types.NodeType = <NodeType.Exposure: 'exposure'>, description: str = '', label: Union[str, NoneType] = None, maturity: Union[dbt.contracts.graph.unparsed.MaturityType, NoneType] = None, meta: Dict[str, Any] = <factory>, tags: List[str] = <factory>, config: dbt.contracts.graph.model_config.ExposureConfig = <factory>, unrendered_config: Dict[str, Any] = <factory>, url: Union[str, NoneType] = None, depends_on: dbt.contracts.graph.parsed.DependsOn = <factory>, refs: List[List[str]] = <factory>, sources: List[List[str]] = <factory>, created_at: float = <factory>)" "description": "ParsedExposure(fqn: List[str], unique_id: str, package_name: str, root_path: str, path: str, original_file_path: str, name: str, type: dbt.contracts.graph.unparsed.ExposureType, owner: dbt.contracts.graph.unparsed.ExposureOwner, resource_type: dbt.node_types.NodeType = <NodeType.Exposure: 'exposure'>, description: str = '', maturity: Union[dbt.contracts.graph.unparsed.MaturityType, NoneType] = None, meta: Dict[str, Any] = <factory>, tags: List[str] = <factory>, config: dbt.contracts.graph.model_config.ExposureConfig = <factory>, unrendered_config: Dict[str, Any] = <factory>, url: Union[str, NoneType] = None, depends_on: dbt.contracts.graph.parsed.DependsOn = <factory>, refs: List[List[str]] = <factory>, sources: List[List[str]] = <factory>, created_at: float = <factory>)"
}, },
"ExposureOwner": { "ExposureOwner": {
"type": "object", "type": "object",
@@ -6496,7 +6486,7 @@
}, },
"created_at": { "created_at": {
"type": "number", "type": "number",
"default": 1663187715.38939 "default": 1663015313.836148
} }
}, },
"additionalProperties": false, "additionalProperties": false,

View File

@@ -0,0 +1,7 @@
{{
config(
materialized = "table"
)
}}
select * from {{ref('view_model')}}

View File

@@ -0,0 +1,7 @@
{{
config(
materialized = "table"
)
}}
select a_field_that_does_not_exist from {{ this.schema }}.seed

View File

@@ -0,0 +1 @@
select * from {{ref('invalid')}}

View File

@@ -0,0 +1,7 @@
{{
config(
materialized = "table"
)
}}
select * from {{ this.schema }}.seed

View File

@@ -0,0 +1,7 @@
{{
config(
materialized = "table"
)
}}
select * from {{ this.schema }}.seed

View File

@@ -0,0 +1,7 @@
{{
config(
materialized = "view"
)
}}
select * from {{ this.schema }}.seed

View File

@@ -0,0 +1,5 @@
select * from {{ref('table_a')}}
union all
select * from {{ref('table_b')}}

View File

@@ -0,0 +1,111 @@
create table {schema}.seed (
id BIGSERIAL PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
email VARCHAR(50),
gender VARCHAR(50),
ip_address VARCHAR(20)
);
insert into {schema}.seed (first_name, last_name, email, gender, ip_address) values
('Jack', 'Hunter', 'jhunter0@pbs.org', 'Male', '59.80.20.168'),
('Kathryn', 'Walker', 'kwalker1@ezinearticles.com', 'Female', '194.121.179.35'),
('Gerald', 'Ryan', 'gryan2@com.com', 'Male', '11.3.212.243'),
('Bonnie', 'Spencer', 'bspencer3@ameblo.jp', 'Female', '216.32.196.175'),
('Harold', 'Taylor', 'htaylor4@people.com.cn', 'Male', '253.10.246.136'),
('Jacqueline', 'Griffin', 'jgriffin5@t.co', 'Female', '16.13.192.220'),
('Wanda', 'Arnold', 'warnold6@google.nl', 'Female', '232.116.150.64'),
('Craig', 'Ortiz', 'cortiz7@sciencedaily.com', 'Male', '199.126.106.13'),
('Gary', 'Day', 'gday8@nih.gov', 'Male', '35.81.68.186'),
('Rose', 'Wright', 'rwright9@yahoo.co.jp', 'Female', '236.82.178.100'),
('Raymond', 'Kelley', 'rkelleya@fc2.com', 'Male', '213.65.166.67'),
('Gerald', 'Robinson', 'grobinsonb@disqus.com', 'Male', '72.232.194.193'),
('Mildred', 'Martinez', 'mmartinezc@samsung.com', 'Female', '198.29.112.5'),
('Dennis', 'Arnold', 'darnoldd@google.com', 'Male', '86.96.3.250'),
('Judy', 'Gray', 'jgraye@opensource.org', 'Female', '79.218.162.245'),
('Theresa', 'Garza', 'tgarzaf@epa.gov', 'Female', '21.59.100.54'),
('Gerald', 'Robertson', 'grobertsong@csmonitor.com', 'Male', '131.134.82.96'),
('Philip', 'Hernandez', 'phernandezh@adobe.com', 'Male', '254.196.137.72'),
('Julia', 'Gonzalez', 'jgonzalezi@cam.ac.uk', 'Female', '84.240.227.174'),
('Andrew', 'Davis', 'adavisj@patch.com', 'Male', '9.255.67.25'),
('Kimberly', 'Harper', 'kharperk@foxnews.com', 'Female', '198.208.120.253'),
('Mark', 'Martin', 'mmartinl@marketwatch.com', 'Male', '233.138.182.153'),
('Cynthia', 'Ruiz', 'cruizm@google.fr', 'Female', '18.178.187.201'),
('Samuel', 'Carroll', 'scarrolln@youtu.be', 'Male', '128.113.96.122'),
('Jennifer', 'Larson', 'jlarsono@vinaora.com', 'Female', '98.234.85.95'),
('Ashley', 'Perry', 'aperryp@rakuten.co.jp', 'Female', '247.173.114.52'),
('Howard', 'Rodriguez', 'hrodriguezq@shutterfly.com', 'Male', '231.188.95.26'),
('Amy', 'Brooks', 'abrooksr@theatlantic.com', 'Female', '141.199.174.118'),
('Louise', 'Warren', 'lwarrens@adobe.com', 'Female', '96.105.158.28'),
('Tina', 'Watson', 'twatsont@myspace.com', 'Female', '251.142.118.177'),
('Janice', 'Kelley', 'jkelleyu@creativecommons.org', 'Female', '239.167.34.233'),
('Terry', 'Mccoy', 'tmccoyv@bravesites.com', 'Male', '117.201.183.203'),
('Jeffrey', 'Morgan', 'jmorganw@surveymonkey.com', 'Male', '78.101.78.149'),
('Louis', 'Harvey', 'lharveyx@sina.com.cn', 'Male', '51.50.0.167'),
('Philip', 'Miller', 'pmillery@samsung.com', 'Male', '103.255.222.110'),
('Willie', 'Marshall', 'wmarshallz@ow.ly', 'Male', '149.219.91.68'),
('Patrick', 'Lopez', 'plopez10@redcross.org', 'Male', '250.136.229.89'),
('Adam', 'Jenkins', 'ajenkins11@harvard.edu', 'Male', '7.36.112.81'),
('Benjamin', 'Cruz', 'bcruz12@linkedin.com', 'Male', '32.38.98.15'),
('Ruby', 'Hawkins', 'rhawkins13@gmpg.org', 'Female', '135.171.129.255'),
('Carlos', 'Barnes', 'cbarnes14@a8.net', 'Male', '240.197.85.140'),
('Ruby', 'Griffin', 'rgriffin15@bravesites.com', 'Female', '19.29.135.24'),
('Sean', 'Mason', 'smason16@icq.com', 'Male', '159.219.155.249'),
('Anthony', 'Payne', 'apayne17@utexas.edu', 'Male', '235.168.199.218'),
('Steve', 'Cruz', 'scruz18@pcworld.com', 'Male', '238.201.81.198'),
('Anthony', 'Garcia', 'agarcia19@flavors.me', 'Male', '25.85.10.18'),
('Doris', 'Lopez', 'dlopez1a@sphinn.com', 'Female', '245.218.51.238'),
('Susan', 'Nichols', 'snichols1b@freewebs.com', 'Female', '199.99.9.61'),
('Wanda', 'Ferguson', 'wferguson1c@yahoo.co.jp', 'Female', '236.241.135.21'),
('Andrea', 'Pierce', 'apierce1d@google.co.uk', 'Female', '132.40.10.209'),
('Lawrence', 'Phillips', 'lphillips1e@jugem.jp', 'Male', '72.226.82.87'),
('Judy', 'Gilbert', 'jgilbert1f@multiply.com', 'Female', '196.250.15.142'),
('Eric', 'Williams', 'ewilliams1g@joomla.org', 'Male', '222.202.73.126'),
('Ralph', 'Romero', 'rromero1h@sogou.com', 'Male', '123.184.125.212'),
('Jean', 'Wilson', 'jwilson1i@ocn.ne.jp', 'Female', '176.106.32.194'),
('Lori', 'Reynolds', 'lreynolds1j@illinois.edu', 'Female', '114.181.203.22'),
('Donald', 'Moreno', 'dmoreno1k@bbc.co.uk', 'Male', '233.249.97.60'),
('Steven', 'Berry', 'sberry1l@eepurl.com', 'Male', '186.193.50.50'),
('Theresa', 'Shaw', 'tshaw1m@people.com.cn', 'Female', '120.37.71.222'),
('John', 'Stephens', 'jstephens1n@nationalgeographic.com', 'Male', '191.87.127.115'),
('Richard', 'Jacobs', 'rjacobs1o@state.tx.us', 'Male', '66.210.83.155'),
('Andrew', 'Lawson', 'alawson1p@over-blog.com', 'Male', '54.98.36.94'),
('Peter', 'Morgan', 'pmorgan1q@rambler.ru', 'Male', '14.77.29.106'),
('Nicole', 'Garrett', 'ngarrett1r@zimbio.com', 'Female', '21.127.74.68'),
('Joshua', 'Kim', 'jkim1s@edublogs.org', 'Male', '57.255.207.41'),
('Ralph', 'Roberts', 'rroberts1t@people.com.cn', 'Male', '222.143.131.109'),
('George', 'Montgomery', 'gmontgomery1u@smugmug.com', 'Male', '76.75.111.77'),
('Gerald', 'Alvarez', 'galvarez1v@flavors.me', 'Male', '58.157.186.194'),
('Donald', 'Olson', 'dolson1w@whitehouse.gov', 'Male', '69.65.74.135'),
('Carlos', 'Morgan', 'cmorgan1x@pbs.org', 'Male', '96.20.140.87'),
('Aaron', 'Stanley', 'astanley1y@webnode.com', 'Male', '163.119.217.44'),
('Virginia', 'Long', 'vlong1z@spiegel.de', 'Female', '204.150.194.182'),
('Robert', 'Berry', 'rberry20@tripadvisor.com', 'Male', '104.19.48.241'),
('Antonio', 'Brooks', 'abrooks21@unesco.org', 'Male', '210.31.7.24'),
('Ruby', 'Garcia', 'rgarcia22@ovh.net', 'Female', '233.218.162.214'),
('Jack', 'Hanson', 'jhanson23@blogtalkradio.com', 'Male', '31.55.46.199'),
('Kathryn', 'Nelson', 'knelson24@walmart.com', 'Female', '14.189.146.41'),
('Jason', 'Reed', 'jreed25@printfriendly.com', 'Male', '141.189.89.255'),
('George', 'Coleman', 'gcoleman26@people.com.cn', 'Male', '81.189.221.144'),
('Rose', 'King', 'rking27@ucoz.com', 'Female', '212.123.168.231'),
('Johnny', 'Holmes', 'jholmes28@boston.com', 'Male', '177.3.93.188'),
('Katherine', 'Gilbert', 'kgilbert29@altervista.org', 'Female', '199.215.169.61'),
('Joshua', 'Thomas', 'jthomas2a@ustream.tv', 'Male', '0.8.205.30'),
('Julie', 'Perry', 'jperry2b@opensource.org', 'Female', '60.116.114.192'),
('Richard', 'Perry', 'rperry2c@oracle.com', 'Male', '181.125.70.232'),
('Kenneth', 'Ruiz', 'kruiz2d@wikimedia.org', 'Male', '189.105.137.109'),
('Jose', 'Morgan', 'jmorgan2e@webnode.com', 'Male', '101.134.215.156'),
('Donald', 'Campbell', 'dcampbell2f@goo.ne.jp', 'Male', '102.120.215.84'),
('Debra', 'Collins', 'dcollins2g@uol.com.br', 'Female', '90.13.153.235'),
('Jesse', 'Johnson', 'jjohnson2h@stumbleupon.com', 'Male', '225.178.125.53'),
('Elizabeth', 'Stone', 'estone2i@histats.com', 'Female', '123.184.126.221'),
('Angela', 'Rogers', 'arogers2j@goodreads.com', 'Female', '98.104.132.187'),
('Emily', 'Dixon', 'edixon2k@mlb.com', 'Female', '39.190.75.57'),
('Albert', 'Scott', 'ascott2l@tinypic.com', 'Male', '40.209.13.189'),
('Barbara', 'Peterson', 'bpeterson2m@ow.ly', 'Female', '75.249.136.180'),
('Adam', 'Greene', 'agreene2n@fastcompany.com', 'Male', '184.173.109.144'),
('Earl', 'Sanders', 'esanders2o@hc360.com', 'Male', '247.34.90.117'),
('Angela', 'Brooks', 'abrooks2p@mtv.com', 'Female', '10.63.249.126'),
('Harold', 'Foster', 'hfoster2q@privacy.gov.au', 'Male', '139.214.40.244'),
('Carl', 'Meyer', 'cmeyer2r@disqus.com', 'Male', '204.117.7.88');

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