-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CT-1922: Parsing and postgres implementations for model level constraints #7230
Conversation
Thank you for your pull request! We could not find a changelog entry for this change. For details on how to document a change, see the contributing guide. |
core/dbt/contracts/graph/nodes.py
Outdated
|
||
@dataclass | ||
class ModelLevelConstraint(ColumnLevelConstraint): | ||
pass # REVIEW: The columns property above should be on this class instead |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reasons I am still working to understand, moving the columns property above to this class, where it would actually makes sense, results in the columns property being missing when the raw dictionary is passed to the adapter implementation of render_raw_model_constraint().
@gerda It seems like there is a mechanism at work which adds defaults values to and removes unexpected values from the raw dictionaries, but is confused about which type this one has. Can you help me understand what's going on here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"constraints" is not processed as a config, it's a node-level field. So you need to actually copy it at the top of the "patch" method on parsed node, where "description" and "columns" and "access" are copied. It's only on model nodes so you'd also want to check the node.resource_type. (At some point we may want to split out more finely grained "patch" methods...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find the answer, but I want to make sure I actually understand the question. It sounds like if columns
is a key in raw_constraint
, then BaseAdapter._parse_model_constraint(raw_constraint)
does not have a columns
attribute, but BaseAdapter._parse_column_constraint(raw_constraint)
does have a columns
attribute. Is that right?
Is raw_constraint["columns"]
just a List[str]
? Or is it more complicated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(At some point we may want to split out more finely grained "patch" methods...)
Thinking along the same lines for patching other new node-level fields that are only on model nodes in the model version parsing spike (version, is_latest_version).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This turned out to be the result of a typo fixed in my last commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't count the time I've lost to typos. Turns out the computer is right again.
core/dbt/include/global_project/macros/materializations/models/table/create_table_as.sql
Show resolved
Hide resolved
core/dbt/contracts/graph/nodes.py
Outdated
|
||
@dataclass | ||
class ModelLevelConstraint(ColumnLevelConstraint): | ||
pass # REVIEW: The columns property above should be on this class instead |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find the answer, but I want to make sure I actually understand the question. It sounds like if columns
is a key in raw_constraint
, then BaseAdapter._parse_model_constraint(raw_constraint)
does not have a columns
attribute, but BaseAdapter._parse_column_constraint(raw_constraint)
does have a columns
attribute. Is that right?
Is raw_constraint["columns"]
just a List[str]
? Or is it more complicated?
core/dbt/parser/schemas.py
Outdated
raise ParsingError( | ||
f"Original File Path: ({original_file_path})\nConstraints must be defined in a `yml` schema configuration file like `schema.yml`.\nOnly the SQL table and view materializations are supported for constraints. \n`data_type` values must be defined for all columns and NOT be null or blank.{self.convert_errors_to_string(error_messages)}" | ||
|
||
if isinstance(node, ModelNode) and node.config.contract is True: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code works, but right now we're doing node-level field setting in the patch method so ideally this should happen in that method if possible. I presume that access to "validate_constraints" is the issue? Can that be changed into a function that can be called in both places?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, we could pull that "patch" method into where this code is called from. It doesn't need to stay on the node object...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per huddle convo, I've added some comments for now and we'll plan to make this more consistent soon.
core/dbt/adapters/base/impl.py
Outdated
|
||
@available | ||
@classmethod | ||
def render_raw_model_constraints(cls, raw_constraints: Dict[str, Any]) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the return type be a List here? Probably an Optional[List]
?
def render_raw_model_constraints(cls, raw_constraints: Dict[str, Any]) -> str: | |
def render_raw_model_constraints(cls, raw_constraints: Dict[str, Any]) -> List: |
|
||
@classmethod | ||
def render_raw_model_constraint(cls, raw_constraint: Dict[str, Any]) -> Optional[str]: | ||
constraint = cls._parse_model_constraint(raw_constraint) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incidentally, I do love the design here since we get to use an enumerated type instead of strings ✨
{%- for c in constraints -%} | ||
{%- set constraint_str = adapter.render_raw_column_constraint(c) -%} | ||
{%- if constraint_str -%} | ||
{{ ' ' ~ constraint_str }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: WS
assert len(results) == 1 | ||
generated_sql = read_file("target", "run", "test", "models", "my_model.sql") | ||
generated_sql_list = _normalize_whitespace(generated_sql).split(" ") | ||
generated_sql_list = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This is repeating the same variable name with different semantics and it just strikes me is something we might want to clean up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some syntax-related clean up nits
resolves #6754
Description
Adds code to parse and validate constraints on models. Implements default (and postgres) adapter support for model level constraint rendering.
Checklist
changie new
to create a changelog entry