Skip to content

Commit 685a641

Browse files
Merge remote-tracking branch 'origin/master' into design/restricted-diagram
# Conflicts: # pixi.lock
2 parents f1c0725 + e9b6e83 commit 685a641

File tree

9 files changed

+209
-173
lines changed

9 files changed

+209
-173
lines changed

pixi.lock

Lines changed: 151 additions & 140 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/datajoint/adapters/base.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,39 @@ def parameter_placeholder(self) -> str:
238238
"""
239239
...
240240

241+
def make_full_table_name(self, database: str, table_name: str) -> str:
242+
"""
243+
Construct a fully-qualified table name for this backend.
244+
245+
Default implementation produces a two-part name (``schema.table``).
246+
Backends that require additional namespace levels can override.
247+
248+
Parameters
249+
----------
250+
database : str
251+
Schema/database name.
252+
table_name : str
253+
Table name (including tier prefix).
254+
255+
Returns
256+
-------
257+
str
258+
Fully-qualified, quoted table name.
259+
"""
260+
return f"{self.quote_identifier(database)}.{self.quote_identifier(table_name)}"
261+
262+
@property
263+
def max_table_name_length(self) -> int:
264+
"""
265+
Maximum length of a table name for this backend.
266+
267+
Returns
268+
-------
269+
int
270+
Maximum allowed characters in a table identifier.
271+
"""
272+
return 64 # safe default (MySQL limit)
273+
241274
# =========================================================================
242275
# Type Mapping
243276
# =========================================================================

src/datajoint/adapters/postgres.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,11 @@ def quote_identifier(self, name: str) -> str:
249249
"""
250250
return f'"{name}"'
251251

252+
@property
253+
def max_table_name_length(self) -> int:
254+
"""PostgreSQL NAMEDATALEN-1 = 63."""
255+
return 63
256+
252257
def split_full_table_name(self, full_table_name: str) -> tuple[str, str]:
253258
"""Split ``"schema"."table"`` into ``('schema', 'table')``."""
254259
schema, table = full_table_name.replace('"', "").split(".")

src/datajoint/autopopulate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ def _update_job_metadata(self, key, start_time, duration, version):
778778
pk_condition = make_condition(self, key, set())
779779
self.connection.query(
780780
f"UPDATE {self.full_table_name} SET "
781-
"`_job_start_time`=%s, `_job_duration`=%s, `_job_version`=%s "
781+
"_job_start_time=%s, _job_duration=%s, _job_version=%s "
782782
f"WHERE {pk_condition}",
783783
args=(start_time, duration, version[:64] if version else ""),
784784
)

src/datajoint/declare.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
# Get SQL mapping for core types
5353
CORE_TYPE_SQL = {name: sql_type for name, (_, sql_type) in CORE_TYPES.items()}
5454

