9749: Move experimental Select API code into a branch
This commit is contained in:
@ -2080,63 +2080,6 @@ class DBAPI(DbGeneric):
|
||||
else:
|
||||
return repr(value)
|
||||
|
||||
def _build_where_clause_recursive(self, table, where):
|
||||
"""
|
||||
where - (field, op, value)
|
||||
- ["NOT", where]
|
||||
- ["AND", (where, ...)]
|
||||
- ["OR", (where, ...)]
|
||||
"""
|
||||
if where is None:
|
||||
return ""
|
||||
elif len(where) == 3:
|
||||
field, db_op, value = where
|
||||
return "(%s %s %s)" % (self._hash_name(table, field),
|
||||
db_op, self._sql_repr(value))
|
||||
elif where[0] in ["AND", "OR"]:
|
||||
parts = [self._build_where_clause_recursive(table, part)
|
||||
for part in where[1]]
|
||||
return "(%s)" % ((" %s " % where[0]).join(parts))
|
||||
else:
|
||||
return "(NOT %s)" % self._build_where_clause_recursive(table,
|
||||
where[1])
|
||||
|
||||
def _build_where_clause(self, table, where):
|
||||
"""
|
||||
where - a list in where format
|
||||
return - "WHERE conditions..."
|
||||
"""
|
||||
parts = self._build_where_clause_recursive(table, where)
|
||||
if parts:
|
||||
return "WHERE " + parts
|
||||
else:
|
||||
return ""
|
||||
|
||||
def _build_order_clause(self, table, order_by):
|
||||
"""
|
||||
order_by - [(field, "ASC" | "DESC"), ...]
|
||||
"""
|
||||
if order_by:
|
||||
order_clause = ", ".join(["%s %s" % (self._hash_name(table, field),
|
||||
dir)
|
||||
for (field, dir) in order_by])
|
||||
return "ORDER BY " + order_clause
|
||||
else:
|
||||
return ""
|
||||
|
||||
def _build_select_fields(self, table, select_fields, secondary_fields):
|
||||
"""
|
||||
fields - [field, ...]
|
||||
return: "field, field, field"
|
||||
"""
|
||||
all_available = all([(field in secondary_fields)
|
||||
for field in select_fields])
|
||||
if all_available: # we can get them without expanding
|
||||
return select_fields
|
||||
else:
|
||||
# nope, we'll have to expand blob to get all fields
|
||||
return ["blob_data"]
|
||||
|
||||
def _check_order_by_fields(self, table, order_by, secondary_fields):
|
||||
"""
|
||||
Check to make sure all order_by fields are defined. If not, then
|
||||
@ -2150,128 +2093,6 @@ class DBAPI(DbGeneric):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_where_fields(self, table, where, secondary_fields):
|
||||
"""
|
||||
Check to make sure all where fields are defined. If not, then
|
||||
we need to do the Python-based select.
|
||||
|
||||
secondary_fields are hashed.
|
||||
"""
|
||||
if where is None:
|
||||
return True
|
||||
elif len(where) == 2: # ["AND" [...]] | ["OR" [...]] | ["NOT" expr]
|
||||
connector, exprs = where
|
||||
if connector in ["AND", "OR"]:
|
||||
for expr in exprs:
|
||||
value = self._check_where_fields(table, expr,
|
||||
secondary_fields)
|
||||
if value == False:
|
||||
return False
|
||||
return True
|
||||
else: # "NOT"
|
||||
return self._check_where_fields(table, exprs, secondary_fields)
|
||||
elif len(where) == 3: # (name, db_op, value)
|
||||
(name, db_op, value) = where
|
||||
# just the ones we need for where
|
||||
return self._hash_name(table, name) in secondary_fields
|
||||
|
||||
def _select(self, table, fields=None, start=0, limit=-1,
|
||||
where=None, order_by=None):
|
||||
"""
|
||||
Default implementation of a select for those databases
|
||||
that don't support SQL. Returns a list of dicts, total,
|
||||
and time.
|
||||
|
||||
table - Person, Family, etc.
|
||||
fields - used by object.get_field()
|
||||
start - position to start
|
||||
limit - count to get; -1 for all
|
||||
where - (field, SQL string_operator, value) |
|
||||
["AND", [where, where, ...]] |
|
||||
["OR", [where, where, ...]] |
|
||||
["NOT", where]
|
||||
order_by - [[fieldname, "ASC" | "DESC"], ...]
|
||||
"""
|
||||
secondary_fields = ([self._hash_name(table, field)
|
||||
for (field, ptype)
|
||||
in self.get_table_func(
|
||||
table, "class_func").get_secondary_fields()]
|
||||
+ ["handle"])
|
||||
# handle is a sql field, but not listed in secondaries
|
||||
# If no fields, then we need objects:
|
||||
# Check to see if where matches SQL fields:
|
||||
table_name = table.lower()
|
||||
if ((not self._check_where_fields(table, where, secondary_fields))
|
||||
or (not self._check_order_by_fields(table, order_by,
|
||||
secondary_fields))):
|
||||
# If not, then need to do select via Python:
|
||||
generator = super()._select(table, fields, start,
|
||||
limit, where, order_by)
|
||||
for item in generator:
|
||||
yield item
|
||||
return
|
||||
# Otherwise, we are SQL
|
||||
if fields is None:
|
||||
fields = ["blob_data"]
|
||||
get_count_only = False
|
||||
if fields[0] == "count(1)":
|
||||
hashed_fields = ["count(1)"]
|
||||
fields = ["count(1)"]
|
||||
select_fields = ["count(1)"]
|
||||
get_count_only = True
|
||||
else:
|
||||
hashed_fields = [self._hash_name(table, field) for field in fields]
|
||||
fields = hashed_fields
|
||||
select_fields = self._build_select_fields(table, fields,
|
||||
secondary_fields)
|
||||
where_clause = self._build_where_clause(table, where)
|
||||
order_clause = self._build_order_clause(table, order_by)
|
||||
if get_count_only:
|
||||
select_fields = ["1"]
|
||||
if start:
|
||||
query = "SELECT %s FROM %s %s %s LIMIT %s, %s " % (
|
||||
", ".join(select_fields),
|
||||
table_name, where_clause, order_clause, start, limit
|
||||
)
|
||||
else:
|
||||
query = "SELECT %s FROM %s %s %s LIMIT %s" % (
|
||||
", ".join(select_fields),
|
||||
table_name, where_clause, order_clause, limit
|
||||
)
|
||||
if get_count_only:
|
||||
self.dbapi.execute("SELECT count(1) from (%s) AS temp_select;"
|
||||
% query)
|
||||
rows = self.dbapi.fetchall()
|
||||
yield rows[0][0]
|
||||
return
|
||||
self.dbapi.execute(query)
|
||||
rows = self.dbapi.fetchall()
|
||||
for row in rows:
|
||||
if fields[0] != "blob_data":
|
||||
obj = None # don't build it if you don't need it
|
||||
data = {}
|
||||
for field in fields:
|
||||
if field in select_fields:
|
||||
data[field.replace("__", ".")
|
||||
] = row[select_fields.index(field)]
|
||||
else:
|
||||
if obj is None: # we need it! create it and cache it:
|
||||
obj = self.get_table_func(table,
|
||||
"class_func").create(
|
||||
pickle.loads(row[0]))
|
||||
# get the field, even if we need to do a join:
|
||||
# FIXME: possible optimize:
|
||||
# do a join in select for this if needed:
|
||||
field = field.replace("__", ".")
|
||||
data[field] = obj.get_field(field, self,
|
||||
ignore_errors=True)
|
||||
yield data
|
||||
else:
|
||||
obj = self.get_table_func(table,
|
||||
"class_func").create(
|
||||
pickle.loads(row[0]))
|
||||
yield obj
|
||||
|
||||
def get_summary(self):
|
||||
"""
|
||||
Returns dictionary of summary item.
|
||||
|
Reference in New Issue
Block a user