# Buggy code
def get_objects_permissions(self, objects_ids, permissions=None):
    query = """
        WITH required_object_ids AS (
          VALUES %(objects_ids)s
        )
        SELECT object_id, permission, principal
            FROM required_object_ids JOIN access_control_entries
              ON (object_id = column2)
              %(permissions_condition)s
        ORDER BY column1 ASC;
        """
    obj_ids_values = ",".join(["(%s, '%s')" % t for t in enumerate(objects_ids)])
    safeholders = {"objects_ids": obj_ids_values, "permissions_condition": ""}
    placeholders = {}
    if permissions is not None:
        safeholders["permissions_condition"] = """
              WHERE permission IN :permissions"""
        placeholders["permissions"] = tuple(permissions)

    with self.client.connect(readonly=True) as conn:
        result = conn.execute(query % safeholders, placeholders)
        rows = result.fetchall()

    groupby_id = OrderedDict()
    for row in rows:
        object_id, permission, principal = (
            row["object_id"],
            row["permission"],
            row["principal"],
        )
        permissions = groupby_id.setdefault(object_id, {})
        permissions.setdefault(permission, set()).add(principal)
    return list(groupby_id.values())


# Traceback (KeyError)
http --check-status --form POST http://localhost:8888/v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment attachment@kinto-logo.png --auth user:pass

"POST  /v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment" ? (? ms) u'/buckets/source/collections/source' agent=HTTPie/0.9.6 authn_type=basicauth collection_id=record collection_timestamp=1475232331014 errno=110 exception=Traceback (most recent call last):

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/tweens.py", line 22, in excview_tween

response = handler(request)

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 109, in tm_tween

reraise(*exc_info)

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid_tm/__init__.py", line 95, in tm_tween

manager.commit()

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_manager.py", line 123, in commit

return self.get().commit()

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_transaction.py", line 265, in commit

self._callBeforeCommitHooks()

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/transaction/_transaction.py", line 336, in _callBeforeCommitHooks

hook(*args, **kws)

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/kinto/core/events.py", line 76, in _notify_resource_events_before

request.registry.notify(event)

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/registry.py", line 91, in notify

[ _ for _ in self.subscribers(events, None) ]

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/zope/interface/registry.py", line 442, in subscribers

return self.adapters.subscribers(objects, provided)

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/zope/interface/adapter.py", line 596, in subscribers

subscription(*objects)

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/config/adapters.py", line 130, in subscriber_wrapper

return derived_subscriber(*arg)

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/pyramid/config/adapters.py", line 103, in derived_subscriber

return subscriber(arg[0])

File "/home/travis/gopath/src/github.com/mozilla-services/kinto-dist/.venv/local/lib/python2.7/site-packages/kinto/plugins/history/listener.py", line 65, in on_resource_changed

collection_perms = perms_by_object_id[collection_uri]

KeyError: u'/buckets/source/collections/source' lang=None uid=cbd3731f18c97ebe1d31d9846b5f1b95cf8eeeae586e201277263434041e99d1

"POST  /v1/buckets/source/collections/source/records/80ec9929-6896-4022-8443-3da4f5353f47/attachment" 500 (23 ms) request.summary agent=HTTPie/0.9.6 authn_type=basicauth collection_id=record collection_timestamp=1475232331014 errno=999 lang=None time=2016-09-30T10:45:31 uid=cbd3731f18c97ebe1d31d9846b5f1b95cf8eeeae586e201277263434041e99d1

HTTP/1.1 500 Internal Server Error

Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff

Content-Length: 183

Content-Type: application/json; charset=UTF-8

Date: Fri, 30 Sep 2016 10:45:31 GMT

Server: waitress

{

"code": 500,

"errno": 999,

"error": "Internal Server Error",

"info": "https://github.com/Kinto/kinto/issues/",

"message": "A programmatic error occured, developers have been informed."

}

# Fixed code
def get_objects_permissions(self, objects_ids, permissions=None):
    query = """
        WITH required_object_ids AS (
          VALUES %(objects_ids)s
        )
        SELECT object_id, permission, principal
            FROM required_object_ids JOIN access_control_entries
              ON (object_id = column2)
              %(permissions_condition)s
        ORDER BY column1 ASC;
        """
    obj_ids_values = ",".join(["(%s, '%s')" % t for t in enumerate(objects_ids)])
    safeholders = {"objects_ids": obj_ids_values, "permissions_condition": ""}
    placeholders = {}
    if permissions is not None:
        safeholders["permissions_condition"] = """
              WHERE permission IN :permissions"""
        placeholders["permissions"] = tuple(permissions)

    with self.client.connect(readonly=True) as conn:
        result = conn.execute(query % safeholders, placeholders)
        rows = result.fetchall()

    groupby_id = OrderedDict()
    for object_id in objects_ids:
        groupby_id[object_id] = {}
    for row in rows:
        object_id, permission, principal = (
            row["object_id"],
            row["permission"],
            row["principal"],
        )
        groupby_id[object_id].setdefault(permission, set()).add(principal)
    return list(groupby_id.values())