0

Use Case

I'm using SQLAlchemy and SQLite for a standalone application (non-web) and I need to store numerical ranges in a database. I'm doing this by saving the start and end of each range in SQLite like so:

[[float("-inf"), 0], [1, 1000],..., [9999, 12000], [12001, float("inf")]]

In the final application I need to determine what range an arbitrary number falls within. I'm doing something like below:

...
for range_ in ranges:
    range_start, range_end = range_
    if range_start <= number <= range_end:
        return range_
...

number can be any integer or infinity so the ranges in the db must stretch from negative infinity to positive infinity

Problem

The following code works fine

import sqlalchemy as sa
from sqlalchemy.orm import declarative_base, sessionmaker, scoped_session


Base = declarative_base()


class MyMappedClass(Base):
    __tablename__ = "my_table"
    id = sa.Column(sa.Integer, primary_key=True)
    ranges = sa.Column(sa.JSON)


if __name__ == "__main__":
    engine = sa.create_engine("sqlite://", future=True)
    Session = scoped_session(sessionmaker(bind=engine))

    session = Session()
    Base.metadata.create_all(session.bind)
    mapped_class = MyMappedClass(
        ranges=[[float("-inf"), 1], [1, float("inf")]],
    )
    with session:
        session.add(mapped_class)
        session.commit()

But when I add a CheckConstraint calling json_array_length(ranges) I get a malformed JSON error:

...
ranges = sa.Column(
    sa.JSON,
    sa.CheckConstraint("json_array_length(ranges) >= 2"),
)
...

Traceback:

...
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) malformed JSON
...

I confirmed [[float("-inf"), 1], [1, float("inf")]] isn't being converted to valid JSON by changing the CheckConstraint to json_valid(ranges) and it fails only when infinity is in the column.

I must be able to add CheckConstraints to the ranges column. There are data validation steps I need to do with other columns. The details of which are complex and beyond the scope of this question.

Workarounds

I think I can get around this by replacing float("inf") with "inf" like so

mapped_class = MyMappedClass(
    ranges=[["-inf", 1], [1, "inf"]],
)

And I can determine the range of an arbitrary number like:

...
for range_ in ranges:
    range_start, range_end = range
    if float(range_start) <= number <= float(range_end):
        return range_
...

But now I have mixed types in the column and I would prefer only numbers present.

Back to the Question

Is there any way to store infinity in a JSON1 column? If there are other databases that support infinity in an array (that are suitable for a standalone application) please share. Ideally, SQLAlchemy supports this database.

GMB
  • 216,147
  • 25
  • 84
  • 135
baqyoteto
  • 334
  • 2
  • 9

0 Answers0