diff options
Diffstat (limited to 'test/engine')
| -rw-r--r-- | test/engine/test_execute.py | 91 | ||||
| -rw-r--r-- | test/engine/test_logging.py | 119 |
2 files changed, 192 insertions, 18 deletions
diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index 196b70340..2940a1e7f 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -3736,7 +3736,7 @@ class DialectEventTest(fixtures.TestBase): class SetInputSizesTest(fixtures.TablesTest): __backend__ = True - __requires__ = ("independent_connections",) + __requires__ = ("independent_connections", "insert_returning") @classmethod def define_tables(cls, metadata): @@ -3752,15 +3752,6 @@ class SetInputSizesTest(fixtures.TablesTest): canary = mock.Mock() def do_set_input_sizes(cursor, list_of_tuples, context): - if not engine.dialect.positional: - # sort by "user_id", "user_name", or otherwise - # param name for a non-positional dialect, so that we can - # confirm the ordering. mostly a py2 thing probably can't - # occur on py3.6+ since we are passing dictionaries with - # "user_id", "user_name" - list_of_tuples = sorted( - list_of_tuples, key=lambda elem: elem[0] - ) canary.do_set_input_sizes(cursor, list_of_tuples, context) def pre_exec(self): @@ -3786,15 +3777,21 @@ class SetInputSizesTest(fixtures.TablesTest): ): yield engine, canary - def test_set_input_sizes_no_event(self, input_sizes_fixture): + @testing.requires.insertmanyvalues + def test_set_input_sizes_insertmanyvalues_no_event( + self, input_sizes_fixture + ): engine, canary = input_sizes_fixture with engine.begin() as conn: conn.execute( - self.tables.users.insert(), + self.tables.users.insert().returning( + self.tables.users.c.user_id + ), [ {"user_id": 1, "user_name": "n1"}, {"user_id": 2, "user_name": "n2"}, + {"user_id": 3, "user_name": "n3"}, ], ) @@ -3805,6 +3802,58 @@ class SetInputSizesTest(fixtures.TablesTest): mock.ANY, [ ( + "user_id_0", + mock.ANY, + testing.eq_type_affinity(Integer), + ), + ( + "user_name_0", + mock.ANY, + testing.eq_type_affinity(String), + ), + ( + "user_id_1", + mock.ANY, + testing.eq_type_affinity(Integer), + ), + ( + "user_name_1", + mock.ANY, + testing.eq_type_affinity(String), + ), + ( + "user_id_2", + mock.ANY, + testing.eq_type_affinity(Integer), + ), + ( + "user_name_2", + mock.ANY, + testing.eq_type_affinity(String), + ), + ], + mock.ANY, + ) + ], + ) + + def test_set_input_sizes_no_event(self, input_sizes_fixture): + engine, canary = input_sizes_fixture + + with engine.begin() as conn: + conn.execute( + self.tables.users.update() + .where(self.tables.users.c.user_id == 15) + .values(user_id=15, user_name="n1"), + ) + + eq_( + canary.mock_calls, + [ + call.do_set_input_sizes( + mock.ANY, + [ + ( "user_id", mock.ANY, testing.eq_type_affinity(Integer), @@ -3814,6 +3863,11 @@ class SetInputSizesTest(fixtures.TablesTest): mock.ANY, testing.eq_type_affinity(String), ), + ( + "user_id_1", + mock.ANY, + testing.eq_type_affinity(Integer), + ), ], mock.ANY, ) @@ -3924,11 +3978,9 @@ class SetInputSizesTest(fixtures.TablesTest): with engine.begin() as conn: conn.execute( - self.tables.users.insert(), - [ - {"user_id": 1, "user_name": "n1"}, - {"user_id": 2, "user_name": "n2"}, - ], + self.tables.users.update() + .where(self.tables.users.c.user_id == 15) + .values(user_id=15, user_name="n1"), ) eq_( @@ -3947,6 +3999,11 @@ class SetInputSizesTest(fixtures.TablesTest): (SPECIAL_STRING, None, 0), testing.eq_type_affinity(String), ), + ( + "user_id_1", + mock.ANY, + testing.eq_type_affinity(Integer), + ), ], mock.ANY, ) diff --git a/test/engine/test_logging.py b/test/engine/test_logging.py index 38e1c436c..b1c487631 100644 --- a/test/engine/test_logging.py +++ b/test/engine/test_logging.py @@ -4,6 +4,7 @@ import re import sqlalchemy as tsa from sqlalchemy import bindparam from sqlalchemy import Column +from sqlalchemy import Integer from sqlalchemy import MetaData from sqlalchemy import or_ from sqlalchemy import select @@ -31,7 +32,9 @@ class LogParamsTest(fixtures.TestBase): __requires__ = ("ad_hoc_engines",) def setup_test(self): - self.eng = engines.testing_engine(options={"echo": True}) + self.eng = engines.testing_engine( + options={"echo": True, "insertmanyvalues_page_size": 150} + ) self.no_param_engine = engines.testing_engine( options={"echo": True, "hide_parameters": True} ) @@ -45,6 +48,7 @@ class LogParamsTest(fixtures.TestBase): log.addHandler(self.buf) def teardown_test(self): + self.eng = engines.testing_engine(options={"echo": True}) exec_sql(self.eng, "drop table if exists foo") for log in [logging.getLogger("sqlalchemy.engine")]: log.removeHandler(self.buf) @@ -176,6 +180,21 @@ class LogParamsTest(fixtures.TestBase): repr(params), ) + def test_repr_params_huge_named_dict(self): + # given non-multi-params in a list. repr params with + # per-element truncation, mostly does the exact same thing + params = {"key_%s" % i: i for i in range(800)} + eq_( + repr(sql_util._repr_params(params, batches=10, ismulti=False)), + # this assertion is very hardcoded to exactly how many characters + # are in a Python dict repr() for the given name/value scheme + # in the sample dictionary. If for some strange reason + # Python dictionary repr() changes in some way, then this would + # have to be adjusted + f"{repr(params)[0:679]} ... 700 parameters truncated ... " + f"{repr(params)[-799:]}", + ) + def test_repr_params_ismulti_named_dict(self): # given non-multi-params in a list. repr params with # per-element truncation, mostly does the exact same thing @@ -219,6 +238,104 @@ class LogParamsTest(fixtures.TestBase): "298, 299], 5]]", ) + def test_log_insertmanyvalues(self): + """test the full logging for insertmanyvalues added for #6047. + + to make it as clear as possible what's going on, the "insertmanyvalues" + execute is noted explicitly and includes total number of batches, + batch count. The long SQL string as well as the long parameter list + is now truncated in the middle, which is a new logging capability + as of this feature (we had only truncation of many separate parameter + sets and truncation of long individual parameter values, not + a long single tuple/dict of parameters.) + + """ + t = Table( + "t", + MetaData(), + Column("id", Integer, primary_key=True), + Column("data", String), + ) + + with self.eng.begin() as connection: + t.create(connection) + + connection.execute( + t.insert().returning(t.c.id), + [{"data": f"d{i}"} for i in range(327)], + ) + + full_insert = ( + "INSERT INTO t (data) VALUES (?), (?), " + "(?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), " + "(? ... 439 characters truncated ... ?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?) " + "RETURNING id" + ) + eq_(self.buf.buffer[3].message, full_insert) + + eq_regex( + self.buf.buffer[4].message, + r"\[generated in .* \(insertmanyvalues\)\] \('d0', 'd1', " + r"'d2', 'd3', 'd4', 'd5', 'd6', 'd7', " + r"'d8', 'd9', 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', " + r"'d16', 'd17', 'd18', 'd19', 'd20', 'd21', 'd22', 'd23', " + r"'d24', 'd25', 'd26', 'd27', 'd28', 'd29', 'd30', " + r"'d31', 'd32', 'd33', 'd34', 'd35', 'd36', 'd37', 'd38', " + r"'d39', 'd40', 'd41', 'd42', 'd43', 'd44', 'd45', 'd46', " + r"'d47', 'd48', " + r"'d49' ... 50 parameters truncated ... " + r"'d100', 'd101', 'd102', 'd103', 'd104', 'd105', 'd106', " + r"'d107', 'd108', 'd109', 'd110', 'd111', 'd112', 'd113', " + r"'d114', 'd115', 'd116', 'd117', 'd118', 'd119', 'd120', " + r"'d121', 'd122', 'd123', 'd124', 'd125', 'd126', 'd127', " + r"'d128', 'd129', 'd130', 'd131', 'd132', 'd133', 'd134', " + r"'d135', 'd136', 'd137', 'd138', 'd139', 'd140', " + r"'d141', 'd142', " + r"'d143', 'd144', 'd145', 'd146', 'd147', 'd148', 'd149'\)", + ) + eq_(self.buf.buffer[5].message, full_insert) + eq_( + self.buf.buffer[6].message, + "[insertmanyvalues batch 2 of 3] ('d150', 'd151', 'd152', " + "'d153', 'd154', 'd155', 'd156', 'd157', 'd158', 'd159', " + "'d160', 'd161', 'd162', 'd163', 'd164', 'd165', 'd166', " + "'d167', 'd168', 'd169', 'd170', 'd171', 'd172', 'd173', " + "'d174', 'd175', 'd176', 'd177', 'd178', 'd179', 'd180', " + "'d181', 'd182', 'd183', 'd184', 'd185', 'd186', 'd187', " + "'d188', 'd189', 'd190', 'd191', 'd192', 'd193', 'd194', " + "'d195', 'd196', 'd197', 'd198', 'd199' " + "... 50 parameters truncated ... 'd250', 'd251', 'd252', " + "'d253', 'd254', 'd255', 'd256', 'd257', 'd258', 'd259', " + "'d260', 'd261', 'd262', 'd263', 'd264', 'd265', 'd266', " + "'d267', 'd268', 'd269', 'd270', 'd271', 'd272', 'd273', " + "'d274', 'd275', 'd276', 'd277', 'd278', 'd279', 'd280', " + "'d281', 'd282', 'd283', 'd284', 'd285', 'd286', 'd287', " + "'d288', 'd289', 'd290', 'd291', 'd292', 'd293', 'd294', " + "'d295', 'd296', 'd297', 'd298', 'd299')", + ) + eq_( + self.buf.buffer[7].message, + "INSERT INTO t (data) VALUES (?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), (?), (?), (?), (?), (?), (?), " + "(?), (?), (?), (?), (?), (?) RETURNING id", + ) + eq_( + self.buf.buffer[8].message, + "[insertmanyvalues batch 3 of 3] ('d300', 'd301', 'd302', " + "'d303', 'd304', 'd305', 'd306', 'd307', 'd308', 'd309', " + "'d310', 'd311', 'd312', 'd313', 'd314', 'd315', 'd316', " + "'d317', 'd318', 'd319', 'd320', 'd321', 'd322', 'd323', " + "'d324', 'd325', 'd326')", + ) + def test_log_large_parameter_single(self): import random |
