Skip to content

Commit ef5543b

Browse files
Merge pull request #661 from dimitri-yatsenko/dev
Fix issue #656 - typo in SQL UNIQUE INDEX declaration caused error
2 parents aae57ac + 611eec5 commit ef5543b

File tree

7 files changed

+98
-8
lines changed

7 files changed

+98
-8
lines changed

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
## Release notes
22

3-
### 0.12.0 -- Aug 23, 2019
4-
* Support TLS/SSL connections PR620
3+
### 0.12.0 -- October 1, 2019
4+
* Support secure connections with TLS (aka SSL) PR #620
55
* Convert numpy array from python object to appropriate data type if all elements are of the same type (#587) PR #608
66
* Remove expression requirement to have additional attributes (#604) PR #604
77
* Support for filepath datatype (#481) PR #603
@@ -19,7 +19,7 @@
1919
* Support for pandas and order by "KEY" (#459, #537, #538, #541) PR #534
2020
* Support file attachment datatype and configurable blob storage (#467, #475, #480, #497) PR #532
2121
* Increase default display rows (#523) PR #526
22-
* Bugfixes (#521, #205, #279, #477, #570, #581, #597, #596, #618, #633, #643, #644, #647)
22+
* Bugfixes (#521, #205, #279, #477, #570, #581, #597, #596, #618, #633, #643, #644, #647, #656)
2323

2424
### 0.11.3 -- Jul 26, 2019
2525
* Fix incompatibility with pyparsing 2.4.1 (#629) PR #631

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
[![Join the chat at https://gitter.im/datajoint/datajoint-python](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/datajoint/datajoint-python?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
77

88
# Welcome to DataJoint for Python!
9-
DataJoint for Python is a high-level programming interface for relational databases designed to support data processing chains in science labs. DataJoint is built on the foundation of the relational data model and prescribes a consistent method for organizing, populating, and querying data.
9+
DataJoint for Python is a framework for scientific workflow management based on relational principles. DataJoint is built on the foundation of the relational data model and prescribes a consistent method for organizing, populating, computing, and querying data.
1010

1111
DataJoint was initially developed in 2009 by Dimitri Yatsenko in Andreas Tolias' Lab for the distributed processing and management of large volumes of data streaming from regular experiments. Starting in 2011, DataJoint has been available as an open-source project adopted by other labs and improved through contributions from several developers.
1212

1313
Vathes LLC supports DataJoint for Python as an open-source project and everyone is welcome to contribute.
14+
Its DataJoint Neuro (https://djneuro.io) business provides support to neuroscience labs for developing and executing custom data pipelines.
1415

1516
## Installation
1617
```

datajoint/declare.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def compile_foreign_key(line, context, attributes, primary_key, attr_sql, foreig
220220

221221
# declare unique index
222222
if is_unique:
223-
index_sql.append('UNIQUE INDEX ({attrs})'.format(attrs='`,`'.join(ref.primary_key)))
223+
index_sql.append('UNIQUE INDEX ({attrs})'.format(attrs=','.join("`%s`" % attr for attr in ref.primary_key)))
224224

225225

226226
def prepare_declare(definition, context):

tests/schema.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,4 +296,29 @@ class IndexRich(dj.Manual):
296296
index (first_date, value)
297297
"""
298298

299+
# Schema for issue 656
300+
@schema
301+
class ThingA(dj.Manual):
302+
definition = """
303+
a: int
304+
"""
305+
306+
307+
@schema
308+
class ThingB(dj.Manual):
309+
definition = """
310+
b1: int
311+
b2: int
312+
---
313+
b3: int
314+
"""
315+
316+
317+
@schema
318+
class ThingC(dj.Manual):
319+
definition = """
320+
-> ThingA
321+
---
322+
-> [unique, nullable] ThingB
323+
"""
299324

tests/test_connection.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def test_dj_conn():
1414
Should be able to establish a connection
1515
"""
1616
c = dj.conn(**CONN_INFO)
17-
assert c.is_connected
17+
assert_true(c.is_connected)
1818

1919

2020
def test_persistent_dj_conn():
@@ -33,8 +33,6 @@ def test_persistent_dj_conn():
3333
assert_true(c4 is c5)
3434

3535

36-
37-
3836
def test_repr():
3937
c1 = dj.conn(**CONN_INFO)
4038
assert_true('disconnected' not in repr(c1) and 'connected' in repr(c1))

tests/test_declare.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ def test_describe_indexes():
4040
s2 = declare(rel.full_table_name, rel.describe(), context)
4141
assert_equal(s1, s2)
4242

43+
@staticmethod
44+
def test_describe_dependencies():
45+
"""real_definition should match original definition"""
46+
rel = ThingC()
47+
context = inspect.currentframe().f_globals
48+
s1 = declare(rel.full_table_name, rel.definition, context)
49+
s2 = declare(rel.full_table_name, rel.describe(), context)
50+
assert_equal(s1, s2)
51+
52+
4353
@staticmethod
4454
def test_part():
4555
# Lookup and part with the same name. See issue #365

tests/test_dependencies.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from nose.tools import assert_true, assert_false, assert_equal, assert_list_equal, raises
2+
from .schema import *
3+
4+
5+
def test_nullable_dependency():
6+
"""test nullable unique foreign key"""
7+
8+
# Thing C has a nullable dependency on B whose primary key is composite
9+
a = ThingA()
10+
b = ThingB()
11+
c = ThingC()
12+
13+
# clear previous contents if any.
14+
c.delete_quick()
15+
b.delete_quick()
16+
a.delete_quick()
17+
18+
a.insert(dict(a=a) for a in range(7))
19+
20+
b.insert1(dict(b1=1, b2=1, b3=100))
21+
b.insert1(dict(b1=1, b2=2, b3=100))
22+
23+
# missing foreign key attributes = ok
24+
c.insert1(dict(a=0))
25+
c.insert1(dict(a=1, b1=33))
26+
c.insert1(dict(a=2, b2=77))
27+
28+
# unique foreign key attributes = ok
29+
c.insert1(dict(a=3, b1=1, b2=1))
30+
c.insert1(dict(a=4, b1=1, b2=2))
31+
32+
assert_true(len(c) == len(c.fetch()) == 5)
33+
34+
35+
@raises(dj.errors.DuplicateError)
36+
def test_unique_dependency():
37+
"""test nullable unique foreign key"""
38+
39+
# Thing C has a nullable dependency on B whose primary key is composite
40+
a = ThingA()
41+
b = ThingB()
42+
c = ThingC()
43+
44+
# clear previous contents if any.
45+
c.delete_quick()
46+
b.delete_quick()
47+
a.delete_quick()
48+
49+
a.insert(dict(a=a) for a in range(7))
50+
51+
b.insert1(dict(b1=1, b2=1, b3=100))
52+
b.insert1(dict(b1=1, b2=2, b3=100))
53+
54+
c.insert1(dict(a=0, b1=1, b2=1))
55+
# duplicate foreign key attributes = not ok
56+
c.insert1(dict(a=1, b1=1, b2=1))

0 commit comments

Comments
 (0)