diff --git a/setup.py b/setup.py index 510653b8..2a532d9b 100644 --- a/setup.py +++ b/setup.py @@ -67,6 +67,7 @@ "masoniteorm.connections", "masoniteorm.expressions", "masoniteorm.factories", + "masoniteorm.helpers", "masoniteorm.migrations", "masoniteorm.models", "masoniteorm.observers", diff --git a/src/masoniteorm/expressions/expressions.py b/src/masoniteorm/expressions/expressions.py index 77c6b050..c7c345f0 100644 --- a/src/masoniteorm/expressions/expressions.py +++ b/src/masoniteorm/expressions/expressions.py @@ -1,5 +1,7 @@ import inspect +from ..helpers.misc import deprecated + class QueryExpression: """A helper class to manage query expressions.""" @@ -147,7 +149,6 @@ def __init__(self, table, clause="join"): self.alias = None self.clause = clause self.on_clauses = [] - self.where_clauses = [] if " as " in self.table: self.table = table.split(" as ")[0] @@ -161,25 +162,44 @@ def or_on(self, column1, equality, column2): self.on_clauses.append(OnClause(column1, equality, column2, "or")) return self - def where(self, column, *args): - """Specifies a where expression. + def on_value(self, column, *args): + equality, value = self._extract_operator_value(*args) + self.on_clauses += ((OnValueClause(column, equality, value, "value")),) + return self - Arguments: - column {string} -- The name of the column to search + def or_on_value(self, column, *args): + equality, value = self._extract_operator_value(*args) + self.on_clauses += ( + (OnValueClause(column, equality, value, "value", operator="or")), + ) + return self - Keyword Arguments: - args {List} -- The operator and the value of the column to search. (default: {None}) + def on_null(self, column): + """Specifies an ON expression where the column IS NULL. + + Arguments: + column {string} -- The name of the column. Returns: self """ - operator, value = self._extract_operator_value(*args) + self.on_clauses += ((OnValueClause(column, "=", None, "NULL")),) + return self + + def on_not_null(self, column: str): + """Specifies an ON expression where the column IS NOT NULL. + + Arguments: + column {string} -- The name of the column. - self.where_clauses += ((QueryExpression(column, operator, value, "value")),) + Returns: + self + """ + self.on_clauses += ((OnValueClause(column, "=", True, "NOT NULL")),) return self - def where_null(self, column): - """Specifies a where expression where the column is NULL. + def or_on_null(self, column): + """Specifies an ON expression where the column IS NULL. Arguments: column {string} -- The name of the column. @@ -187,11 +207,11 @@ def where_null(self, column): Returns: self """ - self.where_clauses += ((QueryExpression(column, "=", None, "NULL")),) + self.on_clauses += ((OnValueClause(column, "=", None, "NULL", operator="or")),) return self - def where_not_null(self, column: str): - """Specifies a where expression where the column is not NULL. + def or_on_not_null(self, column: str): + """Specifies an ON expression where the column IS NOT NULL. Arguments: column {string} -- The name of the column. @@ -199,11 +219,16 @@ def where_not_null(self, column: str): Returns: self """ - self._wheres += ((QueryExpression(column, "=", True, "NOT NULL")),) + self.on_clauses += ( + (OnValueClause(column, "=", True, "NOT NULL", operator="or")), + ) return self - def _extract_operator_value(self, *args): + @deprecated("Using where() in a Join clause has been superceded by on_value()") + def where(self, column, *args): + return self.on_value(column, *args) + def _extract_operator_value(self, *args): operators = ["=", ">", ">=", "<", "<=", "!=", "<>", "like", "not like"] operator = operators[0] @@ -224,17 +249,9 @@ def _extract_operator_value(self, *args): return operator, value - def where(self, column, *args): - operator, value = self._extract_operator_value(*args) - self.where_clauses.append(QueryExpression(column, operator, value, "value")) - return self - def get_on_clauses(self): return self.on_clauses - def get_where_clauses(self): - return self.where_clauses - class OnClause: def __init__(self, column1, equality, column2, operator="and"): @@ -242,3 +259,27 @@ def __init__(self, column1, equality, column2, operator="and"): self.column2 = column2 self.equality = equality self.operator = operator + + +class OnValueClause: + """A helper class to manage ON expressions in joins with a value.""" + + def __init__( + self, + column, + equality, + value, + value_type="value", + keyword=None, + raw=False, + bindings=(), + operator="and", + ): + self.column = column + self.equality = equality + self.value = value + self.value_type = value_type + self.keyword = keyword + self.raw = raw + self.bindings = bindings + self.operator = operator diff --git a/src/masoniteorm/helpers.py b/src/masoniteorm/helpers.py deleted file mode 100644 index 7ad1ea08..00000000 --- a/src/masoniteorm/helpers.py +++ /dev/null @@ -1,20 +0,0 @@ -import re - -def database_url(url): - regex = re.compile("(?P.*?)://(?P.*?):(?P.*?)@(?P.*?)/(?P.*)") - dic = {} - match = regex.match(url) - user = match.group(2) - host = match.group(4) - hostname = host.split(':')[0] - port = None if ':' not in host else host.split(':')[1] - database = match.group(5) - dic.update({ - "user": user, - "password": match.group(3), - "host": hostname, - "port": port, - "database": database, - }) - - return dic \ No newline at end of file diff --git a/src/masoniteorm/helpers/__init__.py b/src/masoniteorm/helpers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/masoniteorm/helpers/misc.py b/src/masoniteorm/helpers/misc.py new file mode 100644 index 00000000..301de905 --- /dev/null +++ b/src/masoniteorm/helpers/misc.py @@ -0,0 +1,45 @@ +"""Module for miscellaneous helper methods.""" + +import warnings +import re + + +def deprecated(message): + warnings.simplefilter("default", DeprecationWarning) + + def deprecated_decorator(func): + def deprecated_func(*args, **kwargs): + warnings.warn( + "{} is a deprecated function. {}".format(func.__name__, message), + category=DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return deprecated_func + + return deprecated_decorator + + +def database_url(url): + regex = re.compile( + "(?P.*?)://(?P.*?):(?P.*?)@(?P.*?)/(?P.*)" + ) + dic = {} + match = regex.match(url) + user = match.group(2) + host = match.group(4) + hostname = host.split(":")[0] + port = None if ":" not in host else host.split(":")[1] + database = match.group(5) + dic.update( + { + "user": user, + "password": match.group(3), + "host": hostname, + "port": port, + "database": database, + } + ) + + return dic diff --git a/src/masoniteorm/query/grammars/BaseGrammar.py b/src/masoniteorm/query/grammars/BaseGrammar.py index f6c78432..ed53dab2 100644 --- a/src/masoniteorm/query/grammars/BaseGrammar.py +++ b/src/masoniteorm/query/grammars/BaseGrammar.py @@ -6,6 +6,7 @@ SelectExpression, BetweenExpression, JoinClause, + OnClause, ) @@ -81,13 +82,13 @@ def _compile_select(self, qmark=False): .format( columns=self.process_columns(separator=", ", qmark=qmark), table=self.process_table(self.table), + joins=self.process_joins(qmark=qmark), wheres=self.process_wheres(qmark=qmark), limit=self.process_limit(), offset=self.process_offset(), aggregates=self.process_aggregates(), order_by=self.process_order_by(), group_by=self.process_group_by(), - joins=self.process_joins(qmark=qmark), having=self.process_having(), lock=self.process_locks(), ) @@ -99,13 +100,13 @@ def _compile_select(self, qmark=False): .format( columns=self.process_columns(separator=", ", qmark=qmark), table=self.process_table(self.table), + joins=self.process_joins(qmark=qmark), wheres=self.process_wheres(qmark=qmark), limit=self.process_limit(), offset=self.process_offset(), aggregates=self.process_aggregates(), order_by=self.process_order_by(), group_by=self.process_group_by(), - joins=self.process_joins(qmark=qmark), having=self.process_having(), lock=self.process_locks(), ) @@ -241,49 +242,36 @@ def process_joins(self, qmark=False): for join in self._joins: if isinstance(join, JoinClause): on_string = "" - where_string = "" - cause_loop = 1 - for clause in join.get_on_clauses(): - if cause_loop == 1: - keyword = "ON" - else: - keyword = clause.operator.upper() - - on_string += f"{keyword} {self._table_column_string(clause.column1)} {clause.equality} {self._table_column_string(clause.column2)} " - cause_loop += 1 + for clause_idx, clause in enumerate(join.get_on_clauses()): + keyword = clause.operator.upper() if clause_idx else "ON" - where_loop = 1 - - for clause in join.get_where_clauses(): - if where_loop == 1: - keyword = "WHERE" - else: - keyword = "AND" - - if clause.value_type == "NULL": - sql_string = self.where_null_string() - where_string += sql_string.format( - keyword=keyword, column=self.process_column(clause.column) - ) - elif clause.value_type == "NOT NULL": - sql_string = self.where_not_null_string() - where_string += sql_string.format( - keyword=keyword, column=self.process_column(clause.column) - ) + if isinstance(clause, OnClause): + on_string += f"{keyword} {self._table_column_string(clause.column1)} {clause.equality} {self._table_column_string(clause.column2)} " else: - if qmark: - value = "'?'" - self.add_binding(clause.value) + if clause.value_type == "NULL": + sql_string = self.where_null_string() + on_string += sql_string.format( + keyword=keyword, + column=self.process_column(clause.column), + ) + elif clause.value_type == "NOT NULL": + sql_string = self.where_not_null_string() + on_string += sql_string.format( + keyword=keyword, + column=self.process_column(clause.column), + ) else: - value = self._compile_value(clause.value) - where_string += f"{keyword} {self.process_column(clause.column)} {clause.equality} {value} " - where_loop += 1 + if qmark: + value = "'?'" + self.add_binding(clause.value) + else: + value = self._compile_value(clause.value) + on_string += f"{keyword} {self._table_column_string(clause.column)} {clause.equality} {value} " sql += self.join_string().format( foreign_table=self.process_table(join.table), alias=f" AS {self.process_table(join.alias)}" if join.alias else "", on=on_string, - wheres=f" {where_string}", keyword=self.join_keywords[join.clause], ) sql += " " diff --git a/src/masoniteorm/query/grammars/MSSQLGrammar.py b/src/masoniteorm/query/grammars/MSSQLGrammar.py index 60027721..c147f39b 100644 --- a/src/masoniteorm/query/grammars/MSSQLGrammar.py +++ b/src/masoniteorm/query/grammars/MSSQLGrammar.py @@ -90,7 +90,7 @@ def additional_where_string(self): return "AND" def join_string(self): - return "{keyword} {foreign_table}{alias} {on}{wheres}" + return "{keyword} {foreign_table}{alias} {on}" def aggregate_string(self): return "{aggregate_function}({column}) AS {alias}" @@ -114,7 +114,7 @@ def value_equal_string(self): return "{keyword} {value1} = {value2}" def where_null_string(self): - return "{keyword} {column} IS NULL" + return " {keyword} {column} IS NULL" def between_string(self): return "{keyword} {column} BETWEEN {low} AND {high}" @@ -123,7 +123,7 @@ def not_between_string(self): return "{keyword} {column} NOT BETWEEN {low} AND {high}" def where_not_null_string(self): - return "{keyword} {column} IS NOT NULL" + return " {keyword} {column} IS NOT NULL" def where_string(self): return " {keyword} {column} {equality} {value}" diff --git a/src/masoniteorm/query/grammars/MySQLGrammar.py b/src/masoniteorm/query/grammars/MySQLGrammar.py index b6d434f1..621e3651 100644 --- a/src/masoniteorm/query/grammars/MySQLGrammar.py +++ b/src/masoniteorm/query/grammars/MySQLGrammar.py @@ -167,7 +167,7 @@ def value_string(self): return "'{value}'{separator}" def join_string(self): - return "{keyword} {foreign_table}{alias} {on}{wheres}" + return "{keyword} {foreign_table}{alias} {on}" def limit_string(self, offset=False): return "LIMIT {limit}" @@ -200,7 +200,7 @@ def having_equality_string(self): return "HAVING {column} {equality} {value}" def where_null_string(self): - return "{keyword} {column} IS NULL" + return " {keyword} {column} IS NULL" def where_not_null_string(self): return " {keyword} {column} IS NOT NULL" diff --git a/src/masoniteorm/query/grammars/PostgresGrammar.py b/src/masoniteorm/query/grammars/PostgresGrammar.py index 12cc359e..df07985f 100644 --- a/src/masoniteorm/query/grammars/PostgresGrammar.py +++ b/src/masoniteorm/query/grammars/PostgresGrammar.py @@ -156,7 +156,7 @@ def value_string(self): return "'{value}'{separator}" def join_string(self): - return "{keyword} {foreign_table}{alias} {on}{wheres}" + return "{keyword} {foreign_table}{alias} {on}" def limit_string(self, offset=False): return "LIMIT {limit}" @@ -189,7 +189,7 @@ def having_equality_string(self): return "HAVING {column} {equality} {value}" def where_null_string(self): - return "{keyword} {column} IS NULL" + return " {keyword} {column} IS NULL" def where_not_null_string(self): return " {keyword} {column} IS NOT NULL" diff --git a/src/masoniteorm/query/grammars/SQLiteGrammar.py b/src/masoniteorm/query/grammars/SQLiteGrammar.py index 22287b88..aa420b96 100644 --- a/src/masoniteorm/query/grammars/SQLiteGrammar.py +++ b/src/masoniteorm/query/grammars/SQLiteGrammar.py @@ -145,7 +145,7 @@ def value_string(self): return "'{value}'{separator}" def join_string(self): - return "{keyword} {foreign_table}{alias} {on}{wheres}" + return "{keyword} {foreign_table}{alias} {on}" def limit_string(self, offset=False): return "LIMIT {limit}" @@ -175,7 +175,7 @@ def having_equality_string(self): return "HAVING {column} {equality} {value}" def where_null_string(self): - return "{keyword} {column} IS NULL" + return " {keyword} {column} IS NULL" def value_equal_string(self): return "{keyword} {value1} = {value2}" diff --git a/src/masoniteorm/testing/BaseTestCaseSelectGrammar.py b/src/masoniteorm/testing/BaseTestCaseSelectGrammar.py index 74f42d76..f34882cd 100644 --- a/src/masoniteorm/testing/BaseTestCaseSelectGrammar.py +++ b/src/masoniteorm/testing/BaseTestCaseSelectGrammar.py @@ -290,11 +290,24 @@ def test_can_compile_join_clause(self): )() self.assertEqual(to_sql, sql) - def test_can_compile_join_clause_with_null_where(self): + def test_can_compile_join_clause_with_value(self): clause = ( JoinClause("report_groups as rg") - .on("bgt.fund", "=", "rg.fund") - .where_null("bgt") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + to_sql = self.builder.join(clause).to_sql() + + sql = getattr( + self, inspect.currentframe().f_code.co_name.replace("test_", "") + )() + self.assertEqual(to_sql, sql) + + def test_can_compile_join_clause_with_null(self): + clause = ( + JoinClause("report_groups as rg") + .on_null("bgt.acct") + .or_on_not_null("bgt.dept") ) to_sql = self.builder.join(clause).to_sql() @@ -306,7 +319,7 @@ def test_can_compile_join_clause_with_null_where(self): def test_can_compile_join_clause_with_lambda(self): to_sql = self.builder.join( "report_groups as rg", - lambda clause: (clause.on("bgt.fund", "=", "rg.fund").where_null("bgt")), + lambda clause: (clause.on("bgt.fund", "=", "rg.fund").on_null("bgt")), ).to_sql() sql = getattr( diff --git a/tests/helpers/test_helpers.py b/tests/helpers/test_helpers.py index 3dc1d566..9a18e62e 100644 --- a/tests/helpers/test_helpers.py +++ b/tests/helpers/test_helpers.py @@ -1,5 +1,5 @@ import unittest -from src.masoniteorm.helpers import database_url +from src.masoniteorm.helpers.misc import database_url class TestHelper(unittest.TestCase): diff --git a/tests/mssql/grammar/test_mssql_select_grammar.py b/tests/mssql/grammar/test_mssql_select_grammar.py index 3654a196..eb27bcc6 100644 --- a/tests/mssql/grammar/test_mssql_select_grammar.py +++ b/tests/mssql/grammar/test_mssql_select_grammar.py @@ -303,30 +303,52 @@ def where_not_like(self): def can_compile_join_clause(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on("bgt.fund", "=", "rg.fund") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ return "SELECT * FROM [users] INNER JOIN [report_groups] AS [rg] ON [bgt].[fund] = [rg].[fund] AND [bgt].[dept] = [rg].[dept] AND [bgt].[acct] = [rg].[acct] AND [bgt].[sub] = [rg].[sub]" - def can_compile_join_clause_with_where(self): + def can_compile_join_clause_with_value(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ - return "SELECT * FROM [users] INNER JOIN [report_groups] AS [rg] ON [bgt].[fund] = [rg].[fund] WHERE [bgt] = '1'" + return "SELECT * FROM [users] INNER JOIN [report_groups] AS [rg] ON [bgt].[active] = '1' OR [bgt].[acct] = '1234'" - def can_compile_join_clause_with_null_where(self): + def can_compile_join_clause_with_null(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_null("bgt.acct") + .or_on_not_null("bgt.dept") + ) + builder.join(clause).to_sql() """ - return "SELECT * FROM [users] INNER JOIN [report_groups] AS [rg] ON [bgt].[fund] = [rg].[fund] WHERE [bgt] IS NULL" + return "SELECT * FROM [users] INNER JOIN [report_groups] AS [rg] ON [acct] IS NULL OR [dept] IS NOT NULL" def can_compile_join_clause_with_lambda(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + builder.join( + "report_groups as rg", + lambda clause: ( + clause.on("bgt.fund", "=", "rg.fund") + .on_null("bgt") + ), + ).to_sql() """ - return "SELECT * FROM [users] INNER JOIN [report_groups] AS [rg] ON [bgt].[fund] = [rg].[fund] WHERE [bgt] IS NULL" + return "SELECT * FROM [users] INNER JOIN [report_groups] AS [rg] ON [bgt].[fund] = [rg].[fund] AND [bgt] IS NULL" def shared_lock(self): """ diff --git a/tests/mysql/grammar/test_mysql_select_grammar.py b/tests/mysql/grammar/test_mysql_select_grammar.py index 8bc5f2b3..b1091fc7 100644 --- a/tests/mysql/grammar/test_mysql_select_grammar.py +++ b/tests/mysql/grammar/test_mysql_select_grammar.py @@ -290,37 +290,59 @@ def where_not_like(self): def where_like(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + builder.where("age", "like", "%name%").to_sql() """ return "SELECT * FROM `users` WHERE `users`.`age` LIKE '%name%'" def can_compile_join_clause(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on("bgt.fund", "=", "rg.fund") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ return "SELECT * FROM `users` INNER JOIN `report_groups` AS `rg` ON `bgt`.`fund` = `rg`.`fund` AND `bgt`.`dept` = `rg`.`dept` AND `bgt`.`acct` = `rg`.`acct` AND `bgt`.`sub` = `rg`.`sub`" - def can_compile_join_clause_with_where(self): + def can_compile_join_clause_with_value(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ - return "SELECT * FROM `users` INNER JOIN `report_groups` AS `rg` ON `bgt`.`fund` = `rg`.`fund` WHERE `bgt` = '1'" + return "SELECT * FROM `users` INNER JOIN `report_groups` AS `rg` ON `bgt`.`active` = '1' OR `bgt`.`acct` = '1234'" - def can_compile_join_clause_with_null_where(self): + def can_compile_join_clause_with_null(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_null("bgt.acct") + .or_on_not_null("bgt.dept") + ) + builder.join(clause).to_sql() """ - return "SELECT * FROM `users` INNER JOIN `report_groups` AS `rg` ON `bgt`.`fund` = `rg`.`fund` WHERE `bgt` IS NULL" + return "SELECT * FROM `users` INNER JOIN `report_groups` AS `rg` ON `acct` IS NULL OR `dept` IS NOT NULL" def can_compile_join_clause_with_lambda(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + builder.join( + "report_groups as rg", + lambda clause: ( + clause.on("bgt.fund", "=", "rg.fund") + .on_null("bgt") + ), + ).to_sql() """ - return "SELECT * FROM `users` INNER JOIN `report_groups` AS `rg` ON `bgt`.`fund` = `rg`.`fund` WHERE `bgt` IS NULL" + return "SELECT * FROM `users` INNER JOIN `report_groups` AS `rg` ON `bgt`.`fund` = `rg`.`fund` AND `bgt` IS NULL" def shared_lock(self): """ diff --git a/tests/postgres/grammar/test_select_grammar.py b/tests/postgres/grammar/test_select_grammar.py index b7c5115f..7e29b0b5 100644 --- a/tests/postgres/grammar/test_select_grammar.py +++ b/tests/postgres/grammar/test_select_grammar.py @@ -292,37 +292,59 @@ def where_not_like(self): def where_like(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + builder.where("age", "like", "%name%").to_sql() """ return """SELECT * FROM "users" WHERE "users"."age" ILIKE '%name%'""" def can_compile_join_clause(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on("bgt.fund", "=", "rg.fund") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" AND "bgt"."dept" = "rg"."dept" AND "bgt"."acct" = "rg"."acct" AND "bgt"."sub" = "rg"."sub\"""" - def can_compile_join_clause_with_where(self): + def can_compile_join_clause_with_value(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ - return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" WHERE "bgt" = '1'""" + return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."active" = '1' OR "bgt"."acct" = '1234'""" - def can_compile_join_clause_with_null_where(self): + def can_compile_join_clause_with_null(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_null("bgt.acct") + .or_on_not_null("bgt.dept") + ) + builder.join(clause).to_sql() """ - return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" WHERE "bgt" IS NULL""" + return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "acct" IS NULL OR "dept" IS NOT NULL""" def can_compile_join_clause_with_lambda(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + builder.join( + "report_groups as rg", + lambda clause: ( + clause.on("bgt.fund", "=", "rg.fund") + .on_null("bgt") + ), + ).to_sql() """ - return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" WHERE "bgt" IS NULL""" + return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" AND "bgt" IS NULL""" def shared_lock(self): """ diff --git a/tests/sqlite/grammar/test_sqlite_select_grammar.py b/tests/sqlite/grammar/test_sqlite_select_grammar.py index 5f5b27e7..c297c636 100644 --- a/tests/sqlite/grammar/test_sqlite_select_grammar.py +++ b/tests/sqlite/grammar/test_sqlite_select_grammar.py @@ -292,37 +292,59 @@ def where_not_like(self): def where_like(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + builder.where("age", "like", "%name%").to_sql() """ return """SELECT * FROM "users" WHERE "users"."age" LIKE '%name%'""" def can_compile_join_clause(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on("bgt.fund", "=", "rg.fund") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" AND "bgt"."dept" = "rg"."dept" AND "bgt"."acct" = "rg"."acct" AND "bgt"."sub" = "rg"."sub\"""" - def can_compile_join_clause_with_where(self): + def can_compile_join_clause_with_value(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_value("bgt.active", "=", "1") + .or_on_value("bgt.acct", "=", "1234") + ) + builder.join(clause).to_sql() """ - return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" WHERE "bgt" = '1'""" + return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."active" = '1' OR "bgt"."acct" = '1234'""" - def can_compile_join_clause_with_null_where(self): + def can_compile_join_clause_with_null(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + clause = ( + JoinClause("report_groups as rg") + .on_null("bgt.acct") + .or_on_not_null("bgt.dept") + ) + builder.join(clause).to_sql() """ - return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" WHERE "bgt" IS NULL""" + return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "acct" IS NULL OR "dept" IS NOT NULL""" def can_compile_join_clause_with_lambda(self): """ builder = self.get_builder() - builder.where("age", "not like", "%name%").to_sql() + builder.join( + "report_groups as rg", + lambda clause: ( + clause.on("bgt.fund", "=", "rg.fund") + .on_null("bgt") + ), + ).to_sql() """ - return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" WHERE "bgt" IS NULL""" + return """SELECT * FROM "users" INNER JOIN "report_groups" AS "rg" ON "bgt"."fund" = "rg"."fund" AND "bgt" IS NULL""" def update_lock(self): """