]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/database.py
4 A lightweight wrapper around psycopg2.
6 Originally part of the Tornado framework. The tornado.database module
7 is slated for removal in Tornado 3.0, and it is now available separately
21 log
= logging
.getLogger()
23 class Connection(object):
25 A lightweight wrapper around MySQLdb DB-API connections.
27 The main value we provide is wrapping rows in a dict/object so that
28 columns can be accessed by name. Typical usage::
30 db = torndb.Connection("localhost", "mydatabase")
31 for article in db.query("SELECT * FROM articles"):
34 Cursors are hidden by the implementation, but other than that, the methods
35 are very similar to the DB-API.
37 We explicitly set the timezone to UTC and the character encoding to
38 UTF-8 on all connections to avoid time zone and encoding errors.
40 def __init__(self
, backend
, host
, database
, user
=None, password
=None):
41 self
.backend
= backend
43 # Stores connections assigned to tasks
44 self
.__connections
= {}
46 # Create a connection pool
47 self
.pool
= psycopg_pool
.ConnectionPool(
48 "postgresql://%s:%s@%s/%s" % (user
, password
, host
, database
),
50 # Callback to configure any new connections
51 configure
=self
.__configure
,
53 # Set limits for min/max connections in the pool
57 # Give clients up to one minute to retrieve a connection
60 # Close connections after they have been idle for a few seconds
64 def __configure(self
, conn
):
66 Configures any newly opened connections
69 conn
.autocommit
= True
71 # Return any rows as dicts
72 conn
.row_factory
= psycopg
.rows
.dict_row
74 # Automatically convert DataObjects
75 conn
.adapters
.register_dumper(misc
.Object
, misc
.ObjectDumper
)
77 def connection(self
, *args
, **kwargs
):
79 Returns a connection from the pool
81 # Fetch the current task
82 task
= asyncio
.current_task()
84 assert task
, "Could not determine task"
86 # Try returning the same connection to the same task
88 return self
.__connections
[task
]
92 # Fetch a new connection from the pool
93 conn
= self
.__connections
[task
] = self
.pool
.getconn(*args
, **kwargs
)
95 log
.debug("Assigning database connection %s to %s" % (conn
, task
))
97 # When the task finishes, release the connection
98 task
.add_done_callback(self
.__release
_connection
)
102 def __release_connection(self
, task
):
103 # Retrieve the connection
105 conn
= self
.__connections
[task
]
109 log
.debug("Releasing database connection %s of %s" % (conn
, task
))
112 del self
.__connections
[task
]
114 # Return the connection back into the pool
115 self
.pool
.putconn(conn
)
117 def _execute(self
, cursor
, execute
, query
, parameters
):
118 # Store the time we started this query
122 log
.debug("Running SQL query %s" % (query
% parameters
))
127 execute(query
, parameters
)
129 # How long did this take?
130 elapsed
= time
.monotonic() - t
133 log
.debug(" Query time: %.2fms" % (elapsed
* 1000))
135 def query(self
, query
, *parameters
, **kwparameters
):
137 Returns a row list for the given query and parameters.
139 conn
= self
.connection()
141 with conn
.cursor() as cursor
:
142 self
._execute
(cursor
, cursor
.execute
, query
, parameters
or kwparameters
)
144 return [Row(row
) for row
in cursor
]
146 def get(self
, query
, *parameters
, **kwparameters
):
148 Returns the first row returned for the given query.
150 rows
= self
.query(query
, *parameters
, **kwparameters
)
154 raise Exception("Multiple rows returned for Database.get() query")
158 def execute(self
, query
, *parameters
, **kwparameters
):
160 Executes the given query.
162 conn
= self
.connection()
164 with conn
.cursor() as cursor
:
165 self
._execute
(cursor
, cursor
.execute
, query
, parameters
or kwparameters
)
167 def executemany(self
, query
, parameters
):
169 Executes the given query against all the given param sequences.
171 conn
= self
.connection()
173 with conn
.cursor() as cursor
:
174 self
._execute
(cursor
, cursor
.executemany
, query
, parameters
)
176 def transaction(self
):
178 Creates a new transaction on the current tasks' connection
180 conn
= self
.connection()
182 return conn
.transaction()
184 def fetch_one(self
, cls
, query
, *args
, **kwargs
):
186 Takes a class and a query and will return one object of that class
189 res
= self
.get(query
, *args
)
191 # Return an object (if possible)
193 return cls(self
.backend
, res
.id, res
, **kwargs
)
195 def fetch_many(self
, cls
, query
, *args
, **kwargs
):
197 res
= self
.query(query
, *args
)
199 # Return a generator with objects
201 yield cls(self
.backend
, row
.id, row
, **kwargs
)
205 """A dict that allows for object-like property access syntax."""
206 def __getattr__(self
, name
):
210 raise AttributeError(name
)