2015-01-13 04:20:40 +08:00
|
|
|
import datetime
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
from django.conf import settings
|
2016-10-08 09:06:49 +08:00
|
|
|
from django.core.exceptions import FieldError
|
2015-01-13 04:20:40 +08:00
|
|
|
from django.db import utils
|
|
|
|
from django.db.backends.base.operations import BaseDatabaseOperations
|
2015-01-28 20:35:27 +08:00
|
|
|
from django.db.models import aggregates, fields
|
2017-08-11 03:32:28 +08:00
|
|
|
from django.db.models.expressions import Col
|
2016-12-29 23:27:49 +08:00
|
|
|
from django.utils import timezone
|
2015-04-12 20:46:24 +08:00
|
|
|
from django.utils.dateparse import parse_date, parse_datetime, parse_time
|
2015-01-13 04:20:40 +08:00
|
|
|
from django.utils.duration import duration_string
|
|
|
|
|
|
|
|
|
|
|
|
class DatabaseOperations(BaseDatabaseOperations):
|
2017-07-28 01:36:47 +08:00
|
|
|
cast_char_field_without_max_length = 'text'
|
|
|
|
|
2015-01-13 04:20:40 +08:00
|
|
|
def bulk_batch_size(self, fields, objs):
|
|
|
|
"""
|
|
|
|
SQLite has a compile-time default (SQLITE_LIMIT_VARIABLE_NUMBER) of
|
|
|
|
999 variables per query.
|
|
|
|
|
2016-11-24 21:16:39 +08:00
|
|
|
If there's only a single field to insert, the limit is 500
|
|
|
|
(SQLITE_MAX_COMPOUND_SELECT).
|
2015-01-13 04:20:40 +08:00
|
|
|
"""
|
2017-03-25 01:37:03 +08:00
|
|
|
if len(fields) == 1:
|
|
|
|
return 500
|
|
|
|
elif len(fields) > 1:
|
|
|
|
return self.connection.features.max_query_params // len(fields)
|
|
|
|
else:
|
|
|
|
return len(objs)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
2015-01-17 13:03:46 +08:00
|
|
|
def check_expression_support(self, expression):
|
2015-01-13 04:20:40 +08:00
|
|
|
bad_fields = (fields.DateField, fields.DateTimeField, fields.TimeField)
|
2015-01-17 13:03:46 +08:00
|
|
|
bad_aggregates = (aggregates.Sum, aggregates.Avg, aggregates.Variance, aggregates.StdDev)
|
|
|
|
if isinstance(expression, bad_aggregates):
|
2015-06-04 23:25:38 +08:00
|
|
|
for expr in expression.get_source_expressions():
|
2017-09-07 20:16:21 +08:00
|
|
|
try:
|
2015-06-04 23:25:38 +08:00
|
|
|
output_field = expr.output_field
|
2017-09-07 20:16:21 +08:00
|
|
|
except FieldError:
|
|
|
|
# Not every subexpression has an output_field which is fine
|
|
|
|
# to ignore.
|
|
|
|
pass
|
|
|
|
else:
|
2015-06-04 23:25:38 +08:00
|
|
|
if isinstance(output_field, bad_fields):
|
2017-10-07 00:47:08 +08:00
|
|
|
raise utils.NotSupportedError(
|
2015-06-04 23:25:38 +08:00
|
|
|
'You cannot use Sum, Avg, StdDev, and Variance '
|
|
|
|
'aggregations on date/time fields in sqlite3 '
|
|
|
|
'since date/time is saved as text.'
|
|
|
|
)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
|
|
|
def date_extract_sql(self, lookup_type, field_name):
|
2017-01-25 07:04:12 +08:00
|
|
|
"""
|
|
|
|
Support EXTRACT with a user-defined function django_date_extract()
|
|
|
|
that's registered in connect(). Use single quotes because this is a
|
|
|
|
string and could otherwise cause a collision with a field name.
|
|
|
|
"""
|
2015-01-13 04:20:40 +08:00
|
|
|
return "django_date_extract('%s', %s)" % (lookup_type.lower(), field_name)
|
|
|
|
|
|
|
|
def date_interval_sql(self, timedelta):
|
2017-07-06 19:37:47 +08:00
|
|
|
return "'%s'" % duration_string(timedelta)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
|
|
|
def format_for_duration_arithmetic(self, sql):
|
2017-01-25 07:04:12 +08:00
|
|
|
"""Do nothing since formatting is handled in the custom function."""
|
2015-01-13 04:20:40 +08:00
|
|
|
return sql
|
|
|
|
|
|
|
|
def date_trunc_sql(self, lookup_type, field_name):
|
|
|
|
return "django_date_trunc('%s', %s)" % (lookup_type.lower(), field_name)
|
|
|
|
|
2016-06-19 11:38:24 +08:00
|
|
|
def time_trunc_sql(self, lookup_type, field_name):
|
|
|
|
return "django_time_trunc('%s', %s)" % (lookup_type.lower(), field_name)
|
|
|
|
|
2017-02-01 21:48:04 +08:00
|
|
|
def _convert_tzname_to_sql(self, tzname):
|
|
|
|
return "'%s'" % tzname if settings.USE_TZ else 'NULL'
|
|
|
|
|
2015-03-08 05:20:29 +08:00
|
|
|
def datetime_cast_date_sql(self, field_name, tzname):
|
2017-02-01 21:48:04 +08:00
|
|
|
return "django_datetime_cast_date(%s, %s)" % (
|
|
|
|
field_name, self._convert_tzname_to_sql(tzname),
|
|
|
|
)
|
2015-03-08 05:20:29 +08:00
|
|
|
|
2016-06-19 11:39:26 +08:00
|
|
|
def datetime_cast_time_sql(self, field_name, tzname):
|
2017-02-01 21:48:04 +08:00
|
|
|
return "django_datetime_cast_time(%s, %s)" % (
|
|
|
|
field_name, self._convert_tzname_to_sql(tzname),
|
|
|
|
)
|
2016-06-19 11:39:26 +08:00
|
|
|
|
2015-01-13 04:20:40 +08:00
|
|
|
def datetime_extract_sql(self, lookup_type, field_name, tzname):
|
2017-02-01 21:48:04 +08:00
|
|
|
return "django_datetime_extract('%s', %s, %s)" % (
|
|
|
|
lookup_type.lower(), field_name, self._convert_tzname_to_sql(tzname),
|
|
|
|
)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
|
|
|
def datetime_trunc_sql(self, lookup_type, field_name, tzname):
|
2017-02-01 21:48:04 +08:00
|
|
|
return "django_datetime_trunc('%s', %s, %s)" % (
|
|
|
|
lookup_type.lower(), field_name, self._convert_tzname_to_sql(tzname),
|
|
|
|
)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
2015-05-23 03:16:26 +08:00
|
|
|
def time_extract_sql(self, lookup_type, field_name):
|
|
|
|
return "django_time_extract('%s', %s)" % (lookup_type.lower(), field_name)
|
|
|
|
|
2015-01-13 04:20:40 +08:00
|
|
|
def pk_default_value(self):
|
|
|
|
return "NULL"
|
|
|
|
|
2015-09-13 15:30:35 +08:00
|
|
|
def _quote_params_for_last_executed_query(self, params):
|
|
|
|
"""
|
|
|
|
Only for last_executed_query! Don't use this to execute SQL queries!
|
|
|
|
"""
|
2016-01-10 04:30:19 +08:00
|
|
|
# This function is limited both by SQLITE_LIMIT_VARIABLE_NUMBER (the
|
2016-01-23 19:51:37 +08:00
|
|
|
# number of parameters, default = 999) and SQLITE_MAX_COLUMN (the
|
2016-01-10 04:30:19 +08:00
|
|
|
# number of return values, default = 2000). Since Python's sqlite3
|
|
|
|
# module doesn't expose the get_limit() C API, assume the default
|
|
|
|
# limits are in effect and split the work in batches if needed.
|
|
|
|
BATCH_SIZE = 999
|
|
|
|
if len(params) > BATCH_SIZE:
|
|
|
|
results = ()
|
|
|
|
for index in range(0, len(params), BATCH_SIZE):
|
|
|
|
chunk = params[index:index + BATCH_SIZE]
|
|
|
|
results += self._quote_params_for_last_executed_query(chunk)
|
|
|
|
return results
|
|
|
|
|
2015-09-13 15:30:35 +08:00
|
|
|
sql = 'SELECT ' + ', '.join(['QUOTE(?)'] * len(params))
|
|
|
|
# Bypass Django's wrappers and use the underlying sqlite3 connection
|
|
|
|
# to avoid logging this query - it would trigger infinite recursion.
|
|
|
|
cursor = self.connection.connection.cursor()
|
|
|
|
# Native sqlite3 cursors cannot be used as context managers.
|
|
|
|
try:
|
|
|
|
return cursor.execute(sql, params).fetchone()
|
|
|
|
finally:
|
|
|
|
cursor.close()
|
|
|
|
|
|
|
|
def last_executed_query(self, cursor, sql, params):
|
|
|
|
# Python substitutes parameters in Modules/_sqlite/cursor.c with:
|
|
|
|
# pysqlite_statement_bind_parameters(self->statement, parameters, allow_8bit_chars);
|
|
|
|
# Unfortunately there is no way to reach self->statement from Python,
|
|
|
|
# so we quote and substitute parameters manually.
|
|
|
|
if params:
|
|
|
|
if isinstance(params, (list, tuple)):
|
|
|
|
params = self._quote_params_for_last_executed_query(params)
|
|
|
|
else:
|
|
|
|
values = tuple(params.values())
|
|
|
|
values = self._quote_params_for_last_executed_query(values)
|
2017-05-28 07:08:46 +08:00
|
|
|
params = dict(zip(params, values))
|
2015-09-13 15:30:35 +08:00
|
|
|
return sql % params
|
|
|
|
# For consistency with SQLiteCursorWrapper.execute(), just return sql
|
|
|
|
# when there are no parameters. See #13648 and #17158.
|
|
|
|
else:
|
|
|
|
return sql
|
|
|
|
|
2015-01-13 04:20:40 +08:00
|
|
|
def quote_name(self, name):
|
|
|
|
if name.startswith('"') and name.endswith('"'):
|
|
|
|
return name # Quoting once is enough.
|
|
|
|
return '"%s"' % name
|
|
|
|
|
|
|
|
def no_limit_value(self):
|
|
|
|
return -1
|
|
|
|
|
|
|
|
def sql_flush(self, style, tables, sequences, allow_cascade=False):
|
|
|
|
sql = ['%s %s %s;' % (
|
|
|
|
style.SQL_KEYWORD('DELETE'),
|
|
|
|
style.SQL_KEYWORD('FROM'),
|
|
|
|
style.SQL_FIELD(self.quote_name(table))
|
|
|
|
) for table in tables]
|
|
|
|
# Note: No requirement for reset of auto-incremented indices (cf. other
|
|
|
|
# sql_flush() implementations). Just return SQL at this point
|
|
|
|
return sql
|
|
|
|
|
2015-01-11 00:24:16 +08:00
|
|
|
def execute_sql_flush(self, using, sql_list):
|
|
|
|
# To prevent possible violation of foreign key constraints, deactivate
|
|
|
|
# constraints outside of the transaction created in super().
|
|
|
|
with self.connection.constraint_checks_disabled():
|
|
|
|
super().execute_sql_flush(using, sql_list)
|
|
|
|
|
2015-05-03 03:27:44 +08:00
|
|
|
def adapt_datetimefield_value(self, value):
|
2015-01-13 04:20:40 +08:00
|
|
|
if value is None:
|
|
|
|
return None
|
|
|
|
|
2016-06-03 02:05:25 +08:00
|
|
|
# Expression values are adapted by the database.
|
|
|
|
if hasattr(value, 'resolve_expression'):
|
|
|
|
return value
|
|
|
|
|
2015-01-13 04:20:40 +08:00
|
|
|
# SQLite doesn't support tz-aware datetimes
|
|
|
|
if timezone.is_aware(value):
|
|
|
|
if settings.USE_TZ:
|
2015-05-03 03:56:53 +08:00
|
|
|
value = timezone.make_naive(value, self.connection.timezone)
|
2015-01-13 04:20:40 +08:00
|
|
|
else:
|
|
|
|
raise ValueError("SQLite backend does not support timezone-aware datetimes when USE_TZ is False.")
|
|
|
|
|
2016-12-29 23:27:49 +08:00
|
|
|
return str(value)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
2015-05-03 03:27:44 +08:00
|
|
|
def adapt_timefield_value(self, value):
|
2015-01-13 04:20:40 +08:00
|
|
|
if value is None:
|
|
|
|
return None
|
|
|
|
|
2016-06-03 02:05:25 +08:00
|
|
|
# Expression values are adapted by the database.
|
|
|
|
if hasattr(value, 'resolve_expression'):
|
|
|
|
return value
|
|
|
|
|
2015-01-13 04:20:40 +08:00
|
|
|
# SQLite doesn't support tz-aware datetimes
|
|
|
|
if timezone.is_aware(value):
|
|
|
|
raise ValueError("SQLite backend does not support timezone-aware times.")
|
|
|
|
|
2016-12-29 23:27:49 +08:00
|
|
|
return str(value)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
|
|
|
def get_db_converters(self, expression):
|
2017-01-21 21:13:44 +08:00
|
|
|
converters = super().get_db_converters(expression)
|
2015-01-13 04:20:40 +08:00
|
|
|
internal_type = expression.output_field.get_internal_type()
|
|
|
|
if internal_type == 'DateTimeField':
|
|
|
|
converters.append(self.convert_datetimefield_value)
|
|
|
|
elif internal_type == 'DateField':
|
|
|
|
converters.append(self.convert_datefield_value)
|
|
|
|
elif internal_type == 'TimeField':
|
|
|
|
converters.append(self.convert_timefield_value)
|
2017-08-11 03:32:28 +08:00
|
|
|
# Converter for Col is added with Database.register_converter()
|
|
|
|
# in base.py.
|
|
|
|
elif internal_type == 'DecimalField' and not isinstance(expression, Col):
|
2015-01-13 04:20:40 +08:00
|
|
|
converters.append(self.convert_decimalfield_value)
|
|
|
|
elif internal_type == 'UUIDField':
|
|
|
|
converters.append(self.convert_uuidfield_value)
|
2016-11-15 03:47:20 +08:00
|
|
|
elif internal_type in ('NullBooleanField', 'BooleanField'):
|
|
|
|
converters.append(self.convert_booleanfield_value)
|
2015-01-13 04:20:40 +08:00
|
|
|
return converters
|
|
|
|
|
2017-07-07 01:18:05 +08:00
|
|
|
def convert_datetimefield_value(self, value, expression, connection):
|
2015-04-12 20:46:24 +08:00
|
|
|
if value is not None:
|
|
|
|
if not isinstance(value, datetime.datetime):
|
|
|
|
value = parse_datetime(value)
|
2016-11-30 01:23:44 +08:00
|
|
|
if settings.USE_TZ and not timezone.is_aware(value):
|
2015-05-03 03:56:53 +08:00
|
|
|
value = timezone.make_aware(value, self.connection.timezone)
|
2015-01-13 04:20:40 +08:00
|
|
|
return value
|
|
|
|
|
2017-07-07 01:18:05 +08:00
|
|
|
def convert_datefield_value(self, value, expression, connection):
|
2015-04-13 02:33:58 +08:00
|
|
|
if value is not None:
|
|
|
|
if not isinstance(value, datetime.date):
|
|
|
|
value = parse_date(value)
|
|
|
|
return value
|
|
|
|
|
2017-07-07 01:18:05 +08:00
|
|
|
def convert_timefield_value(self, value, expression, connection):
|
2015-04-12 20:46:24 +08:00
|
|
|
if value is not None:
|
|
|
|
if not isinstance(value, datetime.time):
|
|
|
|
value = parse_time(value)
|
2015-01-13 04:20:40 +08:00
|
|
|
return value
|
|
|
|
|
2017-07-07 01:18:05 +08:00
|
|
|
def convert_decimalfield_value(self, value, expression, connection):
|
2015-04-13 02:33:58 +08:00
|
|
|
if value is not None:
|
|
|
|
value = expression.output_field.format_number(value)
|
2017-08-11 03:32:28 +08:00
|
|
|
# Value is not converted to Decimal here as it will be converted
|
|
|
|
# later in BaseExpression.convert_value().
|
2015-04-13 02:33:58 +08:00
|
|
|
return value
|
|
|
|
|
2017-07-07 01:18:05 +08:00
|
|
|
def convert_uuidfield_value(self, value, expression, connection):
|
2015-01-13 04:20:40 +08:00
|
|
|
if value is not None:
|
|
|
|
value = uuid.UUID(value)
|
|
|
|
return value
|
|
|
|
|
2017-07-07 01:18:05 +08:00
|
|
|
def convert_booleanfield_value(self, value, expression, connection):
|
2016-11-15 03:47:20 +08:00
|
|
|
return bool(value) if value in (1, 0) else value
|
|
|
|
|
2015-08-03 22:34:19 +08:00
|
|
|
def bulk_insert_sql(self, fields, placeholder_rows):
|
|
|
|
return " UNION ALL ".join(
|
|
|
|
"SELECT %s" % ", ".join(row)
|
|
|
|
for row in placeholder_rows
|
|
|
|
)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
|
|
|
def combine_expression(self, connector, sub_expressions):
|
|
|
|
# SQLite doesn't have a power function, so we fake it with a
|
|
|
|
# user-defined function django_power that's registered in connect().
|
|
|
|
if connector == '^':
|
|
|
|
return 'django_power(%s)' % ','.join(sub_expressions)
|
2017-01-21 21:13:44 +08:00
|
|
|
return super().combine_expression(connector, sub_expressions)
|
2015-01-13 04:20:40 +08:00
|
|
|
|
|
|
|
def combine_duration_expression(self, connector, sub_expressions):
|
|
|
|
if connector not in ['+', '-']:
|
|
|
|
raise utils.DatabaseError('Invalid connector for timedelta: %s.' % connector)
|
|
|
|
fn_params = ["'%s'" % connector] + sub_expressions
|
|
|
|
if len(fn_params) > 3:
|
|
|
|
raise ValueError('Too many params for timedelta operations.')
|
|
|
|
return "django_format_dtdelta(%s)" % ', '.join(fn_params)
|
|
|
|
|
|
|
|
def integer_field_range(self, internal_type):
|
|
|
|
# SQLite doesn't enforce any integer constraints
|
|
|
|
return (None, None)
|
2016-01-20 09:43:41 +08:00
|
|
|
|
|
|
|
def subtract_temporals(self, internal_type, lhs, rhs):
|
|
|
|
lhs_sql, lhs_params = lhs
|
|
|
|
rhs_sql, rhs_params = rhs
|
|
|
|
if internal_type == 'TimeField':
|
|
|
|
return "django_time_diff(%s, %s)" % (lhs_sql, rhs_sql), lhs_params + rhs_params
|
|
|
|
return "django_timestamp_diff(%s, %s)" % (lhs_sql, rhs_sql), lhs_params + rhs_params
|