|
| 1 | +## |
| 2 | +# copyright 2009, James William Pye |
| 3 | +# http://python.projects.postgresql.org |
| 4 | +## |
| 5 | +""" |
| 6 | +`postgresql.driver` |
| 7 | +=================== |
| 8 | +
|
| 9 | +The `postgresql.driver` implements PG-API using PQ version 3.0 to connect to |
| 10 | +PostgreSQL servers. It makes use of the protocol's extended features to provide |
| 11 | +binary datatype transmission and protocol level prepared statements. |
| 12 | +
|
| 13 | +Connecting |
| 14 | +---------- |
| 15 | +
|
| 16 | +Connecting to PostgreSQL using `postgresql.driver` is very simple:: |
| 17 | +
|
| 18 | + >>> import postgresql.driver as pg_driver |
| 19 | + >>> pg_con = pg_driver.connect(user = 'usename', password = 'secret', host = 'localhost', port = 5432) |
| 20 | +
|
| 21 | +It's that easy. |
| 22 | +
|
| 23 | +NOTE: `connect` will *not* inherit parameters from the environment as |
| 24 | +libpq-based drivers do. The `postgresql.documentation.driver_details` discusses |
| 25 | +means of gathering standard client parameters. |
| 26 | +
|
| 27 | +Keyword parameters accepted by `connect`: |
| 28 | +
|
| 29 | + user |
| 30 | + The user to connect as. |
| 31 | + password |
| 32 | + The user's password. |
| 33 | + database |
| 34 | + The database to connect to. (PostgreSQL defaults it to `user`) |
| 35 | + host |
| 36 | + The hostname or IP address to connect to. |
| 37 | + port |
| 38 | + The port on the host to connect to. |
| 39 | + settings |
| 40 | + A dictionary or key-value pair sequence stating the parameters to give to the |
| 41 | + database. These settings are included in the startup packet. |
| 42 | +
|
| 43 | + connect_timeout |
| 44 | + Amount of time to wait for a connection to be made. (in seconds) |
| 45 | + Raises `postgresql.exceptions.ConnectionTimeoutError` when triggered. |
| 46 | + server_encoding |
| 47 | + Hint given to the driver to properly encode password data and some information |
| 48 | + in the startup packet. |
| 49 | + This should only be used in cases where connections cannot be made due to |
| 50 | + authentication failures that occur while using known-correct credentials. |
| 51 | +
|
| 52 | + sslmode |
| 53 | + How do to decide whether or not to use SSL: |
| 54 | + 'disallow' |
| 55 | + Don't allow SSL connections. |
| 56 | + 'allow' |
| 57 | + Try without SSL, but if that doesn't work, try with. |
| 58 | + 'prefer' |
| 59 | + Try SSL first, then without. |
| 60 | + 'require' |
| 61 | +
|
| 62 | + sslcrtfile |
| 63 | + Certificate file path given to `ssl.wrap_socket`. |
| 64 | + sslkeyfile |
| 65 | + Key file path given to `ssl.wrap_socket`. |
| 66 | + sslrootcrtfile |
| 67 | + Root certificate file path given to `ssl.wrap_socket` |
| 68 | + sslrootcrlfile |
| 69 | + Revocation list file path. [Throws a NotSupportedWarning] |
| 70 | +
|
| 71 | +From here on `db` will be assumed to exist and serve as the documentation's |
| 72 | +`postresql.api.Connection` instance. |
| 73 | +
|
| 74 | +Querying |
| 75 | +-------- |
| 76 | +
|
| 77 | +Querying PostgreSQL is very easy. A statement object is created, a |
| 78 | +`PreparedStatement` instance, using the `prepare` method on the connection |
| 79 | +object:: |
| 80 | +
|
| 81 | + >>> my_statement = db.prepare("SELECT 'hello, world!'") |
| 82 | +
|
| 83 | +This creates a bound statement object, so it's only usable on the `db` |
| 84 | +connection. While this may seem to be a trifle for some situations, it's very |
| 85 | +handy to be able to pass around the statement object without having to explicitly |
| 86 | +carry the connection object with it. |
| 87 | +
|
| 88 | +Now, to execute it:: |
| 89 | +
|
| 90 | + >>> my_results = my_statement() |
| 91 | +
|
| 92 | +Just like executing a function. In this case, invoking `my_statement` will return a |
| 93 | +`Cursor` object to the result set. |
| 94 | +
|
| 95 | +NOTE: Don't confuse PG-API cursors with DB-API cursors. PG-API cursors are SQL |
| 96 | +cursors and don't contain methods for executing more queries within the cursor. |
| 97 | +
|
| 98 | +Cursor objects have a couple ways to read data from them: |
| 99 | +
|
| 100 | + ``next(my_results)`` |
| 101 | + This fetches the next row in the cursor object. Cursors support the iterator |
| 102 | + protocol, you can just as easily: |
| 103 | +
|
| 104 | + ``for nextrow in my_results:`` |
| 105 | + This will, of course, get the ``nextrow`` in the cursor, ``my_results``, until |
| 106 | + the cursor is exhausted. This and the former way of reading rows use the same |
| 107 | + method, ``__next__``, which is part of the Iterator ABC. |
| 108 | +
|
| 109 | + ``my_results.read(5)`` |
| 110 | + This method name is borrowed from `file` objects, and are semantically |
| 111 | + similar. However, this being a cursor, rows are returned instead of bytes or |
| 112 | + characters. In this case, five rows are requested, but certainly only one will |
| 113 | + come back: ``[('hello, world!',)]``. When the number of rows returned is less |
| 114 | + then the number requested, it means that cursor has been exhausted, and there |
| 115 | + are no more rows to be read. |
| 116 | +
|
| 117 | +Cursors have other methods, but not for reading more tuples. These other methods |
| 118 | +will be discuessed later. |
| 119 | +
|
| 120 | +As cursors have a couple methods for reading tuples, queries a few methods for |
| 121 | +executing the prepared statement: |
| 122 | +
|
| 123 | + ``__call__(...)`` |
| 124 | + As shown before, statement objects can be simply invoked like a function to get a |
| 125 | + cursor to the statement's results. |
| 126 | +
|
| 127 | + ``first(...)`` |
| 128 | + For simple queries, a cursor object can be a bit tiresome to get data from, |
| 129 | + consider the data contained in ``my_results``, 'hello world!'. To get at this |
| 130 | + data directly from the ``__call__(...)`` method, it looks something like:: |
| 131 | +
|
| 132 | + >>> my_statement().read()[0][0] |
| 133 | +
|
| 134 | + While it's certainly easy to understand, it can be quite cumbersome and |
| 135 | + perhaps even error prone for more complicated queries returing single values. |
| 136 | +
|
| 137 | + To simplify access to simple data, the ``first`` method will simply return |
| 138 | + the "first" of the result set. |
| 139 | +
|
| 140 | + The first value: When there is a single row with a single column, ``first()`` |
| 141 | + will return the contents of that cell. |
| 142 | +
|
| 143 | + The first row: When there is a single row with multiple columns, ``first()`` |
| 144 | + will return that row. |
| 145 | +
|
| 146 | + The first column: When there is a single column with multiple rows, |
| 147 | + ``first()`` will return that column as a sequence. |
| 148 | +
|
| 149 | + The first row count: When DML--for instance, an INSERT-statement--is executed, |
| 150 | + ``first()`` will return the row count returned by the statement as an integer. |
| 151 | +
|
| 152 | + The result set created by the statement determines what is actually returned. |
| 153 | + Naturally, a statement used with ``first()`` should be crafted with these |
| 154 | + rules in mind. |
| 155 | +
|
| 156 | +Statement objects can take parameters. To do this, the statement must be defined using |
| 157 | +PostgreSQL's positional parameter notation. ``$1``, ``$2``, ``$3``, etc. If the |
| 158 | +statement object ``my_statement`` were to be re-written to take a parameter, it would be |
| 159 | +done simply:: |
| 160 | +
|
| 161 | + >>> my_statement = db.prepare("SELECT $1") |
| 162 | +
|
| 163 | +And, re-create the ``my_cursor``:: |
| 164 | +
|
| 165 | + >>> my_cursor = my_statement('hello, world!') |
| 166 | +
|
| 167 | +It's that easy. And using ``first()``:: |
| 168 | +
|
| 169 | + >>> 'hello, world!' == my_statement.first('hello, world!') |
| 170 | + True |
| 171 | +
|
| 172 | +Inserting |
| 173 | +--------- |
| 174 | +
|
| 175 | +Copying |
| 176 | +------- |
| 177 | +
|
| 178 | +Transacting |
| 179 | +----------- |
| 180 | +
|
| 181 | +Setting |
| 182 | +------- |
| 183 | +""" |
| 184 | + |
| 185 | +__docformat__ = 'reStructured Text' |
| 186 | +if __name__ == '__main__': |
| 187 | + import sys |
| 188 | + if (sys.argv + [None])[1] == 'dump': |
| 189 | + sys.stdout.write(__doc__) |
| 190 | + else: |
| 191 | + try: |
| 192 | + help(__package__ + '.driver_basics') |
| 193 | + except NameError: |
| 194 | + help(__name__) |
0 commit comments