mirror of
https://github.com/apache/superset.git
synced 2026-04-10 20:06:13 +00:00
This will help us keep track on how long it takes to push the data into the results backend.
169 lines
5.2 KiB
Python
169 lines
5.2 KiB
Python
"""A collection of ORM sqlalchemy models for SQL Lab"""
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
import re
|
|
from datetime import datetime
|
|
|
|
from future.standard_library import install_aliases
|
|
|
|
from flask import Markup
|
|
|
|
from flask_appbuilder import Model
|
|
|
|
import sqlalchemy as sqla
|
|
from sqlalchemy import (
|
|
Column, Integer, String, ForeignKey, Text, Boolean,
|
|
DateTime, Numeric,
|
|
)
|
|
from sqlalchemy.orm import backref, relationship
|
|
|
|
from superset import sm
|
|
from superset.utils import QueryStatus
|
|
from superset.models.helpers import AuditMixinNullable
|
|
|
|
install_aliases()
|
|
|
|
|
|
class Query(Model):
|
|
|
|
"""ORM model for SQL query"""
|
|
|
|
__tablename__ = 'query'
|
|
id = Column(Integer, primary_key=True)
|
|
client_id = Column(String(11), unique=True, nullable=False)
|
|
|
|
database_id = Column(Integer, ForeignKey('dbs.id'), nullable=False)
|
|
|
|
# Store the tmp table into the DB only if the user asks for it.
|
|
tmp_table_name = Column(String(256))
|
|
user_id = Column(
|
|
Integer, ForeignKey('ab_user.id'), nullable=True)
|
|
status = Column(String(16), default=QueryStatus.PENDING)
|
|
tab_name = Column(String(256))
|
|
sql_editor_id = Column(String(256))
|
|
schema = Column(String(256))
|
|
sql = Column(Text)
|
|
# Query to retrieve the results,
|
|
# used only in case of select_as_cta_used is true.
|
|
select_sql = Column(Text)
|
|
executed_sql = Column(Text)
|
|
# Could be configured in the superset config.
|
|
limit = Column(Integer)
|
|
limit_used = Column(Boolean, default=False)
|
|
select_as_cta = Column(Boolean)
|
|
select_as_cta_used = Column(Boolean, default=False)
|
|
|
|
progress = Column(Integer, default=0) # 1..100
|
|
# # of rows in the result set or rows modified.
|
|
rows = Column(Integer)
|
|
error_message = Column(Text)
|
|
# key used to store the results in the results backend
|
|
results_key = Column(String(64), index=True)
|
|
|
|
# Using Numeric in place of DateTime for sub-second precision
|
|
# stored as seconds since epoch, allowing for milliseconds
|
|
start_time = Column(Numeric(precision=20, scale=6))
|
|
start_running_time = Column(Numeric(precision=20, scale=6))
|
|
end_time = Column(Numeric(precision=20, scale=6))
|
|
end_result_backend_time = Column(Numeric(precision=20, scale=6))
|
|
|
|
changed_on = Column(
|
|
DateTime,
|
|
default=datetime.utcnow,
|
|
onupdate=datetime.utcnow,
|
|
nullable=True)
|
|
|
|
database = relationship(
|
|
'Database',
|
|
foreign_keys=[database_id],
|
|
backref=backref('queries', cascade='all, delete-orphan')
|
|
)
|
|
user = relationship(
|
|
sm.user_model,
|
|
foreign_keys=[user_id])
|
|
|
|
__table_args__ = (
|
|
sqla.Index('ti_user_id_changed_on', user_id, changed_on),
|
|
)
|
|
|
|
@property
|
|
def limit_reached(self):
|
|
return self.rows == self.limit if self.limit_used else False
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'changedOn': self.changed_on,
|
|
'changed_on': self.changed_on.isoformat(),
|
|
'dbId': self.database_id,
|
|
'db': self.database.database_name,
|
|
'endDttm': self.end_time,
|
|
'errorMessage': self.error_message,
|
|
'executedSql': self.executed_sql,
|
|
'id': self.client_id,
|
|
'limit': self.limit,
|
|
'progress': self.progress,
|
|
'rows': self.rows,
|
|
'schema': self.schema,
|
|
'ctas': self.select_as_cta,
|
|
'serverId': self.id,
|
|
'sql': self.sql,
|
|
'sqlEditorId': self.sql_editor_id,
|
|
'startDttm': self.start_time,
|
|
'state': self.status.lower(),
|
|
'tab': self.tab_name,
|
|
'tempTable': self.tmp_table_name,
|
|
'userId': self.user_id,
|
|
'user': self.user.username,
|
|
'limit_reached': self.limit_reached,
|
|
'resultsKey': self.results_key,
|
|
}
|
|
|
|
@property
|
|
def name(self):
|
|
"""Name property"""
|
|
ts = datetime.now().isoformat()
|
|
ts = ts.replace('-', '').replace(':', '').split('.')[0]
|
|
tab = (
|
|
self.tab_name.replace(' ', '_').lower()
|
|
if self.tab_name
|
|
else 'notab'
|
|
)
|
|
tab = re.sub(r'\W+', '', tab)
|
|
return "sqllab_{tab}_{ts}".format(**locals())
|
|
|
|
|
|
class SavedQuery(Model, AuditMixinNullable):
|
|
|
|
"""ORM model for SQL query"""
|
|
|
|
__tablename__ = 'saved_query'
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(
|
|
Integer, ForeignKey('ab_user.id'), nullable=True)
|
|
db_id = Column(
|
|
Integer, ForeignKey('dbs.id'), nullable=True)
|
|
schema = Column(String(128))
|
|
label = Column(String(256))
|
|
description = Column(Text)
|
|
sql = Column(Text)
|
|
user = relationship(
|
|
sm.user_model,
|
|
backref=backref('saved_queries', cascade='all, delete-orphan'),
|
|
foreign_keys=[user_id])
|
|
database = relationship(
|
|
'Database',
|
|
foreign_keys=[db_id],
|
|
backref=backref('saved_queries', cascade='all, delete-orphan')
|
|
)
|
|
|
|
@property
|
|
def pop_tab_link(self):
|
|
return Markup("""
|
|
<a href="/superset/sqllab?savedQueryId={self.id}">
|
|
<i class="fa fa-link"></i>
|
|
</a>
|
|
""".format(**locals()))
|