Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
185 commits
Select commit Hold shift + click to select a range
dbd7d8a
add tests for aggr query bugs
dimitri-yatsenko Apr 10, 2020
75e8fdf
simplify expression.py by using a copy method rather than the copy co…
dimitri-yatsenko Apr 11, 2020
ae8c71f
fix tests for the aggr regressions
dimitri-yatsenko Apr 11, 2020
01bf292
factor out expression preview and conditions into separate modules
dimitri-yatsenko Apr 11, 2020
a3eeeec
rework restriction with explicit check for referenced attributes.
dimitri-yatsenko Apr 11, 2020
0f9800c
wrap the projection functionality into QueryExpression
dimitri-yatsenko Apr 12, 2020
ac6e623
add more attribute checks to proj
dimitri-yatsenko Apr 12, 2020
c0cf181
intermediate: minimize attribute lists in subqueries
dimitri-yatsenko Apr 13, 2020
13c2ec5
Merge branch 'update1' into aggr_fix
dimitri-yatsenko Apr 17, 2020
b62a357
Merge branch 'update1' into aggr_fix
dimitri-yatsenko Apr 17, 2020
544220f
Merge branch 'update1' into aggr_fix
dimitri-yatsenko Apr 25, 2020
49541a7
add query design specification document
dimitri-yatsenko Apr 28, 2020
5f78bf6
Query spec for joins
dimitri-yatsenko Apr 28, 2020
8d2180f
rename transpiler design spec file
dimitri-yatsenko Apr 28, 2020
e393d1e
add section on "Backprojection" in the transpiler specs.
dimitri-yatsenko Apr 28, 2020
8463dbd
temporary
dimitri-yatsenko May 1, 2020
e456768
Merge branch 'release013' of https://github.com/datajoint/datajoint-p…
dimitri-yatsenko May 9, 2020
7f1acee
obviate the Join class
dimitri-yatsenko May 9, 2020
b7853ba
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko May 9, 2020
ccdc718
new Aggregation implementation
dimitri-yatsenko May 11, 2020
e5b3953
intermediate
dimitri-yatsenko May 15, 2020
366db84
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko May 15, 2020
9da9424
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko May 17, 2020
0c5ae30
make_condition now collects all column names
dimitri-yatsenko May 17, 2020
722478c
Merge branch 'master' into aggr_fix
dimitri-yatsenko May 17, 2020
9c60556
fix the JobTable to comply with the new QueryExpression structure
dimitri-yatsenko May 18, 2020
a2ede2d
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko May 24, 2020
15b76aa
fix issue 386
dimitri-yatsenko May 24, 2020
642208c
fix issue #558
dimitri-yatsenko May 24, 2020
83f2552
fix Table.alter to work with the new .heading loading
dimitri-yatsenko May 25, 2020
9d70f0e
further corrections in subquery handling
dimitri-yatsenko May 25, 2020
7a1a6cb
Merge branch 'master' into aggr_fix
dimitri-yatsenko Oct 11, 2020
046d4b8
debug query engine
dimitri-yatsenko Oct 12, 2020
9f5c449
debug query engine
dimitri-yatsenko Oct 12, 2020
920870e
rename QueryExpression.source into QueryExpression.support
dimitri-yatsenko Oct 12, 2020
c063171
fix bug in new restriction implementation
dimitri-yatsenko Oct 12, 2020
75f8051
fix alias names in new implementation of aggregation
dimitri-yatsenko Oct 16, 2020
20e5097
fix new aggregation implementation with keep_all_rows=True
dimitri-yatsenko Oct 16, 2020
28d4875
replace NATUAL JOIN with JOIN ... USING ...
dimitri-yatsenko Oct 16, 2020
fe351aa
replace NATUAL JOIN with JOIN ... USING ...
dimitri-yatsenko Oct 16, 2020
e13e58d
Merge branch 'aggr_fix' of https://github.com/dimitri-yatsenko/datajo…
dimitri-yatsenko Oct 16, 2020
cdfa4fc
fix error introduced in last commit
dimitri-yatsenko Oct 16, 2020
3050fb6
add Union back -- yet to be re-implemented
dimitri-yatsenko Oct 20, 2020
2ec06db
update Union specification in dev guide
dimitri-yatsenko Oct 20, 2020
c595fb7
fix delete for new restrictions
dimitri-yatsenko Oct 22, 2020
f4b535a
work on Union
dimitri-yatsenko Oct 22, 2020
ee7fcc1
implement promiscuous restriction and join
dimitri-yatsenko Oct 23, 2020
9c407f4
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko Oct 23, 2020
7ca6347
work on new Union
dimitri-yatsenko Oct 24, 2020
6659c66
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko Oct 24, 2020
19b2f39
new implementation of simple union (all tests passed)
dimitri-yatsenko Oct 26, 2020
cd49f3f
implement outer union
dimitri-yatsenko Oct 26, 2020
6926c03
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko Oct 26, 2020
cc9e89f
Merge branch 'master' into aggr_fix
dimitri-yatsenko Oct 26, 2020
b20ec5d
update change log and documentation release notes
dimitri-yatsenko Oct 26, 2020
dacf382
add tests for outer union
dimitri-yatsenko Oct 26, 2020
676267f
query preview fix for new queries
dimitri-yatsenko Oct 27, 2020
c2ce3d6
bugfix in outer union
dimitri-yatsenko Oct 27, 2020
3ec043d
bump the version number to 0.13.0
dimitri-yatsenko Oct 27, 2020
a50c851
Merge branch 'release013' of https://github.com/datajoint/datajoint-p…
dimitri-yatsenko Oct 27, 2020
21a30a9
fix bug in the new unary minus operator for QueryExpression
dimitri-yatsenko Oct 28, 2020
78595d5
add University schema and query tests
dimitri-yatsenko Nov 7, 2020
5c37cba
add more university tests
dimitri-yatsenko Nov 7, 2020
22b56f8
restore the docstring for QueryExpression.restrict
dimitri-yatsenko Nov 7, 2020
c103058
Merge branch 'master' of https://github.com/datajoint/datajoint-pytho…
dimitri-yatsenko Nov 9, 2020
92981e1
fix requirements files
dimitri-yatsenko Nov 10, 2020
6c1c844
make schema_university compatible with Python3.5
dimitri-yatsenko Nov 20, 2020
9f76912
fix test for the university database
dimitri-yatsenko Nov 20, 2020
aad6528
Merge branch 'master' into aggr_fix
dimitri-yatsenko Nov 20, 2020
d66df7a
autopopulate progress bar displays class name
dimitri-yatsenko Nov 22, 2020
313c252
minor cleanup
dimitri-yatsenko Nov 27, 2020
6eeca0e
minor corrections in tests
dimitri-yatsenko Nov 27, 2020
7d849ac
Merge branch 'aggr_fix' into deferred_schemas
dimitri-yatsenko Nov 27, 2020
046ac14
Merge branch 'master' into aggr_fix
dimitri-yatsenko Nov 27, 2020
0229d29
Merge branch 'aggr_fix' into deferred_schemas
dimitri-yatsenko Nov 27, 2020
aa3ef5d
add schema.activate to support deferred schema declarations (issue #834)
dimitri-yatsenko Nov 27, 2020
db60616
fix python3.5 compatibility in tests
dimitri-yatsenko Nov 28, 2020
0f4936e
enable additional objects in schema.activate
dimitri-yatsenko Nov 28, 2020
fcb94a7
use a deferred schema in a test.
dimitri-yatsenko Nov 28, 2020
8011609
fix the data ordering in the univeresity tests
dimitri-yatsenko Nov 30, 2020
db228ef
fix data ordering in the university schema
dimitri-yatsenko Nov 30, 2020
ce18093
Merge branch 'aggr_fix' into deferred_schemas
dimitri-yatsenko Nov 30, 2020
fff9b37
save the university test data in csv files
dimitri-yatsenko Dec 1, 2020
79bc702
Merge branch 'aggr_fix' into deferred_schemas
dimitri-yatsenko Dec 1, 2020
3174e29
add the method schema.is_activated
dimitri-yatsenko Dec 1, 2020
0f30383
assert schema activation for database operations
dimitri-yatsenko Dec 2, 2020
4e57888
implement query caching
dimitri-yatsenko Dec 3, 2020
e4783e4
fix for previous commit
dimitri-yatsenko Dec 3, 2020
1573151
minor PEP8
dimitri-yatsenko Dec 3, 2020
dc67236
Merge branch 'master' into aggr_fix
dimitri-yatsenko Dec 4, 2020
57085a6
Merge branch 'aggr_fix' into deferred_schemas
dimitri-yatsenko Dec 4, 2020
d2b1167
Merge branch 'deferred_schemas' into query-cache
dimitri-yatsenko Dec 4, 2020
a9af9dd
query activate silently exits if repeatedly activated with the same s…
dimitri-yatsenko Dec 5, 2020
0389f8a
bug fix from previous commit
dimitri-yatsenko Dec 6, 2020
af85dca
Merge branch 'deferred_schemas' into query-cache
dimitri-yatsenko Dec 6, 2020
8120da4
delete relies on integrity errors for cascading.
dimitri-yatsenko Dec 6, 2020
61a54b5
fix restriciton in cascaded delete
dimitri-yatsenko Dec 6, 2020
fbd3de5
update releaes notes
dimitri-yatsenko Dec 6, 2020
0b6b227
Merge branch 'deferred_schemas' into query-cache
dimitri-yatsenko Dec 6, 2020
b5adb12
improve docstring for cascaded delete.
dimitri-yatsenko Dec 6, 2020
f94c415
Bypass delete confirmation when there is nothing to delete
dimitri-yatsenko Dec 6, 2020
651fca5
Merge branch 'deferred_schemas' into query-cache
dimitri-yatsenko Dec 6, 2020
431ba2c
Merge pull request #842 from dimitri-yatsenko/deferred_schemas
guzman-raphael Dec 7, 2020
bd4db82
Update version to a dev release and version lock minio until bug patc…
guzman-raphael Dec 9, 2020
12b421e
query caching works
dimitri-yatsenko Dec 9, 2020
d3c9d46
Merge pull request #846 from dimitri-yatsenko/cascade-delete
guzman-raphael Dec 10, 2020
1c08ac3
Merge pull request #845 from dimitri-yatsenko/query-cache
guzman-raphael Dec 10, 2020
6bae9ce
Bump version.
guzman-raphael Dec 10, 2020
385d51a
add property rowcount to EmulatedCursor
dimitri-yatsenko Dec 10, 2020
58f017b
Merge branch 'master' into aggr_fix
dimitri-yatsenko Dec 11, 2020
61a66cb
Merge branch 'master' into aggr_fix
dimitri-yatsenko Dec 21, 2020
5cdc560
Merge branch 'master' into aggr_fix
dimitri-yatsenko Dec 24, 2020
88c3281
Merge branch 'aggr_fix' into cascade-delete
dimitri-yatsenko Dec 24, 2020
2523cc6
fix the requirements file
dimitri-yatsenko Dec 24, 2020
58b57c5
Merge branch 'cascade-delete' into query-cache
dimitri-yatsenko Dec 24, 2020
2c9071e
minor PEP8 styling
dimitri-yatsenko Dec 24, 2020
ff2ffe5
Merge branch 'cascade-delete' into query-cache
dimitri-yatsenko Dec 24, 2020
ef75a3a
Merge branch 'dev' of https://github.com/datajoint/datajoint-python i…
dimitri-yatsenko Dec 24, 2020
69a5210
fix release notes
dimitri-yatsenko Dec 25, 2020
8e89ae2
Merge branch 'cascade-delete' into query-cache
dimitri-yatsenko Dec 25, 2020
4e3d4d7
fix CHANGELOG
dimitri-yatsenko Dec 25, 2020
ec751e1
Merge branch 'cascade-delete' into query-cache
dimitri-yatsenko Dec 25, 2020
403c364
fix relational restrictions applied before group by
dimitri-yatsenko Jan 1, 2021
934b8de
Merge branch 'aggr_fix' into cascade-delete
dimitri-yatsenko Jan 1, 2021
507eb85
Merge branch 'cascade-delete' into query-cache
dimitri-yatsenko Jan 1, 2021
0807f1d
use consistent subquery alias names
dimitri-yatsenko Jan 1, 2021
e7c44db
Ellipsis in aggr no longer includes attributes from the right operand
dimitri-yatsenko Jan 1, 2021
2cf8e30
add test for aggregation with Ellipsis
dimitri-yatsenko Jan 2, 2021
a1aab81
Merge branch 'aggr_fix' into cascade-delete
dimitri-yatsenko Jan 2, 2021
dbcb469
Merge branch 'cascade-delete' into query-cache
dimitri-yatsenko Jan 2, 2021
8ca8594
Update to version 0.13.dev2
dimitri-yatsenko Jan 5, 2021
952607c
Merge pull request #850 from dimitri-yatsenko/cascade-delete
guzman-raphael Jan 5, 2021
e300649
fix #853 -- queries with `is null` and `is not null` work correctly
dimitri-yatsenko Jan 11, 2021
ad4a10f
upgrade image for fakeservices.datajoint.io
dimitri-yatsenko Jan 11, 2021
d0138e4
add tests for issue #853 - restrictions by NULL
dimitri-yatsenko Feb 5, 2021
dd45f61
Merge pull request #854 from dimitri-yatsenko/cascade-delete
guzman-raphael Feb 5, 2021
f8b7c96
Merge branch 'release013' of https://github.com/datajoint/datajoint-p…
dimitri-yatsenko Feb 8, 2021
b802f98
replace OrderedDict with dict since Python 3.5 is no longer supported
dimitri-yatsenko Feb 8, 2021
e81438b
Merge branch 'query-cache' into cascade-delete
dimitri-yatsenko Feb 8, 2021
89cdd11
Merge branch 'release013' of https://github.com/datajoint/datajoint-p…
dimitri-yatsenko Feb 8, 2021
8805e30
Merge branch 'aggr_fix' into cascade-delete
dimitri-yatsenko Feb 8, 2021
397befe
remove conflict from changelogs
dimitri-yatsenko Feb 8, 2021
64d9ee3
Merge branch 'aggr_fix' into cascade-delete
dimitri-yatsenko Feb 8, 2021
a1b7df0
PEP 8
dimitri-yatsenko Feb 8, 2021
e30d7b3
Merge pull request #868 from dimitri-yatsenko/cascade-delete
guzman-raphael Feb 8, 2021
4bc567f
Update release version for pre-release.
guzman-raphael Feb 8, 2021
c14eb73
Merge pull request #869 from guzman-raphael/dev-plugin
dimitri-yatsenko Feb 8, 2021
09453f4
Update datajoint/table.py
dimitri-yatsenko Feb 10, 2021
3aa3189
Update datajoint/table.py
dimitri-yatsenko Feb 10, 2021
7f74f09
fix transaction switch in delete
dimitri-yatsenko Feb 10, 2021
d3d7d67
pep 8
dimitri-yatsenko Feb 10, 2021
2a4ff4a
Merge branch 'cascade-delete' of https://github.com/dimitri-yatsenko/…
dimitri-yatsenko Feb 10, 2021
5e9ee73
bump version to 0.13.dev4
dimitri-yatsenko Feb 10, 2021
eb72758
Merge branch 'dev' of https://github.com/datajoint/datajoint-python i…
dimitri-yatsenko Feb 10, 2021
06e441c
Merge pull request #870 from dimitri-yatsenko/cascade-delete
guzman-raphael Feb 10, 2021
0d85617
add test for dj.list_schemas
dimitri-yatsenko Feb 13, 2021
4bb4090
clean up schema.activate logic
dimitri-yatsenko Feb 13, 2021
198f2ab
Update datajoint/schemas.py
dimitri-yatsenko Feb 26, 2021
cd30e39
reformat SQL query
dimitri-yatsenko Feb 26, 2021
8b01633
Merge branch 'dev' of https://github.com/datajoint/datajoint-python i…
dimitri-yatsenko Feb 27, 2021
74159e3
increment the version to 0.13.dev5
dimitri-yatsenko Feb 27, 2021
e200ca0
PEP8
dimitri-yatsenko Feb 27, 2021
65df8cd
fix comments
dimitri-yatsenko Feb 27, 2021
322f17f
Merge pull request #872 from dimitri-yatsenko/cascade-delete
guzman-raphael Feb 27, 2021
afa77f9
Add test case for same secondary attributes between parent/child.
guzman-raphael Mar 9, 2021
fe85a82
Fix merge conflicts.
guzman-raphael Mar 9, 2021
052a36b
Merge pull request #8 from guzman-raphael/duplicate-secondary-attr-test
dimitri-yatsenko Mar 9, 2021
ed7c624
add test for #876
dimitri-yatsenko Mar 9, 2021
d52f072
Merge branch 'cascade-delete' of https://github.com/dimitri-yatsenko/…
dimitri-yatsenko Mar 9, 2021
5469c5c
fix #857
dimitri-yatsenko Mar 11, 2021
26f6265
fix #876
dimitri-yatsenko Mar 12, 2021
d5f6f1d
Add debug logging.
guzman-raphael Mar 12, 2021
66a0995
Update duplicate attribute test.
guzman-raphael Mar 17, 2021
40c94cb
Remove debug statement.
guzman-raphael Mar 17, 2021
a172a1d
Bump version.
guzman-raphael Mar 17, 2021
f52b8a7
Merge branch 'duplicate-secondary-attr-test' into duplicate-secondary…
guzman-raphael Mar 17, 2021
5456c1e
Fix test and update changelog.
guzman-raphael Mar 17, 2021
9955fe2
Merge pull request #9 from guzman-raphael/duplicate-secondary-attr
dimitri-yatsenko Mar 17, 2021
5217cd4
Merge pull request #878 from dimitri-yatsenko/cascade-delete
guzman-raphael Mar 17, 2021
0a77bcd
Fix issue on cascading deletes where MySQL truncates the contraint du…
guzman-raphael Mar 21, 2021
31a371a
Bump version, update 0.13.0 release date, and update release log.
guzman-raphael Mar 21, 2021
1ab733c
Fix styling.
guzman-raphael Mar 21, 2021
e567ca1
Make error message parsing more explicit and simplify logic.
guzman-raphael Mar 22, 2021
b8e98ef
Merge error regex with optional tokens, remove unnecessary whitespace…
guzman-raphael Mar 22, 2021
5cfdf21
Merge pull request #10 from guzman-raphael/delete-parsing
dimitri-yatsenko Mar 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ notebook
.vscode
__main__.py
jupyter_custom.js
apk_requirements.txt
apk_requirements.txt
.eggs
17 changes: 12 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
## Release notes

### 0.13.0 -- TBD
### 0.13.0 -- Mar 24, 2021
* Re-implement query transpilation into SQL, fixing issues (#386, #449, #450, #484). PR #754
* Re-implement cascading deletes for better performance. PR #839.
* Add table method `.update1` to update a row in the table with new values PR #763
* Python datatypes are now enabled by default in blobs (#761). PR #785
* Added permissive join and restriction operators `@` and `^` (#785) PR #754
* Support DataJoint datatype and connection plugins (#715, #729) PR 730, #735
* Allow updating specified secondary attributes using `update1` PR #763
* add dj.key_hash reference to dj.hash.key_hash, treat as 'public api'
* default enable_python_native_blobs to True
* Remove python 3.5 support
* Add `dj.key_hash` alias to `dj.hash.key_hash`
* Default enable_python_native_blobs to True
* Bugfix - Regression error on joins with same attribute name (#857) PR #878
* Bugfix - Error when `fetch1('KEY')` when `dj.config['fetch_format']='frame'` set (#876) PR #880, #878
* Bugfix - Error when cascading deletes in tables with many, complex keys (#883, #886) PR #839
* Drop support for Python 3.5

### 0.12.8 -- Jan 12, 2021
* table.children, .parents, .descendents, and ancestors can return queryable objects. PR #833
Expand Down
21 changes: 12 additions & 9 deletions LNX-docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ services:
interval: 1s
fakeservices.datajoint.io:
<<: *net
image: raphaelguzman/nginx:v0.0.13
image: datajoint/nginx:v0.0.16
environment:
- ADD_db_TYPE=DATABASE
- ADD_db_ENDPOINT=db:3306
Expand Down Expand Up @@ -72,14 +72,17 @@ services:
- COVERALLS_SERVICE_NAME
- COVERALLS_REPO_TOKEN
working_dir: /src
command: >
/bin/sh -c
"
pip install --user nose nose-cov coveralls .;
pip freeze | grep datajoint;
nosetests -vsw tests --with-coverage --cover-package=datajoint && coveralls;
# jupyter notebook;
"
command:
- sh
- -c
- |
set -e
pip install --user -r test_requirements.txt
pip install --user .
pip freeze | grep datajoint
nosetests -vsw tests --with-coverage --cover-package=datajoint
coveralls
# jupyter notebook
# ports:
# - "8888:8888"
user: ${UID}:${GID}
Expand Down
2 changes: 1 addition & 1 deletion datajoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"""

__author__ = "DataJoint Contributors"
__date__ = "February 7, 2019"
__date__ = "November 7, 2020"
__all__ = ['__author__', '__version__',
'config', 'conn', 'Connection',
'Schema', 'schema', 'VirtualModule', 'create_virtual_module',
Expand Down
8 changes: 4 additions & 4 deletions datajoint/autopopulate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from tqdm import tqdm
from .expression import QueryExpression, AndList
from .errors import DataJointError, LostConnectionError
from .table import FreeTable
import signal

# noinspection PyExceptionInherit,PyCallingNonCallable
Expand Down Expand Up @@ -58,15 +57,16 @@ def make(self, key):
@property
def target(self):
"""
relation to be populated.
Typically, AutoPopulate are mixed into a Relation object and the target is self.
:return: table to be populated.
In the typical case, dj.AutoPopulate is mixed into a dj.Table class by inheritance and the target is self.
"""
return self

def _job_key(self, key):
"""
:param key: they key returned for the job from the key source
:return: the dict to use to generate the job reservation hash
This method allows subclasses to control the job reservation granularity.
"""
return key

Expand Down Expand Up @@ -136,7 +136,7 @@ def handler(signum, frame):

make = self._make_tuples if hasattr(self, '_make_tuples') else self.make

for key in (tqdm(keys) if display_progress else keys):
for key in (tqdm(keys, desc=self.__class__.__name__) if display_progress else keys):
if max_calls is not None and call_count >= max_calls:
break
if not reserve_jobs or jobs.reserve(self.target.table_name, self._job_key(key)):
Expand Down
7 changes: 3 additions & 4 deletions datajoint/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
import uuid
import numpy as np
from .errors import DataJointError
from .utils import OrderedDict
from .settings import config


mxClassID = OrderedDict((
mxClassID = dict((
# see http://www.mathworks.com/help/techdoc/apiref/mxclassid.html
('mxUNKNOWN_CLASS', None),
('mxCELL_CLASS', None),
Expand Down Expand Up @@ -346,8 +345,8 @@ def pack_set(self, t):
len_u64(it) + it for it in (self.pack_blob(i) for i in t))

def read_dict(self):
return OrderedDict((self.read_blob(self.read_value()), self.read_blob(self.read_value()))
for _ in range(self.read_value()))
return dict((self.read_blob(self.read_value()), self.read_blob(self.read_value()))
for _ in range(self.read_value()))

def pack_dict(self, d):
return b"\4" + len_u64(d) + b"".join(
Expand Down
209 changes: 209 additions & 0 deletions datajoint/condition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
""" methods for generating SQL WHERE clauses from datajoint restriction conditions """

import inspect
import collections
import re
import uuid
import datetime
import decimal
import numpy
import pandas
from .errors import DataJointError


class PromiscuousOperand:
"""
A container for an operand to ignore join compatibility
"""
def __init__(self, operand):
self.operand = operand


class AndList(list):
"""
A list of conditions to by applied to a query expression by logical conjunction: the conditions are AND-ed.
All other collections (lists, sets, other entity sets, etc) are applied by logical disjunction (OR).

Example:
expr2 = expr & dj.AndList((cond1, cond2, cond3))
is equivalent to
expr2 = expr & cond1 & cond2 & cond3
"""
def append(self, restriction):
if isinstance(restriction, AndList):
# extend to reduce nesting
self.extend(restriction)
else:
super().append(restriction)


class Not:
""" invert restriction """
def __init__(self, restriction):
self.restriction = restriction


def assert_join_compatibility(expr1, expr2):
"""
Determine if expressions expr1 and expr2 are join-compatible. To be join-compatible,
the matching attributes in the two expressions must be in the primary key of one or the
other expression.
Raises an exception if not compatible.
:param expr1: A QueryExpression object
:param expr2: A QueryExpression object
"""
from .expression import QueryExpression, U

for rel in (expr1, expr2):
if not isinstance(rel, (U, QueryExpression)):
raise DataJointError('Object %r is not a QueryExpression and cannot be joined.' % rel)
if not isinstance(expr1, U) and not isinstance(expr2, U): # dj.U is always compatible
try:
raise DataJointError(
"Cannot join query expressions on dependent attribute `%s`" % next(
r for r in set(expr1.heading.secondary_attributes).intersection(
expr2.heading.secondary_attributes)))
except StopIteration:
pass # all ok


def make_condition(query_expression, condition, columns):
"""
Translate the input condition into the equivalent SQL condition (a string)
:param query_expression: a dj.QueryExpression object to apply condition
:param condition: any valid restriction object.
:param columns: a set passed by reference to collect all column names used in the condition.
:return: an SQL condition string or a boolean value.
"""
from .expression import QueryExpression, Aggregation, U

def prep_value(k, v):
"""prepare value v for inclusion as a string in an SQL condition"""
if query_expression.heading[k].uuid:
if not isinstance(v, uuid.UUID):
try:
v = uuid.UUID(v)
except (AttributeError, ValueError):
raise DataJointError('Badly formed UUID {v} in restriction by `{k}`'.format(k=k, v=v)) from None
return "X'%s'" % v.bytes.hex()
if isinstance(v, (datetime.date, datetime.datetime, datetime.time, decimal.Decimal)):
return '"%s"' % v
if isinstance(v, str):
return '"%s"' % v.replace('%', '%%')
return '%r' % v

negate = False
while isinstance(condition, Not):
negate = not negate
condition = condition.restriction
template = "NOT (%s)" if negate else "%s"

# restrict by string
if isinstance(condition, str):
columns.update(extract_column_names(condition))
return template % condition.strip().replace("%", "%%") # escape % in strings, see issue #376

# restrict by AndList
if isinstance(condition, AndList):
# omit all conditions that evaluate to True
items = [item for item in (make_condition(query_expression, cond, columns) for cond in condition)
if item is not True]
if any(item is False for item in items):
return negate # if any item is False, the whole thing is False
if not items:
return not negate # and empty AndList is True
return template % ('(' + ') AND ('.join(items) + ')')

# restriction by dj.U evaluates to True
if isinstance(condition, U):
return not negate

# restrict by boolean
if isinstance(condition, bool):
return negate != condition

# restrict by a mapping such as a dict -- convert to an AndList of string equality conditions
if isinstance(condition, collections.abc.Mapping):
common_attributes = set(condition).intersection(query_expression.heading.names)
if not common_attributes:
return not negate # no matching attributes -> evaluates to True
columns.update(common_attributes)
return template % ('(' + ') AND ('.join(
'`%s`=%s' % (k, prep_value(k, condition[k])) for k in common_attributes) + ')')

# restrict by a numpy record -- convert to an AndList of string equality conditions
if isinstance(condition, numpy.void):
common_attributes = set(condition.dtype.fields).intersection(query_expression.heading.names)
if not common_attributes:
return not negate # no matching attributes -> evaluate to True
columns.update(common_attributes)
return template % ('(' + ') AND ('.join(
'`%s`=%s' % (k, prep_value(k, condition[k])) for k in common_attributes) + ')')

# restrict by a QueryExpression subclass -- trigger instantiation and move on
if inspect.isclass(condition) and issubclass(condition, QueryExpression):
condition = condition()

# restrict by another expression (aka semijoin and antijoin)
check_compatibility = True
if isinstance(condition, PromiscuousOperand):
condition = condition.operand
check_compatibility = False

if isinstance(condition, QueryExpression):
if check_compatibility:
assert_join_compatibility(query_expression, condition)
common_attributes = [q for q in condition.heading.names if q in query_expression.heading.names]
columns.update(common_attributes)
if isinstance(condition, Aggregation):
condition = condition.make_subquery()
return (
# without common attributes, any non-empty set matches everything
(not negate if condition else negate) if not common_attributes
else '({fields}) {not_}in ({subquery})'.format(
fields='`' + '`,`'.join(common_attributes) + '`',
not_="not " if negate else "",
subquery=condition.make_sql(common_attributes)))

# restrict by pandas.DataFrames
if isinstance(condition, pandas.DataFrame):
condition = condition.to_records() # convert to numpy.recarray and move on

# if iterable (but not a string, a QueryExpression, or an AndList), treat as an OrList
try:
or_list = [make_condition(query_expression, q, columns) for q in condition]
except TypeError:
raise DataJointError('Invalid restriction type %r' % condition)
else:
or_list = [item for item in or_list if item is not False] # ignore all False conditions
if any(item is True for item in or_list): # if any item is True, the whole thing is True
return not negate
return template % ('(%s)' % ' OR '.join(or_list)) if or_list else negate # an empty or list is False


def extract_column_names(sql_expression):
"""
extract all presumed column names from an sql expression such as the WHERE clause, for example.
:param sql_expression: a string containing an SQL expression
:return: set of extracted column names
This may be MySQL-specific for now.
"""
assert isinstance(sql_expression, str)
result = set()
s = sql_expression # for terseness
# remove escaped quotes
s = re.sub(r'(\\\")|(\\\')', '', s)
# remove quoted text
s = re.sub(r"'[^']*'", "", s)
s = re.sub(r'"[^"]*"', '', s)
# find all tokens in back quotes and remove them
result.update(re.findall(r"`([a-z][a-z_0-9]*)`", s))
s = re.sub(r"`[a-z][a-z_0-9]*`", '', s)
# remove space before parentheses
s = re.sub(r"\s*\(", "(", s)
# remove tokens followed by ( since they must be functions
s = re.sub(r"(\b[a-z][a-z_0-9]*)\(", "(", s)
remaining_tokens = set(re.findall(r"\b[a-z][a-z_0-9]*\b", s))
# update result removing reserved words
result.update(remaining_tokens - {"is", "in", "between", "like", "and", "or", "null", "not"})
return result
Loading