Skip to content

Insert multiple rows fails when using a column's default value #3288

@sqlalchemy-bot

Description

@sqlalchemy-bot

Migrated issue, originally created by Matthew Booth

OpenStack's oslo.db contains a TimestampMixin, which defines a created_at column:

created_at = Column(DateTime, default=lambda: timeutils.utcnow())

Attempting to insert multiple rows without explicitly specifying this column will fail. e.g.:

DBError: (sqlite3.ProgrammingError) Incorrect number of bindings supplied. The current statement uses 20, and there are 19 supplied. [SQL: u'INSERT INTO fixed_ips (created_at, deleted, address, network_id, virtual_interface_id, instance_uuid, allocated, leased, reserved, host) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'] [parameters: ('2015-01-13 14:04:51.046323', 0, '192.168.1.5', 1, None, 'd4473f3a-dbb9-43ce-a30b-51a4583ead5f', 0, 0, 0, '127.0.0.1', 0, '192.168.1.6', 2, None, 'd4473f3a-dbb9-43ce-a30b-51a4583ead5f', 1, 0, 0, 'localhost')]

In the above case, the caller is not passing a value for created_at:

    params = [
        {'reserved': False, 'deleted': 0, 'leased': False,
         'host': '127.0.0.1', 'address': address_1, 'allocated': False,
         'instance_uuid': instance_uuid, 'network_id': network_id_1,
         'virtual_interface_id': None},
        {'reserved': False, 'deleted': 0, 'leased': False,
         'host': 'localhost', 'address': address_2, 'allocated': True,
         'instance_uuid': instance_uuid, 'network_id': network_id_2,
         'virtual_interface_id': None}
    ]
    ... openstack stuff ...
    tab = models.FixedIp.__table__
    insert = tab.insert().values(ips)
    session.execute(insert)

The cause seems to be in _extend_values_for_multiparams:

values.extend(
    [
        (
            c,
            (_create_bind_param(
                compiler, c, row[c.key],
                name="%s_%d" % (c.key, i + 1)
            ) if elements._is_literal(row[c.key])
                else compiler.process(
                    row[c.key].self_group(), **kw))
            if c.key in row else param
        )
        for (c, param) in values_0
    ]
    for i, row in enumerate(stmt.parameters[1:])
)

Note how the tricky innermost code only calls _create_bind_param() 'if c.key in row'. In the above case, created_at is not in the statement parameters. The first row is handled separately, and correctly adds a bind parameter for created_at, hence the error message complains about having only 19 bind values.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions