Thanks. Yes, that's enough for my use-case, more than enough, to be honest.
Out of curiosity, how would you do it for a native ENUM?
Thanks a lot!
On Wednesday, December 4, 2019 at 4:09:36 PM UTC+1, Mike Bayer wrote:
>
>
>
> On Wed, Dec 4, 2019, at 7:57 AM, Hameer Abbasi wrote:
>
> Hello. I've created an encryption wrapper that marshals things in and out
> of encryption, except that for the TypeDecorator the input and output type
> are different. I'm having trouble wrapping float and Enum types while still
> keeping them queryable.
>
> Can someone take a look at the attached reproducer and let me know what I
> can do to fix it up/work around the issue?
>
>
>
> hi -
>
> this is pretty well written, which is good to see. I got your test to
> run and the error is "TypeError: can't escape Type to binary" and that's
> being raised by psycopg2 for me.
>
> your type is actually wrapping a nested type and there are issues in how
> that is working. first off the float needs to be a type instance, using
> self.inner = sqlalchemy.types.to_instance(inner), or otherwise pass a
> Float() explicitly.
>
> then if we look what's coming in, the values are still seen as Binary, not
> as a simple string / float:
>
> (Pdb) parameters
> {'user_type': <psycopg2.extensions.Binary object at 0x7fb602b9d810>,
> 'pgp_sym_encrypt_1': 'PASSPHRASE_USER', 'reputation':
> <psycopg2.extensions.Binary object at 0x7fb602bad6c0>, 'pgp_sym_encrypt_2':
> 'PASSPHRASE_USER'}
>
> next, the type handlers for Enum and Float, of which the former is
> necessary, aren't invoked at all because your "inner" type is not known to
> SQLAlchemy. so we have to add handlers for these as:
>
> def process_bind_param(self, value, dialect):
> proc = self.inner.bind_processor(dialect)
> return proc(value)
>
> def process_result_value(self, value, dialect):
> proc = self.inner.result_processor(dialect, None)
> return proc(value)
>
> but that is still not enough because this TypeDecorator is running the
> BYTEA value processors on top of those in any case, putting the value
> inside of a psycopg2.Binary() which still fails, with the same message but
> different type, "TypeError: can't escape str to binary". so we have to
> also ensure we set the correct type for that nested bind parameter:
>
> def bind_expression(self, bindvalue):
> bindvalue.type = self.inner
> bindvalue = sqlalchemy.cast(bindvalue, sqlalchemy.String)
> return sqlalchemy.func.pgp_sym_encrypt(bindvalue, self.passphrase)
>
>
> but still wrong because String is not instantiated, leading to the wrong
> CAST:
>
> def bind_expression(self, bindvalue):
> bindvalue.type = self.inner
> bindvalue = sqlalchemy.cast(bindvalue, sqlalchemy.String())
> return sqlalchemy.func.pgp_sym_encrypt(bindvalue, self.passphrase)
>
>
> INSERT now succeeds.
>
>
> SELECT fails with
>
> sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedObject) type
> "type" does not exist
> LINE 1: ...ym_decrypt(users.user_type, 'PASSPHRASE_USER') AS type) AS u...
> ^
>
> [SQL: SELECT users.id AS users_id, CAST(pgp_sym_decrypt(users.user_type,
> %(pgp_sym_decrypt_1)s) AS type) AS users_user_type,
> CAST(pgp_sym_decrypt(users.reputation, %(pgp_sym_decrypt_2)s) AS FLOAT) AS
> users_reputation
> FROM users]
>
>
> still a "type" getting in there , what's happening is that your Enum is a
> "native" enum, that is, it expects Postgresql to have a "CREATE TYPE"
> happening in there, but because your TypeDecorator is holding onto that
> Enum, none of that is happening so there is no "type". Hoping you don't
> want to use a native enum in any case here as they are more trouble than
> they're worth; setting the Enum as native=False removes the cumbsersome
> requirement for a "CREATE TYPE":
>
> user_type = sqlalchemy.Column(
> Encrypted(sqlalchemy.types.Enum(Type, native_enum=False),
> PASSPHRASE_USER)
> )
>
>
> and that brings it all up:
>
> class Encrypted(sqlalchemy.types.TypeDecorator):
> impl = postgresql.BYTEA
>
> def __init__(self, inner, passphrase):
> super().__init__()
> self.inner = types.to_instance(inner)
> self.passphrase = passphrase
>
> def process_bind_param(self, value, dialect):
> proc = self.inner.bind_processor(dialect)
> return proc(value)
>
> def process_result_value(self, value, dialect):
> proc = self.inner.result_processor(dialect, None)
> return proc(value)
>
> def bind_expression(self, bindvalue):
> bindvalue.type = self.inner
> bindvalue = sqlalchemy.cast(bindvalue, sqlalchemy.String())
> return sqlalchemy.func.pgp_sym_encrypt(bindvalue, self.passphrase)
>
> def column_expression(self, col):
> value = sqlalchemy.func.pgp_sym_decrypt(col, self.passphrase)
> value = sqlalchemy.cast(value, self.inner)
> return value
>
>
> class User(Base):
> __tablename__ = "users"
>
> id = sqlalchemy.Column(sqlalchemy.types.BigInteger, primary_key=True)
> user_type = sqlalchemy.Column(
> Encrypted(sqlalchemy.types.Enum(Type, native_enum=False),
> PASSPHRASE_USER)
> )
> reputation = sqlalchemy.Column(
> Encrypted(sqlalchemy.types.Float, PASSPHRASE_USER)
> )
>
>
>
>
>
>
>
>
>
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected] <javascript:>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/sqlalchemy/c44677c2-343e-49de-8bfe-0b5cfab70b41%40googlegroups.com
>
> <https://groups.google.com/d/msgid/sqlalchemy/c44677c2-343e-49de-8bfe-0b5cfab70b41%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
>
> *Attachments:*
>
> - repro.py
>
>
>
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/sqlalchemy/38490815-e86a-4059-ab84-5aeb3b90f205%40googlegroups.com.