From: Frederic Massart Date: Tue, 22 Jul 2014 02:31:43 +0000 (+0800) Subject: Prefer using external packages rather than shipping PgSQL lib X-Git-Tag: v1.3~13 X-Git-Url: https://git.cameron1729.xyz/?a=commitdiff_plain;h=289a2e12926f5795a60d62ce0f6b38462b1caa4a;p=mdk.git Prefer using external packages rather than shipping PgSQL lib --- diff --git a/mdk/bpgsql/CHANGES b/mdk/bpgsql/CHANGES deleted file mode 100644 index 5211aac..0000000 --- a/mdk/bpgsql/CHANGES +++ /dev/null @@ -1,38 +0,0 @@ -CHANGES --------- - -2.0 alpha 2 - - Unicode support - - Rework code that maps between Python classes and PgSQL types, made - more flexible and customizable on a per-connection basis. - - Map between these Python and PgSQL types - - Decimal <---> numeric - datetime.date <---> date - datetime.time <---> time or 'time with time zone' - datetime.datetime <---> timestamp or 'timestamp with time zone' - - bpgsql.Binary <---> bytea - (wrapper for str) - - Experimental Django 1.0 DB backend - -Version 1.2 - - Rework to be close to DB-API 2.0 compliant - -Version 1.1 - - Initial released version - - ------- - -2001-10-28 Started -2002-04-06 Changed connect args to be more like the Python DB-API -2004-03-27 Reworked to follow DB-API 2.0 (http://www.python.org/peps/pep-0249.html) -2005-11-30 Added Unicode support - all char-type fields are now returned as Python Unicode - strings, and SQL commands may be in Unicode or include Unicode parameters. diff --git a/mdk/bpgsql/LICENSE b/mdk/bpgsql/LICENSE deleted file mode 100644 index 3b473db..0000000 --- a/mdk/bpgsql/LICENSE +++ /dev/null @@ -1,458 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS diff --git a/mdk/bpgsql/MDK_README b/mdk/bpgsql/MDK_README deleted file mode 100644 index a9d01aa..0000000 --- a/mdk/bpgsql/MDK_README +++ /dev/null @@ -1,4 +0,0 @@ -py-bpgsql 2.0 alpha 2 - -http://barryp.org/software/bpgsql/ -http://code.google.com/p/py-bpgsql/ \ No newline at end of file diff --git a/mdk/bpgsql/README b/mdk/bpgsql/README deleted file mode 100644 index cd1a61f..0000000 --- a/mdk/bpgsql/README +++ /dev/null @@ -1,7 +0,0 @@ -Barebones pure-python PostGreSQL Client Library - -http://barryp.org/software/py-bpgsql/ - -Mostly DB-API 2.0 compliant - (see docs/differences.txt and docs/extensions.txt) - diff --git a/mdk/bpgsql/__init__.py b/mdk/bpgsql/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/mdk/bpgsql/bpgsql.py b/mdk/bpgsql/bpgsql.py deleted file mode 100644 index 9bed440..0000000 --- a/mdk/bpgsql/bpgsql.py +++ /dev/null @@ -1,1515 +0,0 @@ -""" -Barebones pure-python PostGreSQL - -""" -# Copyright (C) 2001-2008 Barry Pederson -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - -import datetime -import errno -import exceptions -import re -import select -import socket -import sys -import types -try: - from decimal import Decimal -except: - Decimal = float -from struct import pack as _pack -from struct import unpack as _unpack - -# -# See Python sys.version and sys.version_info -# -version = '2.0 alpha 2' -version_info = (2, 0, 0, 'alpha', 2) - -# -# Module Globals specified by DB-API 2.0 -# -apilevel = '2.0' -threadsafety = 1 # Threads may share the module, but not connections. -paramstyle = 'pyformat' # we also understand plain-format - -# -# Constructors specified by DB-API 2.0 -# -Date = datetime.date -Time = datetime.time -Timestamp = datetime.datetime -DateFromTicks = datetime.date.fromtimestamp - -def TimeFromTicks(t): - dt = datetime.datetime.fromtimestamp(t) - return datetime.time(dt.hour, dt.minute, dt.second) - -TimestampFromTicks = datetime.datetime.fromtimestamp - -class Binary(str): - """ - Wrapper class for plain string to indicate it - should be passed as a binary value to PostgreSQL. - - """ - pass - -# -# Type identifiers specified by DB-API 2.0 -# -STRING = object() -BINARY = object() -NUMBER = object() -DATETIME = object() -ROWID = object() - -# -# Exception hierarchy from DB-API 2.0 spec -# -class Error(exceptions.StandardError): - """ - Exception that is the base class of all other error - exceptions. You can use this to catch all errors with one - single 'except' statement. Warnings are not considered - errors and thus should not use this class as base. - - """ - pass - -class Warning(exceptions.StandardError): - """ - Exception raised for important warnings like data - truncations while inserting, etc. - - """ - pass - -class InterfaceError(Error): - """ - Exception raised for errors that are related to the - database interface rather than the database itself. - - """ - pass - -class DatabaseError(Error): - """ - Exception raised for errors that are related to the - database. - - """ - pass - -class InternalError(DatabaseError): - """ - Exception raised when the database encounters an internal - error, e.g. the cursor is not valid anymore, the - transaction is out of sync, etc. - - """ - pass - -class OperationalError(DatabaseError): - """ - Exception raised for errors that are related to the - database's operation and not necessarily under the control - of the programmer, e.g. an unexpected disconnect occurs, - the data source name is not found, a transaction could not - be processed, a memory allocation error occurred during - processing, etc. - - """ - pass - -class ProgrammingError(DatabaseError): - """ - Exception raised for programming errors, e.g. table not - found or already exists, syntax error in the SQL - statement, wrong number of parameters specified, etc. - - """ - pass - -class IntegrityError(DatabaseError): - """ - Exception raised when the relational integrity of the - database is affected, e.g. a foreign key check fails. - - """ - pass - -class DataError(DatabaseError): - """ - Exception raised for errors that are due to problems with - the processed data like division by zero, numeric value - out of range, etc. - - """ - pass - -class NotSupportedError(DatabaseError): - """ - Exception raised in case a method or database API was used - which is not supported by the database, e.g. requesting a - .rollback() on a connection that does not support - transaction or has transactions turned off. - - """ - pass - -# -# Custom exceptions raised by this driver -# - -class PostgreSQL_Timeout(InterfaceError): - """ - Exception raised by wait_notify() when timeout has expired. - - """ - pass - - -# -# Constants relating to Large Object support -# -INV_WRITE = 0x00020000 -INV_READ = 0x00040000 - -SEEK_SET = 0 -SEEK_CUR = 1 -SEEK_END = 2 - - -################################ -# -# Type conversion functions - - -_OCTAL_ESCAPE = re.compile(r'\\(\d\d\d)') - -def _binary_to_python(s): - """ - Convert a PgSQL binary value to a plain Python string. - - """ - s = _OCTAL_ESCAPE.sub(lambda x: chr(int(x.group(1), 8)), s) - return Binary(s.replace('\\\\', '\\')) - - -def _bool_to_python(s): - """ - Convert PgSQL boolean string to Python boolean - - """ - if s == 't': - return True - if s == 'f': - return False - raise InterfaceError('Boolean type came across as unknown value [%s]' % s) - - -def _char_to_python(s): - """ - Convert character data, which should be utf-8 strings, to Python Unicode strings - - """ - return s.decode('utf-8') - - -def _date_to_python(s): - """ - Convert date string to Python datetime.date object - - """ - y, m, d = s.split('-') - return datetime.date(int(y), int(m), int(d)) - - -class _SimpleTzInfo(datetime.tzinfo): - """ - Concrete subclass of datetime.tzinfo that can represent - the hour and minute offsets PgSQL supplies in the - '... with time zone' types. - - """ - def __init__(self, tz): - super(_SimpleTzInfo, self).__init__() - if ':' in tz: - hour, minute = tz.split(':') - else: - hour = tz - minute = 0 - hour = int(hour) - if hour < 0: - minute = -minute - self.offset = datetime.timedelta(hours=hour, minutes=minute) - - def dst(self, dt): - return None - - def utcoffset(self, dt): - return self.offset - - -def _time_to_python(timepart): - """ - Convert time string to Python datetime.time object - - """ - if '+' in timepart: - timepart, tz = timepart.split('+') - tz = _SimpleTzInfo(tz) - elif '-' in timepart: - timepart, tz = timepart.split('-') - tz = _SimpleTzInfo('-' + tz) - else: - tz = None - - hour, minute, second = timepart.split(':') - if '.' in second: - second, frac = second.split('.') - frac = int(Decimal('0.' + frac) * 1000000) - else: - frac = 0 - - return datetime.time(int(hour), int(minute), int(second), frac, tz) - - -def _timestamp_to_python(s): - """ - Convert timestamp string to Python datetime.datetime object - - """ - datepart, timepart = s.split(' ') - d = _date_to_python(datepart) - t = _time_to_python(timepart) - return datetime.datetime(d.year, d.month, d.day, - t.hour, t.minute, t.second, - t.microsecond, t.tzinfo) - - -_ESCAPE_CHARS = re.compile("[\x00-\x1f'\\\\\x7f-\xff]") -def _binary_to_pgsql(b): - """ - Convert a python string (probably subclassed as 'Binary') to - a PgSQL bytea. - - """ - return "E'%s'::bytea" % _ESCAPE_CHARS.sub(lambda x: '\\\\%03o' % ord(x.group(0)), b) - - -def _datetime_to_pgsql(dt): - """ - Convert Python datetime.datetime to PgSQL timestamp. - """ - if dt.tzinfo: - return "'%s'::timestamp with time zone" % dt.isoformat(' ') - return "'%s'::timestamp" % dt.isoformat(' ') - - -def _time_to_pgsql(t): - """ - Convert Python datetime.time to PgSQL time. - - """ - if t.tzinfo: - return "'%s'::time with time zone" % t.isoformat() - return "'%s'::time" % t.isoformat() - - -################ -# -# Helper classes and functions -# - -def _parseDSN(s): - """ - Parse a string containing PostgreSQL libpq-style connection info in the form: - - "keyword1=val1 keyword2='val2 with space' keyword3 = val3" - - into a dictionary:: - - {'keyword1': 'val1', 'keyword2': 'val2 with space', 'keyword3': 'val3'} - - Returns empty dict if s is empty string or None. - """ - if not s: - return {} - - result = {} - state = 1 - buf = '' - for ch in s.strip(): - if state == 1: # reading keyword - if ch in '=': - keyword = buf.strip() - buf = '' - state = 2 - else: - buf += ch - elif state == 2: # have read '=' - if ch == "'": - state = 3 - elif ch != ' ': - buf = ch - state = 4 - elif state == 3: # reading single-quoted val - if ch == "'": - result[keyword] = buf - buf = '' - state = 1 - else: - buf += ch - elif state == 4: # reading non-quoted val - if ch == ' ': - result[keyword] = buf - buf = '' - state = 1 - else: - buf += ch - if state == 4: # was reading non-quoted val when string ran out - result[keyword] = buf - return result - - -class _LargeObject(object): - """ - Make a PostgreSQL Large Object look somewhat like - a Python file. Should be created from Connection object - open or create methods. - - """ - def __init__(self, client, fd): - self.__client = client - self.__fd = fd - - def __del__(self): - if self.__client: - self.close() - - def close(self): - """ - Close an opened Large Object - """ - try: - self.__client._lo_funcall('lo_close', self.__fd) - finally: - self.__client = self.__fd = None - - def flush(self): - pass - - def read(self, readlen): - return self.__client._lo_funcall('loread', self.__fd, readlen) - - def seek(self, offset, whence): - self.__client._lo_funcall('lo_lseek', self.__fd, offset, whence) - - def tell(self): - r = self.__client._lo_funcall('lo_tell', self.__fd) - return _unpack('!i', r)[0] - - def write(self, data): - """ - Write data to lobj, return number of bytes written - """ - r = self.__client._lo_funcall('lowrite', self.__fd, data) - return _unpack('!i', r)[0] - - -class _PgType(object): - """ - Helper class to hold info for mapping from pgsql types - to Python objecs. - - """ - def __init__(self, name, converter, type_id): - self.name = name - self.converter = converter - self.type_id = type_id - self.oid = None - -_DEFAULT_PGTYPE = _PgType('unknown', _char_to_python, 'unknown') - - -class _ResultSet(object): - """ - Helper class only used internally by the Connection class for - building up result sets. - - """ - def __init__(self): - self.completed = None - self.conversion = None - self.description = None - self.error = None - self.null_byte_count = 0 - self.num_fields = 0 - self.rows = None - self.messages = [] - - def set_description(self, description): - self.description = description - self.num_fields = len(description) - self.null_byte_count = (self.num_fields + 7) >> 3 - self.rows = [] - - -class Connection(object): - """ - connection objects are created by calling this module's connect function. - - """ - def __init__(self, dsn=None, username='', password='', - host=None, dbname='', port='', opt=''): - self.__backend_pid = None - self.__backend_key = None - self.__socket = None - self.__input_buffer = '' - self.__authenticated = 0 - self.__ready = 0 - self.__result = None - self.__current_result = None - self.__notify_queue = [] - self.__func_result = None - self.__lo_funcs = {} - self.__lo_funcnames = {} - self._pg_types = {} - self._oid_map = {} - self._python_converters = [] - - # - # Come up with a reasonable default host for - # win32 and presumably Unix platforms - # - if host == None: - if sys.platform == 'win32': - host = '127.0.0.1' - else: - host = '/tmp/.s.PGSQL.5432' - - args = _parseDSN(dsn) - - if not args.has_key('host'): - args['host'] = host - if not args.has_key('port'): - args['port'] = port or 5432 - if not args.has_key('dbname'): - args['dbname'] = dbname - if not args.has_key('user'): - args['user'] = username - if not args.has_key('password'): - args['password'] = password - if not args.has_key('options'): - args['options'] = opt - - if args['host'].startswith('/'): - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.connect(args['host']) - else: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((args['host'], int(args['port']))) - - if not args['user']: - # - # If no userid specified in the args, try to use the userid - # this process is running under, if we can figure that out. - # - try: - import os, pwd - args['user'] = pwd.getpwuid(os.getuid())[0] - except: - pass - - self.__socket = s - self.__passwd = args['password'] - self.__userid = args['user'] - - # - # Send startup packet specifying protocol version 2.0 - # (works with PostgreSQL 6.3 or higher?) - # - self.__send(_pack('!ihh64s32s64s64s64s', 296, 2, 0, args['dbname'], - args['user'], args['options'], '', '')) - while not self.__ready: - self.__read_response() - - # - # Get type info from the backend to help put together some dictionaries - # to help in converting Pgsql types to Python types. - # - self._initialize_types() - self.__initialize_type_map() - - - def __del__(self): - if self.__socket: - self.__send('X') - self.__socket.close() - self.__socket = None - - - def _get_conversion(self, oid): - """ - Given an oid of a PgSQL type, come up with a Python callable - that will turn a string holding a representation of the value - into a Python object - - """ - return self._oid_map.get(oid, _DEFAULT_PGTYPE).converter - - - def __initialize_type_map(self): - """ - Query the backend to find out a mapping for type_oid -> type_name, and - then lookup the map of type_name -> conversion_function, to come up - with a map of type_oid -> conversion_function - """ - cur = self.cursor() - cur.execute("SET CLIENT_ENCODING to 'UNICODE'") - cur.execute("SET STANDARD_CONFORMING_STRINGS to 'ON'") - - cur.execute('SELECT oid, typname FROM pg_type') - - for oid, name in cur: - self._register_oid(int(oid), name) - - - def __lo_init(self): - # - # Make up a dictionary mapping function names beginning with "lo" - # to function oids (there may be some non-lobject functions - # in there, but that should be harmless) - # - result = self._execute("SELECT proname, oid FROM pg_proc WHERE proname like 'lo%'") - for proname, oid in result.rows: - self.__lo_funcs[proname] = oid - self.__lo_funcnames[oid] = proname - - - def __new_result(self): - # - # Start a new ResultSet - # - if self.__result is None: - self.__result = [] - self.__current_result = _ResultSet() - self.__result.append(self.__current_result) - - - def _python_to_sql(self, obj): - """ - Convert a Python object to a utf-8 string suitable for insertion - into an SQL statement. - - """ - escape_string = True - - for klass, converter in self._python_converters: - if isinstance(obj, klass): - obj = converter(obj) - escape_string = False - break - - if obj is None: - return 'NULL' - - if isinstance(obj, unicode): - obj = obj.encode('utf-8') - - if escape_string and isinstance(obj, str): - return "E'%s'" % _ESCAPE_CHARS.sub(lambda x: '\\x%02x' % ord(x.group(0)), obj) - - return obj - - - def __read_bytes(self, nBytes): - # - # Read the specified number of bytes from the backend - # - while len(self.__input_buffer) < nBytes: - d = self.__recv(4096) - if d: - self.__input_buffer += d - else: - raise OperationalError('Connection to backend closed') - result, self.__input_buffer = self.__input_buffer[:nBytes], self.__input_buffer[nBytes:] - return result - - - def __read_string(self, terminator='\0'): - # - # Read a something-terminated string from the backend - # (the terminator isn't returned as part of the result) - # - while True: - if terminator in self.__input_buffer: - result, self.__input_buffer = self.__input_buffer.split(terminator, 1) - return result - else: - # need more data - d = self.__recv(4096) - if d: - self.__input_buffer += d - else: - raise OperationalError('Connection to backend closed') - - - def __read_response(self): - # - # Read a single response from the backend - # Looks at the next byte, and calls a more specific - # method the handle the rest of the response - # - # PostgreSQL responses begin with a single character , this - # method looks up a method named _pkt_ and calls that - # to handle the response - # - pkt_type = self.__read_bytes(1) - - try: - getattr(self, '_pkt_' + pkt_type)() - except AttributeError: - raise InterfaceError('Unrecognized packet type from server: %s' % pkt_type) - - - def __read_row(self, ascii=True): - # - # Read an ASCII or Binary Row - # - result = self.__current_result - - # check if we need to use longs (more than 32 fields) - if result.null_byte_count > 4: - null_bits = 0L - field_mask = 128L - else: - null_bits = 0 - field_mask = 128 - - # read bytes holding null bits and setup the field mask - # to point at the first (leftmost) field - if result.null_byte_count: - for ch in self.__read_bytes(result.null_byte_count): - null_bits = (null_bits << 8) | ord(ch) - field_mask <<= (result.null_byte_count - 1) * 8 - - # read each field into a row - row = [] - for field_num in range(result.num_fields): - if null_bits & field_mask: - # field has data present, read what was sent - field_size = _unpack('!i', self.__read_bytes(4))[0] - if ascii: - field_size -= 4 - data = self.__read_bytes(field_size) - row.append(result.conversion[field_num](data)) - else: - # field has no data (is null) - row.append(None) - field_mask >>= 1 - - result.rows.append(row) - - - def __recv(self, bufsize): - while True: - try: - return self.__socket.recv(bufsize) - except socket.error, serr: - if serr[0] != errno.EINTR: - raise - - - def _register_oid(self, oid, name): - """ - Tie a numeric type oid to a name, which we may have already - registered a conversion function for. If not, register a - default conversion function. - - """ - if name in self._pg_types: - pg_type = self._pg_types[name] - else: - self._pg_types[name] = pg_type = _PgType(name, _char_to_python, 'oid:%d:%s' % (oid, name)) - - pg_type.oid = oid - self._oid_map[oid] = pg_type - - - def __send(self, data): - # - # Send data to the backend, make sure it's all sent - # - if self.__socket is None: - raise InterfaceError('Connection not open') - - while data: - try: - nSent = self.__socket.send(data) - except socket.error, serr: - if serr[0] != errno.EINTR: - raise - continue - data = data[nSent:] - - - def __wait_response(self, timeout): - # - # Wait for something to be in the input buffer, timeout - # is a floating-point number of seconds, zero means - # timeout immediately, < 0 means don't timeout (call blocks - # indefinitely) - # - if self.__input_buffer: - return 1 - - if timeout >= 0: - r, _, _ = select.select([self.__socket], [], [], timeout) - else: - r, _, _ = select.select([self.__socket], [], []) - - if r: - return 1 - else: - return 0 - - - - #----------------------------------- - # Packet Handling Methods - # - - def _pkt_A(self): - # - # Notification Response - # - pid = _unpack('!i', self.__read_bytes(4))[0] - self.__notify_queue.append((self.__read_string(), pid)) - - - def _pkt_B(self): - # - # Binary Row - # - self.__read_row(ascii=False) - - - def _pkt_C(self): - # - # Completed Response - # - self.__current_result.completed = self.__read_string() - self.__new_result() - - - def _pkt_D(self): - # - # ASCII Row - # - self.__read_row() - - - def _pkt_E(self): - # - # Error Response - # - error_msg = self.__read_string() - exc = DatabaseError(error_msg) - - if self.__current_result: - self.__current_result.error = exc - self.__new_result() - else: - raise exc - - - def _pkt_G(self): - # - # CopyIn Response from self.stdin if available, or - # sys.stdin Supplies the final terminating line: - # '\.' (one backslash followd by a period) if it - # doesn't appear in the input - # - if hasattr(self, 'stdin') and self.stdin: - stdin = self.stdin - else: - stdin = sys.stdin - - lastline = None - while True: - s = stdin.readline() - if (not s) or (s == '\\.\n'): - break - self.__send(s) - lastline = s - if lastline and (lastline[-1] != '\n'): - self.__send('\n') - self.__send('\\.\n') - - - def _pkt_H(self): - # - # CopyOut Response to self.stdout if available, or - # sys.stdout Doesn't write the final terminating line: - # '\.' (one backslash followed by a period) - # - if hasattr(self, 'stdout') and self.stdout: - stdout = self.stdout - else: - stdout = sys.stdout - - while True: - s = self.__read_string('\n') - if s == '\\.': - break - else: - stdout.write(s) - stdout.write('\n') - - - def _pkt_I(self): - # - # EmptyQuery Response - # - pass - - - def _pkt_K(self): - # - # Backend Key data - # - self.__backend_pid, self.__backend_key = _unpack('!ii', self.__read_bytes(8)) - - - def _pkt_N(self): - # - # Notice Response - # - n = self.__read_string() - self.__current_result.messages.append((Warning, n)) - - - def _pkt_P(self): - # - # Cursor Response - # - cursor = self.__read_string() - - - def _pkt_R(self): - # - # Startup Response - # - code = _unpack('!i', self.__read_bytes(4))[0] - if code == 0: - self.__authenticated = 1 - #print 'Authenticated!' - elif code == 1: - raise InterfaceError('Kerberos V4 authentication is required by server, but not supported by this client') - elif code == 2: - raise InterfaceError('Kerberos V5 authentication is required by server, but not supported by this client') - elif code == 3: - self.__send(_pack('!i', len(self.__passwd)+5) + self.__passwd + '\0') - elif code == 4: - salt = self.__read_bytes(2) - try: - import crypt - except: - raise InterfaceError('Encrypted authentication is required by server, but Python crypt module not available') - cpwd = crypt.crypt(self.__passwd, salt) - self.__send(_pack('!i', len(cpwd)+5) + cpwd + '\0') - elif code == 5: - import md5 - - m = md5.new(self.__passwd + self.__userid).hexdigest() - m = md5.new(m + self.__read_bytes(4)).hexdigest() - m = 'md5' + m + '\0' - self.__send(_pack('!i', len(m)+4) + m) - else: - raise InterfaceError('Unknown startup response code: R%d (unknown password encryption?)' % code) - - - def _pkt_T(self): - # - # Row Description - # - nFields = _unpack('!h', self.__read_bytes(2))[0] - descr = [] - for i in range(nFields): - fieldname = self.__read_string() - oid, type_size, type_modifier = _unpack('!ihi', self.__read_bytes(10)) - descr.append((fieldname, oid, type_size, type_modifier)) - - description = [] - for name, oid, size, modifier in descr: - pg_type = self._oid_map.get(oid, _DEFAULT_PGTYPE) - description.append((name, pg_type.type_id, None, None, None, None, None)) - - # Save the field description list - self.__current_result.set_description(description) - - # build a list of field conversion functions we can use against each row - self.__current_result.conversion = [self._get_conversion(d[1]) for d in descr] - - - def _pkt_V(self): - # - # Function call response - # - self.__func_result = None - while True: - ch = self.__read_bytes(1) - if ch == '0': - break - if ch == 'G': - result_size = _unpack('!i', self.__read_bytes(4))[0] - self.__func_result = self.__read_bytes(result_size) - else: - raise InterfaceError('Unexpected byte: [%s] in Function call reponse' % ch) - - - def _pkt_Z(self): - # - # Ready for Query - # - self.__ready = 1 - #print 'Ready for Query' - - - #-------------------------------------- - # Helper func for _LargeObject - # - def _lo_funcall(self, name, *args): - return apply(self.funcall, (self.__lo_funcs[name],) + args) - - - #-------------------------------------- - # Helper function for Cursor objects - # - def _execute(self, cmd, args=None): - if isinstance(cmd, unicode): - cmd = cmd.encode('utf-8') - - while args is not None: - if isinstance(args, (tuple, list)): - # Replace plain-format markers with fixed-up tuple parameters - cmd = cmd % tuple([self._python_to_sql(a) for a in args]) - break - elif isinstance(args, dict): - # replace pyformat markers with dictionary parameters - cmd = cmd % dict([(k, self._python_to_sql(v)) for k, v in args.items()]) - break - else: - # Args wasn't a tuple, list, or dict: wrap it up - # in a tuple and retry - args = (args,) - - self.__ready = 0 - self.__result = None - self.__new_result() - self.__send('Q'+cmd+'\0') - while not self.__ready: - self.__read_response() - result, self.__result = self.__result[:-1], None - - # Convert old-style results to what the new Cursor class expects - result = result[0] - result.query = cmd - return result - - - def _initialize_types(self): - """ - Setup mappings between Python and PgSQL types. Subclasses may - want to override or extend this. - - """ - # - ## Map PgSQL -> Python - # - self.register_pgsql(['char', 'varchar', 'text'], - _char_to_python, STRING) - self.register_pgsql('bytea', _binary_to_python, BINARY) - - self.register_pgsql(['int2', 'int4'], int, NUMBER) - self.register_pgsql('int8', long, NUMBER) - self.register_pgsql(['float4', 'float8'], float, NUMBER) - self.register_pgsql('numeric', Decimal, NUMBER) - - self.register_pgsql('oid', long, ROWID) - self.register_pgsql('bool', _bool_to_python, 'bool') - - self.register_pgsql('date', _date_to_python, DATETIME) - self.register_pgsql(['time', 'timetz'], _time_to_python, DATETIME) - self.register_pgsql(['timestamp', 'timestamptz'], - _timestamp_to_python, DATETIME) - - # - ## Map Python -> PgSQL - # the order matters, so put subclasses before superclasses - # (such as datetime before date) - # - self.register_python(datetime.datetime, _datetime_to_pgsql) - self.register_python(datetime.date, lambda x: "'%s'::date" % str(x)) - self.register_python(datetime.time, _time_to_pgsql) - self.register_python(Binary, _binary_to_pgsql) - - - #-------------------------------------- - # Public methods - # - - def close(self): - """ - Close the connection now (rather than whenever __del__ is - called). The connection will be unusable from this point - forward; an Error (or subclass) exception will be raised - if any operation is attempted with the connection. The - same applies to all cursor objects trying to use the - connection. - - """ - if self.__socket is None: - raise InterfaceError("Can't close connection that's not open") - self.__del__() - - - def commit(self): - """ - Commit any pending transaction to the database. - - """ - self._execute('COMMIT') - - - def cursor(self): - """ - Get a new cursor object using this connection. - - """ - return Cursor(self) - - - def funcall(self, oid, *args): - """ - Low-level call to PostgreSQL function, you must supply - the oid of the function, and have the args supplied as - ints or strings. - - """ - self.__ready = 0 - self.__send(_pack('!2sIi', 'F\0', oid, len(args))) - for arg in args: - atype = type(arg) - if (atype == types.LongType) and (arg >= 0): - # Make sure positive longs, such as OIDs, get - # sent back as unsigned ints - self.__send(_pack('!iI', 4, arg)) - elif (atype == types.IntType) or (atype == types.LongType): - self.__send(_pack('!ii', 4, arg)) - else: - self.__send(_pack('!i', len(arg))) - self.__send(arg) - - while not self.__ready: - self.__read_response() - result, self.__func_result = self.__func_result, None - return result - - - def lo_create(self, mode=INV_READ|INV_WRITE): - """ - Return the oid of a new Large Object, created with the specified mode - - """ - if not self.__lo_funcs: - self.__lo_init() - r = self.funcall(self.__lo_funcs['lo_creat'], mode) - return _unpack('!i', r)[0] - - - def lo_open(self, oid, mode=INV_READ|INV_WRITE): - """ - Open the Large Object with the specified oid, returns - a file-like object - - """ - if not self.__lo_funcs: - self.__lo_init() - r = self.funcall(self.__lo_funcs['lo_open'], oid, mode) - fd = _unpack('!i', r)[0] - lobj = _LargeObject(self, fd) - lobj.seek(0, SEEK_SET) - return lobj - - - def lo_unlink(self, oid): - """ - Delete the specified Large Object - - """ - if not self.__lo_funcs: - self.__lo_init() - self.funcall(self.__lo_funcs['lo_unlink'], oid) - - - def register_pgsql(self, typenames, converter, type_id): - """ - For a PgSQL typename or list of typenames, register a callable - that converts strings of those values into Python objects, and - a type_id object that will be used to identify the type in - result descriptions. - - """ - # if the first arg is just a single string, put it into a list - # - if isinstance(typenames, basestring): - typenames = [typenames] - - for name in typenames: - # - # See if we've already done '_register_oid' on this name - # - if name in self._pg_types: - oid = self._pg_types[name].oid - else: - oid = None - - self._pg_types[name] = pg_type = _PgType(name, converter, type_id) - - # - # Update oid_map if we already did _register_oid on this name - # - if oid is not None: - self._oid_map[oid] = pg_type - - - def register_python(self, klass, converter): - """ - Register a callable for converting a Python object - to a string suitable for use as a value in an SQL statement. The - result should ideally be a utf-8 encoded plain string, or else a - unicode string. - - Converters are searched in the order they're added, so be sure - to register more specific types before general times (for example, - datetime.datetime before datetime.date). - - """ - self._python_converters.append((klass, converter)) - - - def rollback(self): - """ - Cause the the database to roll back to the start of any - pending transaction. - - """ - self._execute('ROLLBACK') - - - def wait_for_notify(self, timeout=-1): - """ - Wait for an async notification from the backend, which comes - when another client executes the SQL command: - - NOTIFY name - - where 'name' is an arbitrary string. timeout is specified in - floating- point seconds, -1 means no timeout, 0 means timeout - immediately if nothing is available. - - In practice though the timeout is a timeout to wait for the - beginning of a message from the backend. Once a message has - begun, the client will wait for the entire message to finish no - matter how long it takes. - - Return value is a tuple: (name, pid) where 'name' string - specified in the NOTIFY command, and 'pid' is the pid of the - backend process that processed the command. - - Raises a PostgreSQL_Timeout exception on timeout - - """ - while True: - if self.__notify_queue: - result, self.__notify_queue = self.__notify_queue[0], self.__notify_queue[1:] - return result - if self.__wait_response(timeout): - self.__read_response() - else: - raise PostgreSQL_Timeout() - -# -# DB API 2.0 extension: -# All exception classes defined by the DB API standard should be -# exposed on the Connection objects as attributes (in addition -# to being available at module scope). -# -# These attributes simplify error handling in multi-connection -# environments. - -Connection.Error = Error -Connection.Warning = Warning -Connection.InterfaceError = InterfaceError -Connection.DatabaseError = DatabaseError -Connection.InternalError = InternalError -Connection.OperationalError = OperationalError -Connection.ProgrammingError = ProgrammingError -Connection.IntegrityError = IntegrityError -Connection.DataError = DataError -Connection.NotSupportedError = NotSupportedError - - -class Cursor(object): - """ - Cursor objects are created by calling a connection's cursor() method, - and are used to manage the context of a fetch operation. - - Cursors created from the same connection are not isolated, i.e., any changes - done to the database by a cursor are immediately visible by the - other cursors. - - Cursors created from different connections are isolated. - - """ - def __init__(self, conn): - """ - Create a cursor from a given bpgsql Connection object. - - """ - self.arraysize = 1 - self.connection = conn - self.description = None - self.lastrowid = None - self.messages = [] - self.rowcount = -1 - self.rownumber = None - self.__rows = None - self.query = '' - - - def __iter__(self): - """ - Return an iterator for the result set this cursor holds. - - """ - return self - - - def close(self): - """ - Close the cursor now (rather than whenever __del__ is - called). The cursor will be unusable from this point - forward; an Error (or subclass) exception will be raised - if any operation is attempted with the cursor. - - """ - self.__init__(None) - - - def execute(self, cmd, args=None): - """ - Execute a database operation (query or command). - Parameters may be provided as sequence or - mapping or singleton argument and will be bound to variables - in the operation. Variables are specified in format (...WHERE foo=%s...) - or pyformat (...WHERE foo=%(name)s...) paramstyles. - - """ - self.rowcount = -1 - self.rownumber = None - self.description = None - self.lastrowid = None - self.__rows = None - self.messages = [] - - result = self.connection._execute(cmd, args) - - if result.error: - raise result.error - - self.description = result.description - self.__rows = result.rows - self.messages = result.messages - self.query = result.query - - try: - words = result.completed.split(' ') - self.rowcount = int(words[-1]) - if words[0] == 'INSERT': - try: - self.lastrowid = int(words[-2]) - except: - pass - except: - pass - - if self.__rows is not None: - self.rowcount = len(self.__rows) - self.rownumber = 0 - - - def executemany(self, cmd, seq_of_parameters): - """ - Execute a database operation (query or command) against - all parameter sequences or mappings found in the - sequence seq_of_parameters. - - """ - for p in seq_of_parameters: - self.execute(cmd, p) - - # Don't want to leave the value of the last execute() call - self.rowcount = -1 - - - def fetchall(self): - """ - Fetch all remaining rows of a query set, as a list of lists. - An empty list is returned if no more rows are available. - An Error is raised if no result set exists - - """ - if self.__rows is None: - raise Error('No result set available') - - return self.fetchmany(self.rowcount - self.rownumber) - - - def fetchone(self): - """ - Fetch the next row of the result set as a list of fields, or None if - no more are available. Will raise an Error if no - result set exists. - - """ - try: - return self.next() - except StopIteration: - return None - - - def fetchmany(self, size=None): - """ - Fetch all the specified number of rows of a query set, as a list of lists. - If no size is specified, then the cursor's .arraysize property is used. - An empty list is returned if no more rows are available. - An Error is raised if no result set exists - - """ - if self.__rows is None: - raise Error('No result set available') - - if size is None: - size = self.arraysize - - n = self.rownumber - self.rownumber += size - return self.__rows[n:self.rownumber] - - - def next(self): - """ - Return the next row of a result set. Raises StopIteration - if no more rows are available. Raises an Error if no result set - exists. - - """ - if self.__rows is None: - raise Error('No result set available') - - n = self.rownumber - if n >= self.rowcount: - raise StopIteration - - self.rownumber += 1 - return self.__rows[n] - - - def scroll(self, n, mode='relative'): - """ - Scroll the cursor in the result set to a new position according - to mode. - - If mode is 'relative' (default), value is taken as offset to - the current position in the result set, if set to 'absolute', - value states an absolute target position. - - An IndexError will be raised in case a scroll operation would - leave the result set. In this case, the cursor position unchanged. - - """ - if self.__rows is None: - raise Error('No result set available') - - if mode == 'relative': - newpos = self.rownumber + n - elif mode == 'absolute': - newpos = n - else: - raise ProgrammingError('Unknown scroll mode [%s]' % mode) - - if (newpos < 0) or (newpos >= self.rowcount): - raise IndexError('scroll(%d, "%s") target position: %d outsize of range: 0..%d' % (n, mode, newpos, self.rowcount-1)) - - self.rownumber = newpos - - - def setinputsizes(self, sizes): - """ - Intented to be used before a call to execute() or executemany() to - predefine memory areas for the operation's parameters. - - Doesn't actually do anything in this client. - - """ - pass - - - def setoutputsize(self, size, column=None): - """ - Set a column buffer size for fetches of large columns - (e.g. LONGs, BLOBs, etc.). - - Doesn't actually do anything in this client. - - """ - pass - - -def connect(dsn=None, username='', password='', - host=None, dbname='', port='', opt='', **extra): - """ - Connect to a PostgreSQL database. - - The dsn, if used, is in the format used by the PostgreSQL libpq C library, which is one - or more "keyword=value" pairs separated by spaces. Values that are single-quoted may - contain spaces. Spaces around the '=' chars are ignored. Recognized keywords are: - - host, port, dbname, user, password, options - - For example: - - cnx = bpgsql.connect("host=127.0.0.1 dbname=mydb user=jake") - - """ - return Connection(dsn, username, password, host, dbname, port, opt) - -# ---- EOF ---- diff --git a/mdk/db.py b/mdk/db.py index 86eb4db..e8ca141 100644 --- a/mdk/db.py +++ b/mdk/db.py @@ -30,12 +30,7 @@ except: # If it is not found, fallback on the one shipped with MDK. import pymysql as mysql -try: - # Try to import the library python-psycopg2. - import psycopg2 as pgsql -except: - # If it is not found, fallback on the one shipped with MDK. - from bpgsql import bpgsql as pgsql +import psycopg2 as pgsql # TODO: Clean up the mess caused by the different engines and libraries. @@ -68,23 +63,13 @@ class DB(object): self.cur = self.conn.cursor() elif engine == 'pgsql': - try: - # psycopg2. - self.conn = pgsql.connect( - host=str(options['host']), - port=int(options['port']), - user=str(options['user']), - password=str(options['passwd']) - ) - except Exception: - # bpsql. - self.conn = pgsql.connect( - host=str(options['host']), - port=int(options['port']), - username=str(options['user']), - password=str(options['passwd']), - dbname='' - ) + # psycopg2. + self.conn = pgsql.connect( + host=str(options['host']), + port=int(options['port']), + user=str(options['user']), + password=str(options['passwd']) + ) try: self.cur = self.conn.cursor() except: @@ -218,24 +203,15 @@ class DB(object): if self.conn: self.conn.close() - try: - # psycopg2. - self.conn = pgsql.connect( - host=str(self.options['host']), - port=int(self.options['port']), - user=str(self.options['user']), - password=str(self.options['passwd']), - database=str(db) - ) - except Exception: - # bpsql. - self.conn = pgsql.connect( - host=str(self.options['host']), - port=int(self.options['port']), - username=str(self.options['user']), - password=str(self.options['passwd']), - dbname=str(db) - ) + # psycopg2. + self.conn = pgsql.connect( + host=str(self.options['host']), + port=int(self.options['port']), + user=str(self.options['user']), + password=str(self.options['passwd']), + database=str(db) + ) + self.cur = self.conn.cursor() diff --git a/requirements.txt b/requirements.txt index ff1af7f..18a8404 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ keyring +psycopg2>=2.4.5 requests>=2.2.1 watchdog \ No newline at end of file