On 09/30/2016 04:32 PM, Seth P wrote:
On Thursday, September 29, 2016 at 9:45:24 AM UTC-4, Mike Bayer wrote:
you can add your own types to do these things also, especially
read-only, just make any subclass of UserDefinedType and apply whatever
result-row handling is needed for how cx_Oracle is returning the data.
The hard part about types is the elaborate expression support (e.g.
like
JSON foo ->> bar vs. foo -> bar in PG for example). Reading and
writing a value is not that hard and especially if the type is just
specific to what you need right now, you don't have the burden of
making
sure your type works for all versions / flags / settings of Oracle /
cx_Oracle etc.
The following seems to work fine for my purposes. (It uses alchy,
https://github.com/dgilland/alchy).
Is there a way to specify that any column of type VARRAY should always
be bound literally, so that one doesn't need to specify explicitly
.compile(compile_kwargs={"literal_binds": True}) (and thereby literally
binding all fields)?
ha see you *are* doing the write side.
the bind_expression() hook is here to allow you to re-render the
expression. assuming value-bound bindparam() objects (e.g. not like
you'd get with an INSERT or UPDATE usually), the value should be present
and you can do this (had to work up a POC):
from sqlalchemy import *
from sqlalchemy.types import UserDefinedType
class T(UserDefinedType):
def bind_expression(self, colexpr):
return literal_column(colexpr.value) # or whatever is needed here
t = table('t', column('x', T()))
print t.select().where(t.c.x == 'hi')
Also, is there a way, inside VARRAY.__init__() or some other place that
is called before table creation to specify the sa.event.listen(<table>,
"before_create", self.create_ddl().execute_if(dialect='oracle'))?
look into adding SchemaType as a mixin, it signals to the owning Column
that it should receive events. You can then add the events to your
type itself like before_parent_attach which should fire for the Column.
import six
import sqlalchemy as sa
class VARRAY(sa.types.UserDefinedType):
def __init__(self, type_name, size_limit, item_type, nullable=True,
as_tuple=False):
super(VARRAY, self).__init__()
self.type_name = type_name
self.size_limit = size_limit
self.item_type = item_type
self.nullable = nullable
self.as_tuple = as_tuple
def compile(self, dialect=None):
return self.type_name
def get_col_spec(self, **kw):
return self.type_name
def create_ddl(self, dialect=None, or_replace=True):
sql = "CREATE "
if or_replace:
sql += "OR REPLACE "
sql += "TYPE %(schema)s.{} AS VARRAY({}) OF {}".format(self.type_name,
self.size_limit,
self.item_type.compile(dialect=dialect))
if not self.nullable:
sql += " NOT NULL"
return sa.DDL(sql)
def process_literal_param(self, value, dialect):
return "{}({})".format(self.type_name,
','.join("NULL" if x is None else
("'%s'" % x) if isinstance(x, six.string_types) else str(x)
for x in value))
def literal_processor(self, dialect):
def processor(value):
return self.process_literal_param(value, dialect)
return processor
def process_result_value(self, value, dialect):
if self.as_tuple:
value = tuple(value)
return value
def result_processor(self, dialect, coltype):
def processor(value):
return self.process_result_value(value, dialect)
return processor
def copy(self):
return VARRAY(self.type_name, self.size_limit, self.item_type,
nullable=self.nullable, as_tuple=self.as_tuple)
if __name__ == '__main__':
uri = "oracle://user:password@host"
import alchy
import sqlalchemy.dialects.oracle as oc
db = alchy.Manager(config={'SQLALCHEMY_DATABASE_URI': uri})
class TestVarray(db.Model):
__tablename__ = 'test_varray'
__table_args__ = { 'schema': 'myschema' }
idx = sa.Column(sa.Integer, primary_key=True)
label = sa.Column(sa.String(20), nullable=False)
words = sa.Column(VARRAY("tp_test_varray_words", 3000, sa.String(8),
nullable=True), nullable=False)
numbers = sa.Column(VARRAY("tp_test_varray_numbers", 3000, oc.NUMBER(),
nullable=True), nullable=False)
sa.event.listen(TestVarray.__table__, "before_create",
TestVarray.column_attrs()['words'].columns[0].type.create_ddl().execute_if(dialect='oracle'))
sa.event.listen(TestVarray.__table__, "before_create",
TestVarray.column_attrs()['numbers'].columns[0].type.create_ddl().execute_if(dialect='oracle'))
db.drop_all()
db.create_all()
db.engine.execute(TestVarray.__table__.insert({'idx': 1,
'label': 'One',
'words': ['Once', 'upon',
'a', 'time'],
'numbers': [1.1, 1.2]}).
compile(compile_kwargs={"literal_binds": True}))
db.engine.execute(TestVarray.__table__.insert({'idx': 2,
'label': 'Two',
'words': ['To', 'be', 'or',
'not'],
'numbers': [2.1, 2.2]}).
compile(compile_kwargs={"literal_binds": True}))
print TestVarray.query.all()
print db.session().query(TestVarray.label, TestVarray.words,
TestVarray.numbers).all()
[<TestVarray(idx=1, label='One', words=['Once', 'upon', 'a', 'time'],
numbers=[1.1, 1.2])>,
<TestVarray(idx=2, label='Two', words=['To', 'be', 'or', 'not'], numbers=[2.1,
2.2])>]
[('One', ['Once', 'upon', 'a', 'time'], [1.1, 1.2]),
('Two', ['To', 'be', 'or', 'not'], [2.1, 2.2])]
--
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 sqlalchemy+unsubscr...@googlegroups.com
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto:sqlalchemy@googlegroups.com>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.