Skip to content

Commit

Permalink
Merge pull request #187 from informatics-isi-edu/sqlite_ddl
Browse files Browse the repository at this point in the history
add Table.sqlite3_ddl() method that returns a SQL DDL statement
  • Loading branch information
karlcz authored Oct 29, 2024
2 parents bc7c6d8 + 9b0c634 commit 3761a14
Showing 1 changed file with 85 additions and 1 deletion.
86 changes: 85 additions & 1 deletion deriva/core/ermrest_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ def truncate(s, maxlen):
# last-ditch (e.g. multibyte unicode suffix worst case)
return truncate(naive_result, 55) + naive_hash

def sql_identifier(s):
# double " to protect from SQL
return '"%s"' % (s.replace('"', '""'))

def sql_literal(v):
if v is None:
return 'NULL'
if type(v) is list:
s = json.dumps(v)
# double ' to protect from SQL
s = '%s' % v
return "'%s'" % (s.replace("'", "''"))

def presence_annotation(tag_uri):
"""Decorator to establish property getter/setter/deleter for presence annotations.
Expand Down Expand Up @@ -2060,7 +2073,39 @@ def find_associations(self, min_arity=2, max_arity=2, unqualified=True, pure=Tru
# arbitrarily choose first fkey to self
# in case association is back to same table
break


def sqlite3_table_name(self) -> str:
"""Return SQLite3 mapped table name for this table"""
return "%s:%s" % (
self.schema.name,
self.name,
)

def sqlite3_ddl(self, keys: bool=True) -> str:
"""Return SQLite3 table definition DDL statement for this table.
:param keys: If true, include unique constraints for each table key
Caveat: this utility does not produce:
- column default expressions
- foreign key constraint DDL
Both of these features are fragile in data export scenarios
where we want to represent arbitrary ERMrest catalog dumps.
"""
parts = [ col.sqlite3_ddl() for col in self.columns ]
if keys:
parts.extend([ key.sqlite3_ddl() for key in self.keys ])
return ("""
CREATE TABLE IF NOT EXISTS %(tname)s
%(body)s
);
""" % {
'tname': sql_identifier(self.sqlite3_table_name()),
'body': ',\n '.join(parts),
})

@presence_annotation(tag.immutable)
def immutable(self): pass

Expand Down Expand Up @@ -2363,6 +2408,16 @@ def drop(self, cascade=False, update_mappings=UpdateMappings.no_update):
if update_mappings == UpdateMappings.immediate:
self.table.schema.model.apply()

def sqlite3_ddl(self) -> str:
"""Return SQLite3 column definition DDL fragment for this column."""
parts = [
sql_identifier(self.name),
self.type.sqlite3_ddl(),
]
if not self.nullok:
parts.append('NOT NULL')
return ' '.join(parts)

@presence_annotation(tag.immutable)
def immutable(self): pass

Expand Down Expand Up @@ -2619,6 +2674,11 @@ def drop(self, cascade=False, update_mappings=UpdateMappings.no_update):
if update_mappings == UpdateMappings.immediate:
self.table.schema.model.apply()

def sqlite3_ddl(self) -> str:
"""Return SQLite3 unique constraint DDL fragment for this key."""
parts = [ sql_identifier(col.name) for col in self.unique_columns ]
return 'UNIQUE (%s)' % (', '.join(parts),)

class ForeignKey (object):
"""Named foreign key.
"""
Expand Down Expand Up @@ -2980,6 +3040,22 @@ def prejson(self, prune=True):
}
return d

def sqlite3_ddl(self) -> str:
"""Return a SQLite3 column type DDL fragment for this type"""
return {
'boolean': 'boolean',
'date': 'date',
'float4': 'real',
'float8': 'real',
'int2': 'integer',
'int4': 'integer',
'int8': 'integer',
'json': 'json',
'jsonb': 'json',
'timestamptz': 'datetime',
'timestamp': 'datetime',
}.get(self.typename, 'text')

class DomainType (Type):
"""Named domain type.
"""
Expand All @@ -2996,6 +3072,10 @@ def prejson(self, prune=True):
})
return d

def sqlite3_ddl(self) -> str:
"""Return a SQLite3 column type DDL fragment for this type"""
return self.base_type.sqlite3_ddl()

class ArrayType (Type):
"""Named domain type.
"""
Expand All @@ -3012,6 +3092,10 @@ def prejson(self, prune=True):
})
return d

def sqlite3_ddl(self) -> str:
"""Return a SQLite3 column type DDL fragment for this type"""
return 'json'

builtin_types = AttrDict(
# first define standard scalar types
{
Expand Down

0 comments on commit 3761a14

Please sign in to comment.