55-
MAX_TABLE_NAME_LENGTH = 64
5655
CONSTANT_LITERALS = {
5756
"CURRENT_TIMESTAMP",
5857
"NULL",
@@ -296,7 +295,7 @@ def compile_foreign_key(
296295
parent_full_name = ref.support[0]
297296
# Parse as database.table using the adapter's quoting convention
298297
parts = adapter.split_full_table_name(parent_full_name)
299-
ref_table_name = f"{adapter.quote_identifier(parts[0])}.{adapter.quote_identifier(parts[1])}"
298+
ref_table_name = adapter.make_full_table_name(parts[0], parts[1])
300299

301300
foreign_key_sql.append(
302301
f"FOREIGN KEY ({fk_cols}) REFERENCES {ref_table_name} ({pk_cols}) ON UPDATE CASCADE ON DELETE RESTRICT"
@@ -432,21 +431,13 @@ def declare(
432431
DataJointError
433432
If table name exceeds max length or has no primary key.
434433
"""
435-
# Parse table name without assuming quote character
436-
# Extract schema.table from quoted name using adapter
437-
quote_char = adapter.quote_identifier("x")[0] # Get quote char from adapter
438-
parts = full_table_name.split(".")
439-
if len(parts) == 2:
440-
schema_name = parts[0].strip(quote_char)
441-
table_name = parts[1].strip(quote_char)
442-
else:
443-
schema_name = None
444-
table_name = parts[0].strip(quote_char)
434+
# Parse table name using adapter (handles backend-specific quoting)
435+
schema_name, table_name = adapter.split_full_table_name(full_table_name)
445436

446-
if len(table_name) > MAX_TABLE_NAME_LENGTH:
437+
if len(table_name) > adapter.max_table_name_length:
447438
raise DataJointError(
448439
"Table name `{name}` exceeds the max length of {max_length}".format(
449-
name=table_name, max_length=MAX_TABLE_NAME_LENGTH
440+
name=table_name, max_length=adapter.max_table_name_length
450441
)
451442
)
452443

src/datajoint/lineage.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,12 @@ def lineage_table_exists(connection, database):
7979
bool
8080
True if the table exists, False otherwise.
8181
"""
82-
result = connection.query(
83-
"""
84-
SELECT COUNT(*) FROM information_schema.tables
85-
WHERE table_schema = %s AND table_name = '~lineage'
86-
""",
87-
args=(database,),
88-
).fetchone()
89-
return result[0] > 0
82+
try:
83+
result = connection.query(connection.adapter.get_table_info_sql(database, "~lineage")).fetchone()
84+
return result is not None
85+
except Exception:
86+
# Schema or catalog query may fail on some backends
87+
return False
9088

9189

9290
def get_lineage(connection, database, table_name, attribute_name):

src/datajoint/schemas.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,11 @@ def make_classes(self, into: dict[str, Any] | None = None) -> None:
341341
frame = inspect.currentframe().f_back
342342
into = frame.f_locals
343343
del frame
344+
adapter = self.connection.adapter
344345
tables = [
345346
row[0]
346-
for row in self.connection.query(self.connection.adapter.list_tables_sql(self.database))
347-
if lookup_class_name("`{db}`.`{tab}`".format(db=self.database, tab=row[0]), into, 0) is None
347+
for row in self.connection.query(adapter.list_tables_sql(self.database))
348+
if lookup_class_name(adapter.make_full_table_name(self.database, row[0]), into, 0) is None
348349
]
349350
master_classes = (Lookup, Manual, Imported, Computed)
350351
part_tables = []
@@ -502,7 +503,7 @@ def jobs(self) -> list[Job]:
502503
# Iterate over auto-populated tables and check if their job table exists
503504
for table_name in self.list_tables():
504505
adapter = self.connection.adapter
505-
full_name = f"{adapter.quote_identifier(self.database)}." f"{adapter.quote_identifier(table_name)}"
506+
full_name = adapter.make_full_table_name(self.database, table_name)
506507
table = FreeTable(self.connection, full_name)
507508
tier = _get_tier(table.full_table_name)
508509
if tier in (Computed, Imported):
@@ -602,8 +603,7 @@ def get_table(self, name: str) -> FreeTable:
602603
if table_name is None:
603604
raise DataJointError(f"Table `{name}` does not exist in schema `{self.database}`.")
604605

605-
adapter = self.connection.adapter
606-
full_name = f"{adapter.quote_identifier(self.database)}.{adapter.quote_identifier(table_name)}"
606+
full_name = self.connection.adapter.make_full_table_name(self.database, table_name)
607607
return FreeTable(self.connection, full_name)
608608

609609
def __getitem__(self, name: str) -> FreeTable:

src/datajoint/table.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ def full_table_name(self):
475475
f"Class {self.__class__.__name__} is not associated with a schema. "
476476
"Apply a schema decorator or use schema() to bind it."
477477
)
478-
return f"{self.adapter.quote_identifier(self.database)}.{self.adapter.quote_identifier(self.table_name)}"
478+
return self.adapter.make_full_table_name(self.database, self.table_name)
479479

480480
@property
481481
def adapter(self):

src/datajoint/user_tables.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ def full_table_name(cls):
106106
"""The fully qualified table name (quoted per backend)."""
107107
if cls.database is None:
108108
return None
109-
adapter = cls._connection.adapter
110-
return f"{adapter.quote_identifier(cls.database)}.{adapter.quote_identifier(cls.table_name)}"
109+
return cls._connection.adapter.make_full_table_name(cls.database, cls.table_name)
111110

112111

113112
class UserTable(Table, metaclass=TableMeta):
@@ -186,8 +185,7 @@ def full_table_name(cls):
186185
"""The fully qualified table name (quoted per backend)."""
187186
if cls.database is None or cls.table_name is None:
188187
return None
189-
adapter = cls._connection.adapter
190-
return f"{adapter.quote_identifier(cls.database)}.{adapter.quote_identifier(cls.table_name)}"
188+
return cls._connection.adapter.make_full_table_name(cls.database, cls.table_name)
191189

192190
@property
193191
def master(cls):

0 commit comments

Comments
 (0)