Skip to content
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

better with_alias handling, including proper flattening when the expression accessed a single table #730

Merged
merged 3 commits into from
Feb 25, 2025

Conversation

laundmo
Copy link
Contributor

@laundmo laundmo commented Feb 24, 2025

Partially fixes:

This PR changes the handling of aliases, specifically, a dot-less with_alias("name") now figures out which table(s) it originates from. If its a single table, it acts as if .with_alias("table.name") was provided, adding itself to the detected tables dictionary.

This is still incompatible with older versions for more complex queries, as can be seen with the second commit, how it fixes the subselect unittests. Specifically, due to a blank alias still getting a inferred table, queries which access multiple tables won't have their aliases at the "root" level of the Row nesting.

The behavior for dotted .with_alias("table.name") is the same as before.

The behavior for non-expression "AS" is restored to pre-16c9395 state, with it just putting the parsed aliased at the root of the row besides, of course, inside _extras

Script which i used to test various cases, far more than the unittests

from pydal import DAL, Field
from pydal.objects import Expression

db = DAL("sqlite:memory")
db.define_table(
    "test",
    Field("a", type="integer"),
)
db.define_table(
    "test2",
    Field("b", type="integer"),
)
for i in range(3):
    db.test.insert(a=i)
    db.test2.insert(b=i + 4)
db.commit()


def cases(expr):
    print(db(db.test)._select(db.test.id, expr, limitby=(1, 2)))
    noalias = db(db.test).select(db.test.id, expr, limitby=(1, 2))
    partial_col_alias = db(db.test).select(
        db.test.id, expr.with_alias("colalias"), limitby=(1, 2)
    )
    partial_tbl_col_alias = db(db.test).select(
        db.test.id, expr.with_alias("test.colalias"), limitby=(1, 2)
    )
    partial_newtbl_col_alias = db(db.test).select(
        db.test.id, expr.with_alias("data.colalias"), limitby=(1, 2)
    )
    all_col_alias = db(db.test).select(
        db.test.id.with_alias("id"),
        expr.with_alias("colalias"),
        limitby=(1, 2),
    )
    all_col_tbl_alias = db(db.test).select(
        db.test.id.with_alias("test.id"),
        expr.with_alias("test.colalias"),
        limitby=(1, 2),
    )
    all_col_tbl_different_alias = db(db.test).select(
        db.test.id.with_alias("data.id"),
        expr.with_alias("data.colalias"),
        limitby=(1, 2),
    )
    all_mixed_alias = db(db.test).select(
        db.test.id.with_alias("id"),
        expr.with_alias("test.colalias"),
        limitby=(1, 2),
    )

    all_mixed_newtbl_alias = db(db.test).select(
        db.test.id.with_alias("id"),
        expr.with_alias("data.colalias"),
        limitby=(1, 2),
    )
    return [
        ("noalias", noalias),
        ("partial_col_alias", partial_col_alias),
        ("partial_tbl_col_alias", partial_tbl_col_alias),
        ("partial_newtbl_col_alias", partial_newtbl_col_alias),
        ("all_col_alias", all_col_alias),
        ("all_col_tbl_alias", all_col_tbl_alias),
        ("all_col_tbl_different_alias", all_col_tbl_different_alias),
        ("all_mixed_alias", all_mixed_alias),
        ("all_mixed_newtbl_alias", all_mixed_newtbl_alias),
    ]


# same table, simple field alias
for n, case in cases(db.test.a):
    print("same table col", n)
    print("raw data:", case.records[0].__dict__)
    print("as_dict():", case.as_dict())
    print()

# different table, simple field alias
for n, case in cases(db.test2.b):
    print("other table col", n)
    print("raw data:", case.records[0].__dict__)
    print("as_dict():", case.as_dict())
    print()


# alias on expression with 2 tables
def addition(a, b, query_env={}):
    return (
        f"{db._adapter.dialect.expand(a, query_env=query_env)}"
        f" + {db._adapter.dialect.expand(b, query_env=query_env)}"
    )


multi_table_expr = Expression(
    db,
    addition,
    db.test.a,
    db.test2.b,
)
for n, case in cases(multi_table_expr):
    print("multi table expr", n)
    print("raw data:", case.records[0].__dict__)
    print("as_dict():", case.as_dict())
    print()

# old behaviour manually parsing alias from string
case = db(db.test.id).select(
    Expression(
        db, lambda _, query_env={}: "(test.a + test2.b) AS colalias", db.test2.id
    )
)
print("raw data:", case.records[0].__dict__)
print("as_dict():", case.as_dict())
print()

@mdipierro
Copy link
Contributor

Thanks for fixing the issue quickly.

@mdipierro mdipierro merged commit fd88958 into web2py:master Feb 25, 2025
5 of 6 checks passed
@laundmo
Copy link
Contributor Author

laundmo commented Mar 10, 2025

hi @mdipierro, i noticed you reverted this PR again recently. Could you share your reasoning?

@mbelletti
Copy link
Contributor

It should be for this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants