Skip to content

Commit e129eed

Browse files
author
Saurabh Badenkal
committed
fix: address 5 Copilot review comments (round 2)
- sql_joins(): fix docstring example to not use aliases with join_clause (join_clause uses full table name; use sql_join() for aliased queries) - sql_examples: replace _value JOIN pattern with sql_join() helper - list_table_relationships: docstring now mentions ManyToOne - remove unused VALIDATION_SQL_CROSS_JOIN_BLOCKED import - PR description: cartesian updated from ValidationError to UserWarning
1 parent 56f19b8 commit e129eed

4 files changed

Lines changed: 28 additions & 22 deletions

File tree

examples/advanced/sql_examples.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -408,22 +408,21 @@ def _run_examples(client):
408408
# 13. INNER JOIN
409409
# ==============================================================
410410
heading(13, "SQL -- INNER JOIN")
411-
print("Use the lookup column's logical name (e.g. _new_teamid_value).")
412-
413-
child_cols = backoff(lambda: client.tables.list_columns(child_table, select=["LogicalName", "AttributeType"]))
414-
lookup_col = "_new_teamid_value"
415-
for c in child_cols:
416-
ln = c.get("LogicalName", "")
417-
if "teamid" in ln.lower() and ln.startswith("_"):
418-
lookup_col = ln
419-
break
411+
print("Use the lookup attribute's logical name (e.g. new_teamid) for JOINs.")
412+
413+
# Use sql_join() to auto-discover the relationship and build
414+
# the JOIN clause with proper aliases.
415+
lookup_col = "new_teamid" # Lookup logical name, NOT _..._value
416+
join_clause = client.query.sql_join(
417+
from_table=child_table,
418+
to_table=parent_table,
419+
from_alias="tk",
420+
to_alias="t",
421+
)
420422
print(f"[INFO] Lookup column: {lookup_col}")
423+
print(f"[INFO] Generated JOIN: {join_clause}")
421424

422-
sql = (
423-
f"SELECT t.new_code, tk.new_title, tk.new_hours "
424-
f"FROM {child_table} tk "
425-
f"INNER JOIN {parent_table} t ON tk.{lookup_col} = t.{parent_id_col}"
426-
)
425+
sql = f"SELECT t.new_code, tk.new_title, tk.new_hours " f"FROM {child_table} tk " f"{join_clause}"
427426
log_call('client.query.sql("...INNER JOIN...")')
428427
try:
429428
results = backoff(lambda: client.query.sql(sql))
@@ -443,7 +442,7 @@ def _run_examples(client):
443442
f"SELECT t.new_code, tk.new_title "
444443
f"FROM {parent_table} t "
445444
f"LEFT JOIN {child_table} tk ON t.{parent_id_col} = tk.{lookup_col}"
446-
)
445+
) # lookup_col = logical name, NOT _..._value
447446
log_call('client.query.sql("...LEFT JOIN...")')
448447
try:
449448
results = backoff(lambda: client.query.sql(sql))
@@ -459,7 +458,7 @@ def _run_examples(client):
459458
f"SELECT t.new_code, COUNT(tk.new_sqldemotaskid) as task_count, "
460459
f"SUM(tk.new_hours) as total_hours "
461460
f"FROM {parent_table} t "
462-
f"JOIN {child_table} tk ON t.{parent_id_col} = tk.{lookup_col} "
461+
f"JOIN {child_table} tk ON t.{parent_id_col} = tk.{lookup_col} " # logical name
463462
f"GROUP BY t.new_code"
464463
)
465464
log_call('client.query.sql("...JOIN...GROUP BY...")')

src/PowerPlatform/Dataverse/data/_odata.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
VALIDATION_SQL_NOT_STRING,
3232
VALIDATION_SQL_EMPTY,
3333
VALIDATION_SQL_WRITE_BLOCKED,
34-
VALIDATION_SQL_CROSS_JOIN_BLOCKED,
3534
VALIDATION_SQL_UNSUPPORTED_SYNTAX,
3635
METADATA_ENTITYSET_NOT_FOUND,
3736
METADATA_ENTITYSET_NAME_MISSING,

src/PowerPlatform/Dataverse/operations/query.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,23 @@ def sql_joins(
292292
293293
:rtype: list[dict[str, typing.Any]]
294294
295+
.. note::
296+
297+
The ``join_clause`` value references the source table by its
298+
**full name** (e.g. ``ON contact.col = ...``), so the FROM
299+
clause must also use the unaliased table name. For queries
300+
that need aliases, use :meth:`sql_join` instead.
301+
295302
Example::
296303
297304
joins = client.query.sql_joins("contact")
298305
for j in joins:
299306
print(f"{j['column']:30s} -> {j['target']}.{j['target_pk']}")
300307
print(f" {j['join_clause']}")
301308
302-
# Use in a query
309+
# Use in a query (no alias on the FROM table)
303310
j = next(j for j in joins if j['target'] == 'account')
304-
sql = f"SELECT TOP 10 c.fullname, a.name FROM contact c {j['join_clause']}"
311+
sql = f"SELECT TOP 10 contact.fullname, a.name FROM contact {j['join_clause']}"
305312
"""
306313
table_lower = table.lower()
307314
rels = self._client.tables.list_table_relationships(table)

src/PowerPlatform/Dataverse/operations/tables.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -813,9 +813,10 @@ def list_table_relationships(
813813
) -> List[Dict[str, Any]]:
814814
"""List all relationships for a specific table.
815815
816-
Combines one-to-many and many-to-many relationships for the given
817-
table by querying both
818-
``EntityDefinitions({id})/OneToManyRelationships`` and
816+
Combines one-to-many, many-to-one, and many-to-many relationships
817+
for the given table by querying
818+
``EntityDefinitions({id})/OneToManyRelationships``,
819+
``EntityDefinitions({id})/ManyToOneRelationships``, and
819820
``EntityDefinitions({id})/ManyToManyRelationships``.
820821
821822
:param table: Schema name of the table (e.g. ``"account"``).

0 commit comments

Comments
 (0)