Add support for database connection parameters to configuration URI
issue10101 review339891002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add support for database connection parameters to configuration URI
|
||||
* Use immutable datastructures for Dict and MultiSelection fields
|
||||
* Skip warnings for non-interactive operations
|
||||
* Check rule only if _check_access is set
|
||||
|
||||
@@ -106,7 +106,10 @@ uri
|
||||
Contains the URI to connect to the SQL database. The URI follows the RFC-3986_.
|
||||
The typical form is:
|
||||
|
||||
database://username:password@host:port/
|
||||
database://username:password@host:port/?param1=value1¶m2=value2
|
||||
|
||||
The parameters are database dependent, check the database documentation for a
|
||||
list of valid parameters.
|
||||
|
||||
Default: The value of the environment variable ``TRYTOND_DATABASE_URI`` or
|
||||
``sqlite://`` if not set.
|
||||
@@ -116,15 +119,27 @@ The available databases are:
|
||||
PostgreSQL
|
||||
**********
|
||||
|
||||
``pyscopg2`` supports two type of connections:
|
||||
``psycopg2`` supports two type of connections:
|
||||
|
||||
- TCP/IP connection: ``postgresql://user:password@localhost:5432/``
|
||||
- Unix domain connection: ``postgresql://username:password@/``
|
||||
|
||||
Please refer to `psycopg2 for the complete specification of the URI
|
||||
<https://www.psycopg.org/docs/module.html#psycopg2.connect>`_.
|
||||
|
||||
A list of parameters supported by PostgreSQL can be found in the
|
||||
`documentation <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS>`_.
|
||||
|
||||
SQLite
|
||||
******
|
||||
|
||||
The only possible URI is: ``sqlite://``
|
||||
The URI is defined as ``sqlite://``
|
||||
|
||||
If the name of the database is ``:memory:``, the parameter ``mode`` will be set
|
||||
to ``memory`` thus using a pure in-memory database.
|
||||
|
||||
The recognized query parameters can be found in SQLite's
|
||||
`documentation <https://www.sqlite.org/uri.html#recognized_query_parameters>`_.
|
||||
|
||||
path
|
||||
~~~~
|
||||
|
||||
@@ -4,8 +4,8 @@ from collections import defaultdict
|
||||
import time
|
||||
import logging
|
||||
import os
|
||||
import urllib.parse
|
||||
import json
|
||||
import warnings
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from itertools import chain, repeat
|
||||
@@ -243,17 +243,11 @@ class Database(DatabaseInterface):
|
||||
@classmethod
|
||||
def _connection_params(cls, name):
|
||||
uri = parse_uri(config.get('database', 'uri'))
|
||||
if uri.path:
|
||||
warnings.warn("The path specified in the URI will be overridden")
|
||||
params = {
|
||||
'dbname': name,
|
||||
'dsn': uri._replace(path=name).geturl(),
|
||||
}
|
||||
if uri.username:
|
||||
params['user'] = uri.username
|
||||
if uri.password:
|
||||
params['password'] = urllib.parse.unquote_plus(uri.password)
|
||||
if uri.hostname:
|
||||
params['host'] = uri.hostname
|
||||
if uri.port:
|
||||
params['port'] = uri.port
|
||||
return params
|
||||
|
||||
def connect(self):
|
||||
|
||||
@@ -7,8 +7,11 @@ import os
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
import urllib.parse
|
||||
import warnings
|
||||
from decimal import Decimal
|
||||
from weakref import WeakKeyDictionary
|
||||
from werkzeug.security import safe_join
|
||||
|
||||
try:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
@@ -24,7 +27,7 @@ from sql.functions import (Function, Extract, Position, Substring,
|
||||
Overlay, CharLength, CurrentTimestamp, Trim)
|
||||
|
||||
from trytond.backend.database import DatabaseInterface, SQLType
|
||||
from trytond.config import config
|
||||
from trytond.config import config, parse_uri
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
|
||||
@@ -336,16 +339,10 @@ class Database(DatabaseInterface):
|
||||
Database._local.memory_database = self
|
||||
|
||||
def connect(self):
|
||||
if self.name == ':memory:':
|
||||
path = ':memory:'
|
||||
else:
|
||||
db_filename = self.name + '.sqlite'
|
||||
path = os.path.join(config.get('database', 'path'), db_filename)
|
||||
if not os.path.isfile(path):
|
||||
raise IOError('Database "%s" doesn\'t exist!' % path)
|
||||
if self._conn is not None:
|
||||
return self
|
||||
self._conn = sqlite.connect(path,
|
||||
self._conn = sqlite.connect(
|
||||
self._make_uri(), uri=True,
|
||||
detect_types=sqlite.PARSE_DECLTYPES | sqlite.PARSE_COLNAMES,
|
||||
factory=SQLiteConnection)
|
||||
self._conn.create_function('extract', 2, SQLiteExtract.extract)
|
||||
@@ -402,6 +399,28 @@ class Database(DatabaseInterface):
|
||||
self._conn.execute('PRAGMA foreign_keys = ON')
|
||||
return self
|
||||
|
||||
def _make_uri(self):
|
||||
uri = config.get('database', 'uri')
|
||||
base_uri = parse_uri(uri)
|
||||
if base_uri.path:
|
||||
warnings.warn("The path specified in the URI will be overridden")
|
||||
|
||||
if self.name == ':memory:':
|
||||
query_string = urllib.parse.parse_qs(base_uri.query)
|
||||
query_string['mode'] = 'memory'
|
||||
query = urllib.parse.urlencode(query_string, doseq=True)
|
||||
db_uri = base_uri._replace(netloc='', path='/', query=query)
|
||||
else:
|
||||
db_path = safe_join(
|
||||
config.get('database', 'path'), self.name + '.sqlite')
|
||||
db_uri = base_uri._replace(path=db_path)
|
||||
|
||||
# Use unparse before replacing sqlite with file because SQLite accepts
|
||||
# a relative path URI like file:db/test.sqlite which doesn't conform to
|
||||
# RFC8089 which urllib follows and enforces when the scheme is 'file'
|
||||
db_uri = urllib.parse.urlunparse(db_uri)
|
||||
return db_uri.replace('sqlite', 'file', 1)
|
||||
|
||||
def get_connection(self, autocommit=False, readonly=False):
|
||||
if self._conn is None:
|
||||
self.connect()
|
||||
|
||||
Reference in New Issue
Block a user