+++ /dev/null
-Henri Michelon hmichelon at e-cml.org
+++ /dev/null
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, 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 or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-\f
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-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 give any other recipients of the Program a copy of this License
-along with the Program.
-
-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 Program or any portion
-of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-\f
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-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 Program, 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 Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) 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; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, 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 executable. However, as a
-special exception, the source code 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.
-
-If distribution of executable or 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 counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-\f
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program 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.
-
- 5. 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 Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program 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 to
-this License.
-
- 7. 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 Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program 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 Program.
-
-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.
-\f
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program 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.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the 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 Program
-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 Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, 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
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-\f
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
+++ /dev/null
-== General Informations ==
-
-PgWorksheet is a simple GUI frontend to PostgreSQL
-for executing SQL queries without using the psql command line tool.
-
-More informations on : http://pgworksheet.projects.postgresql.org/
-
-== Operating systems ==
-PgWorksheet is certified to run on the following operating systems:
- * FreeBSD
- * Linux Ubuntu
- * Linux Fedora
- * Linux Mandriva
- * Microsoft Windows XP
-
-== Depencies ==
-PgWorksheet requires the following softwares to run :
- * Python 2.4 or better
- * Python GTK+ 2.6 or better
- * python-pgsql 0.9.7 (or better) or pyPgSQL
-
-== Installation ==
-Please refers to http://pgworksheet.projects.postgresql.org/install.html for installation instructions.
+++ /dev/null
-[Desktop Entry]
-Name=PgWorksheet
-GenericName=
-Comment=PostgreSQL SQL Tool
-Categories=Application;Development;Databases;
-Encoding=UTF-8
-Exec=pgworksheet
-Icon=pgworksheet.png
-StartupNotify=true
-Terminal=false
-Type=Application
+++ /dev/null
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: DBConnection.py,v 1.24 2008/03/13 10:46:24 hmichelon Exp $
-#
-import string
-import sys
-try:
- import pgsql
- havepgsql = True
-except:
- try:
- from mx import DateTime
- from pyPgSQL import libpq
- import MyPgSQL
- havepgsql = False
- except:
- msg = _("please install the python-pgsql or pyPgSQL (with mxDateTime) packages")
- print(msg)
- import gtk
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
- result = dialog.run()
- dialog.destroy()
- sys.exit(0)
-
-
-class DBConnection:
- """Database connection/deconnection and query execution"""
-
- def __init__(self, host = None, port = None, db = None, user = None, password = None):
- """Try to connect to a database"""
- if (havepgsql) :
- self.connect_pgsql(host, port, db, user, password)
- else :
- self.connect_pypgsql(host, port, db, user, password)
-
- def connect_pypgsql(self, host = None, port = None, db = None, user = None, password = None):
- dsn = ""
- if (host is not None): dsn = host
- dsn = dsn + ":"
- if (port is not None): dsn = dsn + str(port)
- dsn = dsn + ":"
- if (db is not None): dsn = dsn + db
- dsn = dsn + ":"
- if (user is not None): dsn = dsn + user
- dsn = dsn + ":"
- if (password is not None): dsn = dsn + password
- try:
- self.db = MyPgSQL.connect(dsn)
- self.db.autocommit = 1
- self.host = host
- self.port = port
- self.database = db
- self.user = user
- self.password = password
- except libpq.DatabaseError:
- self.db = None
-
-
- def connect_pgsql(self, host = None, port = None, db = None, user = None, password = None):
- try:
- if (port is not None) :
- self.db = pgsql.connect(db, user, password, host, int(port))
- else :
- self.db = pgsql.connect(db, user, password, host)
- self.host = host
- self.port = port
- self.database = db
- self.user = user
- self.password = password
- except pgsql.ProgrammingError:
- self.db = None
-
-
- def pgversion(self):
- if (self.is_connected() and not havepgsql):
- v = string.split(str(self.db.version), ',')
- return v.pop(0)
- return ''
-
-
- def query(self, sql):
- if (havepgsql):
- return self.query_pgsql(sql)
- else:
- return self.query_pypgsql(sql)
-
-
- def query_pypgsql(self, sql):
- """Execute a query and return the corresponding DB-API cursor."""
- if not self.is_connected() : return None
- cursor = self.db.cursor()
- try:
- cursor.execute("SET CLIENT_ENCODING TO 'UTF-8'")
- cursor.execute(sql)
- except libpq.OperationalError, msg:
- cursor.close()
- return { 'error' : str(msg), 'notices' : self.db.notices }
- return { 'cursor' : cursor, 'notices' : self.db.notices }
-
- def query_pgsql(self, sql):
- """Execute a query and return the corresponding DB-API cursor."""
- if not self.is_connected() : return None
- cursor = self.db.cursor()
- try:
- cursor.execute("SET CLIENT_ENCODING TO 'UTF-8'")
- cursor.execute(sql)
- self.db.commit()
- except pgsql.ProgrammingError, msg:
- cursor.close()
- return { 'error' : str(msg), 'notices' : self.db.notices }
- return { 'cursor' : cursor, 'notices' : self.db.notices }
-
-
- def commit(self):
- if not self.is_connected() : return
- self.db.commit()
-
-
- def is_connected(self):
- return (self.db != None)
-
-
- def disconnect(self):
- if not self.is_connected() : return
- self.db.close()
- self.db = None
+++ /dev/null
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: Execute.py,v 1.28 2008/03/12 20:26:23 hmichelon Exp $
-#
-import os
-import sys
-import popen2
-import string
-import gtk
-
-import pgw
-import pgw.Lexical
-
-
-class Execute:
- """SQL query and psql commands handling"""
-
-
- def __init__(self, db):
- self.db = db
- self.lexical = pgw.Lexical.Lexical()
-
-
- def execute_one(self, sql):
- """Execute one query"""
- if (len(sql) > 0) :
- # psql commands
- if (sql[0] == '\\'):
- sql = string.replace(sql, '\\\\', '\\')
- if (pgw.mswindows()):
- cmd = "psql.exe"
- sql = string.strip(sql)
- else:
- cmd = "psql"
- if (self.db.user is not None):
- cmd += ' -U "' + self.db.user + '"'
- if (self.db.host is not None):
- cmd += ' -h "' + self.db.host + '"'
- if (self.db.port is not None):
- cmd += ' -p ' + self.db.port
- if (self.db.database is not None):
- cmd += ' -d "' + self.db.database + '"'
- cmd += ' -c "' + sql + '"'
- if (self.db.password is not None):
- os.putenv("PGPASSWORD", self.db.password)
- #os.putenv("PGCLIENTENCODING", pgw.get_user_encoding())
- output, input = popen2.popen4(cmd)
- result = output.read()
- output.close()
- input.close()
- #os.putenv("PGCLIENTENCODING", "")
- os.putenv("PGPASSWORD", "")
- return { 'text' : result }
- else:
- # SQL query
- return self.db.query(sql)
-
-
- def execute(self, buffer):
- """Execute SQL queries or psql commands"""
- if not self.db.is_connected() : return
- # SQL query
- sqls = self.split(buffer)
- if (len(sqls) > 1):
- # multiple queries
- results = []
- for sql in sqls:
- sql = string.strip(sql)
- if (len(sql) > 0):
- results.append(self.execute_one(sql))
- if (len(results) == 1):
- return results[0]
- return results
- else:
- # unique query
- return self.execute_one(sqls[0])
-
-
- def split(self, oldbuffer):
- """Split multiple queries"""
- # a new textbuffer, because we don't want to modify the original
- buffer = gtk.TextBuffer()
- buffer.set_text(oldbuffer.get_text(oldbuffer.get_start_iter(),
- oldbuffer.get_end_iter()))
- # filter comments
- tokens = self.lexical.analyse(buffer, buffer.get_start_iter(),
- buffer.get_end_iter())
- for token in tokens:
- if (token.token == 'comment'):
- buffer.delete(token.start_iter, token.end_iter)
-
- # restart without the comments
- buffer.set_text(buffer.get_text(buffer.get_start_iter(),
- buffer.get_end_iter()))
- start = buffer.get_start_iter()
- end = buffer.get_end_iter()
- tokens = self.lexical.analyse(buffer, start, end)
-
- # create a list of queries
- sql = buffer.get_text(start, end)
- parts = []
- stop = start
- while (len(tokens) > 0):
- token = tokens.pop(0)
- if (token.token == 'psql'):
- i = token.start_iter
- i.backward_char()
- parts.append(buffer.get_text(stop, i))
- parts.append(token.value)
- stop = token.end_iter
- elif (token.value == ';'):
- i = token.start_iter
- parts.append(buffer.get_text(stop, i))
- stop = token.end_iter
- if (stop != end):
- parts.append(buffer.get_text(stop, end))
- return parts
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: Lexical.py,v 1.9 2008/03/12 20:26:23 hmichelon Exp $
-#
-
-# http://www.postgresql.org/docs/8.0/static/sql-syntax.html
-
-# basic characters sets
-SPACES = [ ' ', '\t', '\n' ]
-DIGITS = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]
-NUMERIC = DIGITS + [ 'e', '.', '+', '-' ]
-OPERATOR_CHARS = [ '+', '-', '*', '/', '<', '>', '=', '~', '!', \
- '@', '#', '%', '^', '&', '|', '`', '?' ]
-SPECIAL_CHARS = [ '(', ')', '[', ']', ',', ';', ':', '*', '.' ]
-OPERATORS = OPERATOR_CHARS + SPECIAL_CHARS
-
-# not the first character of an identifier
-NOT_IDENT_START = SPECIAL_CHARS + OPERATOR_CHARS + DIGITS + [ '$' ]
-# not a character of an identifier
-NOT_IDENT_CHAR = SPECIAL_CHARS + OPERATOR_CHARS + SPACES + [ "'" ]
-# not a character of a dollar quoted string
-NOT_DOLLAR_QUOTED = [ '$' ] + SPACES
-
-
-class Token:
-
- def __init__(self, token, start_iter, end_iter, value=None):
- self.token = token
- self.start_iter = start_iter
- self.end_iter = end_iter
- self.value = value
-
-
-class Eob:
- """End of Buffer Exception"""
-
-
-class Lexical:
- """Simplified lexical analyser"""
-
- def analyse(self, buffer, start, end):
- """Run the lexical analyser"""
- self.buffer = buffer
- self.current = start.copy()
- self.tokens = [];
- try:
- self.lexical_analyser(end.copy())
- except Eob:
- pass
- return self.tokens
-
-
- def next_char(self):
- """Returns the next character to analyse"""
- if (self.current.is_end()):
- raise Eob()
- c = self.current.get_char()
- self.current.forward_char()
- return c
-
-
- def skip_spaces(self, c):
- """Skips everything that looks like a space/tab/etc..."""
- while (c in SPACES):
- c = self.next_char()
- return c
-
-
- def string(self):
- """Single quoted strings"""
- start = self.current.copy()
- start.backward_char()
- prev = None
- c = self.next_char()
- try:
- while (True):
- if ((c == "'") and (prev != '\\')): # a single quote in the string...
- c = self.next_char()
- if (c != "'"):
- break
- prev = c
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('string', start, end))
- raise
- end = self.current.copy()
- end.backward_char()
- self.tokens.append(Token('string', start, end))
- return c
-
-
- def dollar_string(self):
- """Dollar-quoted strings"""
- # first bound
- start = self.current.copy()
- start.backward_char()
- c = self.next_char()
- string_tag = ''
- try:
- while (c not in NOT_DOLLAR_QUOTED):
- string_tag = string_tag + c
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('identifier', start, end, string_tag.upper()))
- raise
- end = self.current.copy()
- end.backward_char()
- if (c != '$'):
- self.tokens.append(Token('identifier', start, end, string_tag.upper()))
- return c
- self.tokens.append(Token('dollarquote', start, end, string_tag.upper()))
-
- # string content
- start = self.current.copy()
- try:
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('identifier', start, end, string_tag.upper()))
- raise
- try:
- while (True):
- if (c == '$'):
- string_end = self.current.copy()
- c = self.next_char()
- s = ''
- while (c not in NOT_DOLLAR_QUOTED):
- s = s + c
- c = self.next_char()
- if (s == string_tag):
- string_end.backward_char()
- self.tokens.append(Token('string', start, string_end))
- end = self.current.copy()
- end.backward_char()
- self.tokens.append(Token('dollarquote', start, end, s.upper()))
- return c
- else:
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('string', start, end))
- raise
- end = self.current.copy()
- end.backward_char()
- self.tokens.append(Token('string', start, end))
- return c
-
-
- def bit_string_constant(self, start):
- """Binary and Hexadecimal numeric constants using strings"""
- c = self.next_char()
- if (c == "'"):
- c = self.next_char()
- start = self.current.copy()
- start.backward_char()
- start.backward_char()
- start.backward_char()
- while (c != "'"):
- c = self.next_char()
- end = self.current.copy()
- self.tokens.append(Token('numeric_constant', start, end))
- return self.next_char()
- else:
- return self.identifier(c, start)
-
-
- def identifier(self, c, ident = ''):
- """An identifier, keyword, type name, etc..."""
- start = self.current.copy()
- for i in range(0, len(ident) + 1):
- start.backward_char()
- try:
- while (c not in NOT_IDENT_CHAR):
- ident = ident + c
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('identifier', start, end, ident.upper()))
- raise
- end = self.current.copy()
- end.backward_char()
- self.tokens.append(Token('identifier', start, end, ident.upper()))
- return c
-
-
- def numeric(self, c):
- """A numeric constant"""
- start = self.current.copy()
- start.backward_char()
- try:
- while (c in NUMERIC):
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('numeric_constant', start, end))
- raise
- end = self.current.copy()
- end.backward_char()
- self.tokens.append(Token('numeric_constant', start, end))
- return c
-
-
- def simple_comment(self):
- """One line comment using --"""
- start = self.current.copy()
- start.backward_char()
- start.backward_char()
- c = self.next_char()
- try:
- while (c != '\n'):
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('comment', start, end))
- raise
- end = self.current.copy()
- self.tokens.append(Token('comment', start, end))
-
-
- def comment(self):
- """Multi lines comments using /* */"""
- start = self.current.copy()
- start.backward_char()
- start.backward_char()
- c = self.next_char()
- prev = None
- nested = 0
- try:
- while (True):
- if (c == '*'):
- c = self.next_char()
- if (prev == '/'):
- nested = nested + 1
- continue
- if (c == '/'):
- if (nested == 0):
- c = self.next_char()
- break
- else:
- nested = nested - 1
- else:
- prev = c
- continue
- prev = c
- c = self.next_char()
- except Eob:
- end = self.current.copy()
- self.tokens.append(Token('comment', start, end))
- raise
- end = self.current.copy()
- end.backward_char()
- self.tokens.append(Token('comment', start, end))
- return c
-
-
- def psql(self):
- """A PgSQL Command"""
- start = self.current.copy()
- start.backward_char()
- c = self.next_char()
- cmd = '\\'
- try:
- while (c != '\n') and (c != ';'):
- cmd = cmd + c
- c = self.next_char()
- except:
- end = self.current.copy()
- self.tokens.append(Token('psql', start, end, cmd))
- raise
- end = self.current.copy()
- self.tokens.append(Token('psql', start, end, cmd))
-
-
- def lexical_analyser(self, fin):
- """A simplified lexical analyser"""
- c = self.next_char()
- while (self.current.compare(fin) <= 0):
- c = self.skip_spaces(c)
- # Multi lines comments
- if (c == '/'):
- c = self.next_char()
- if (c == '*'):
- c = self.comment()
- continue
- else:
- self.current.backward_char()
- # One line comments
- elif (c == '-'):
- c = self.next_char()
- if (c == '-'):
- self.simple_comment()
- else:
- self.current.backward_char()
- # psql commands
- elif (c == '\\'):
- self.psql()
- # numeric
- elif (c in DIGITS):
- c = self.numeric(c)
- continue
- # bit strings
- elif (c == 'B') or (c == 'b') or (c == 'H') or (c == 'h'):
- c = self.bit_string_constant(c)
- continue
- # strings
- elif (c == "'"):
- c = self.string()
- continue
- # dollar-quoted strings
- elif (c == '$'):
- c = self.dollar_string()
- continue
- # numeric
- elif (c == '.'):
- c = self.next_char()
- if (c in DIGITS):
- self.current.backward_char()
- c = self.numeric(self.current.get_char())
- continue
- # quoted identifiers
- elif (c == '"'):
- c = self.next_char()
- while (c != '"'):
- c = self.next_char()
- # operators
- elif (c in OPERATORS):
- start = self.current.copy()
- start.backward_char()
- end = self.current.copy()
- self.tokens.append(Token('operator', start, end, c))
- # everything else
- elif (c not in NOT_IDENT_START):
- c = self.identifier(c)
- continue
- c = self.next_char()
+++ /dev/null
-#ident "@(#) $Id: MyPgSQL.py,v 1.2 2004/11/24 10:31:07 hmichelon Exp $"
-# vi:set sw=4 ts=8 showmode ai:
-#--(H+)-----------------------------------------------------------------+
-# Name: PgSQL.py |
-# |
-# Description: This file implements a Python DB-API 2.0 interface to |
-# PostgreSQL. |
-#=======================================================================|
-# Copyright 2000 by Billy G. Allie. |
-# All rights reserved. |
-# |
-# Permission to use, copy, modify, and distribute this software and its |
-# documentation for any purpose and without fee is hereby granted, pro- |
-# vided that the above copyright notice appear in all copies and that |
-# both that copyright notice and this permission notice appear in sup- |
-# porting documentation, and that the copyright owner's name not be |
-# used in advertising or publicity pertaining to distribution of the |
-# software without specific, written prior permission. |
-# |
-# THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
-# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN |
-# NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
-# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS |
-# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE |
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE |
-# USE OR PERFORMANCE OF THIS SOFTWARE. |
-#=======================================================================|
-# Revision History: |
-# |
-# Date Ini Description |
-# --------- --- ------------------------------------------------------- |
-# 05JUL2003 bga - Fixed a problem with PgNumeric where an exception can |
-# be thrown if the stated scale and precision of the |
-# returned in the first result row does not match later |
-# rows. |
-# 26JUN2003 bga - Applied patch from Laurent Pinchart to allow _quote |
-# to correctly process objects that are sub-classed |
-# from String and Long types. |
-# 05JUN2003 bga - Change the name of the quoting function back to |
-# _quote. Variables named like __*__ should be restrict |
-# to system names. |
-# 02JUN2003 bga - PgTypes is now hashable. repr() of a PgType will now |
-# return the repr() of the underlying OID. |
-# - Connection.binary() will now fail if autocommit is |
-# enabled. |
-# - Connection.binary() will no longer commit the trans- |
-# action after creating the large object. The applica- |
-# tion developer is now responsible for commiting (or |
-# for rolling back) the transaction [Bug #747525]. |
-# - Added PG_TIMETZ to the mix [Patch #708013]. |
-# - Pg_Money will now accept a string as a parameter. |
-# - PostgreSQL int2, int, int4 will now be cast into |
-# Python ints. Int8 will be cast into a Python long. |
-# Float4, float8, and money types will be cast into |
-# a Python float. |
-# 07MAR2003 bga - Correct problem with the PgNumeric.__radd__ method. |
-# [Bug #694358] |
-# - Correct problem with conversion of negitive integers |
-# (with a given scale and precision) to PgNumerics. |
-# [Bug #694358] |
-# - Work around a problem where the precision and scale |
-# of a query result can be different from the first re- |
-# sult in the result set. |
-# [Bug #697221] |
-# 12JAN2003 bga - Change the code so that the display length in the |
-# cursor.description attribute is always None instead |
-# of '-1'. |
-# - Fixed another problem with interval <-> DateTimeDelta |
-# casting. |
-# 23DEC2002 bga - Corrected a problem that caused the close of a portal |
-# (ie. PostgreSQL cursor) to fail. |
-# 13DEC2002 bga - Corrected a problem with interval <-> DateTimeDelta |
-# casting. [Bug #653044] |
-# 06DEC2002 bga - Corrected problem found by Adam Buraczewski in the |
-# __setupTransaction function. |
-# - Allow both 'e' and 'E' to signify an exponet in the |
-# PgNumeric constructor. |
-# 04DEC2002 bga - Correct some problems that were missed in yesterday's |
-# fixes (Thanks, Adam, for the help with the problems) |
-# 03DEC2002 bga - Fixed various problems with the constructor and the |
-# formatting routine. These correct the problems re- |
-# ported by Adam Buraczewski. |
-# 01DEC2002 bga - Fixed problems with new __setupTransaction function: |
-# 1. Made it a method of Connection, not Cursor. |
-# 2. inTransaction was only set if TransactionLevel was |
-# set. |
-# - Fixed instances where method name was incorrect: |
-# Connection__gcCursor -> _Connection__gcCursor |
-# Connection__closeCursor -> _Connection__closeCursor |
-# - Cleaned up code where there was unneeded references |
-# to conn in Connection class. |
-# 01DEC2002 gh - Handle the new '__quote__' method for arrays, too. |
-# - Still handle '_quote' methods for backwards compati- |
-# bility. This will will avoid complaints by users |
-# who have code depending on this. Like me. |
-# 28NOV2002 bga - Fixed changed PG_TIMESTAMP oid, added PG_TIMESTAMPTZ |
-# and PG_REFCURSOR oids. [Bug #845360] |
-# - Reference cursors are now type-casted into cursor ob- |
-# jects. |
-# 27NOV2002 bga - Completed the emulation of a String object for the |
-# PgBytea and PgOther classes. This corrects several |
-# problems with PgBytea concerning comparisons, using |
-# PgBytea types as keys in dictionaries, etc. |
-# 10NOV2002 bga - Added the __hash__ function to the PgNumeric class. |
-# Cleaned up the code in PgNumeric class and made some |
-# small improvments to it. |
-# 02NOV2002 bga - Added the PgArray class. This is a wrapper around a |
-# Python list and is used for all PostgreSQL arrays. |
-# This change was made so that lists and tuples no |
-# longer have a special meaning in the Cursor.execute() |
-# method. |
-# - Changed the quoting methods defined in the various |
-# classes defining PostgreSQL support types to __quote__|
-# 27OCT2002 gh - Merged the Unicode patch. Closes #484468. |
-# - Convert ROWID to PgInt8 instead of PgInt4 (the origi- |
-# nal behaviour led to overflow errors.) |
-# - Always set the displaysize field of |
-# cursor.description to -1. PostgreSQL 7.3 doesn't |
-# provide this information any more and it's pretty |
-# useless nowadays that we've mostly left line printers |
-# beyond us. |
-# 26OCT2002 bga - Column access by name (attribute and dictionary) now |
-# supports mixed-case column name. Be aware that if |
-# you define mixed-case column names in the database, |
-# you have to use the mixed-case name to access the co- |
-# lumn or it will not be found. For column names that |
-# were not defined with mixed-case in the database, the |
-# column access by name is case insensitive. |
-# 02OCT2002 gh - Only support mxDateTime 2.x and give useful error |
-# message if import fails. |
-# - Cosmetic changes: use cmp builtin where appropriate. |
-# - Fixed typo where PgTypes.__str__ was spelled |
-# incorrectly. Compare to None using "is" operator. |
-# - Take into account that libpq.PgInt8Type might not be |
-# available. |
-# - Add support for the INTERVAL type. |
-# 08SEP2002 gh Fixed various problems with the PgResultSet: |
-# - Column (attribute and dictionary) access is now case- |
-# insensitive. |
-# - Added __contains__ method. |
-# - Added default value parameter to get method. |
-# - Made setattr actually work. |
-# 11AUG2002 bga Fixed various problems with the PgNumeric type: |
-# - Added code to allow a float as an argument to the |
-# PgNumeric constructor. |
-# - You can now change the precision/scale of a PgNumeric |
-# by: a = PgNumeric(pgnumeric, new prec, new scale). |
-# This can be used to 'cast' a PgNumeric to the proper |
-# precision and scale before storing it in a field. |
-# - The arithmatic routines (__add__, __radd__, etc) now |
-# ensure that the arguments are properly coerced to the |
-# correct types. |
-# - Added support for the augmented arithmatic operations |
-# (__iadd__, etc). |
-# - The math routines would lose precision becuase the |
-# precision/scale were set to be the same as the first |
-# operand. This is no longer the case all precision is |
-# retained for the +, -, and * operations. |
-# 03AUG2002 gh - Fixed problem that occurs when a query on an OID |
-# field doesn't return any rows. [Bug #589370]. |
-# 29JUL2002 gh - Applied patch #569203 and also added __pos__ and |
-# __abs__ special methods to PgNumeric. |
-# 15MAY2002 gh - Got rid of redundant building and storing of the |
-# mapping of column names to column positions in the |
-# PgResultSet class. Now, rows of the same query are |
-# instances of a dynamically created class, which has |
-# this mapping as a class attribute instead of an at- |
-# tribute of the instance. This saves a lot of space, |
-# and also slightly increases performance of cursor |
-# fetches. |
-# 21APR2002 gh - Improved the array parsing, so that it now passes all |
-# the new mean testcases. Added support for parsing |
-# multidimensional arrays. Eventually, the array par- |
-# sing code should go as a support function into libpq. |
-# - Replaced all typechecks with "is" operators instead |
-# of equals. Mark McEahern had a problem with using |
-# pyPgSQL in combination with a FixedPoint class where |
-# the reason was that the FixedPoint class was not com- |
-# parable to None. The consensus on python-list was |
-# that None and all types are singletons, so they |
-# should be checked using "is", which is also faster, |
-# because it only checks for object identity. |
-# --------- bga Remove prior comments to reduce the size of the flower |
-# box. See revision 1.22 for earlier comments. |
-#--(H-)-----------------------------------------------------------------+
-"""
- PgSQL - A PyDB-SIG 2.0 compliant module for PostgreSQL.
-
- Copyright 2000 by Billy G. Allie <
[email protected]>
- See package documentation for further information on copyright.
-
- Inline documentation is sparse.
- See the Python DB-SIG 2.0 specification for usage information.
-
- basic usage:
-
- PgSQL.connect(connect_string) -> connection
- connect_string = 'host:port:database:user:password:options:tty'
- All parts are optional. You may also pass the information in as
- keyword arguments with the following keywords: 'host', 'port',
- 'database', 'user', 'password', 'options', and 'tty'. The port
- may also be passed in as part of the host keyword parameter,
- ie. host='localhost:5432'. Other optional parameters are
- client_encoding and unicode_results. If unicode_results is true,
- all strings from the backend are returned as Unicode strings.
-
- client_encoding accepts the same parameters as the encode method
- of Unicode strings. If you also want to set a policy for encoding
- errors, set client_encoding to a tuple, like ("koi8-r", "replace")
-
- Note that you still must make sure that the PostgreSQL client is
- using the same encoding as set with the client_encoding parameter.
- This is typically done by issuing a "SET CLIENT_ENCODING TO ..."
- SQL statement immediately after creating the connection.
-
- connection.cursor() -> cursor
- Create a new cursor object. A connection can support multiple
- cursors at the same time.
-
- connection.close()
- Closes the connection now (instead of when __del__ is called).
- The connection will be unusable from this point forward.
- NOTE: Any uncommited transactions will be rolled back and any
- open cursors for this connection will be closed.
-
- connection.commit()
- Commit any pending transactions for this connection.
-
- NOTE: This will reset any open cursors for this connection to their
- inital state. Any PostgreSQL portals in use by cursors will
- be closed and any remaining query results will be discarded.
-
- connection.rollback()
- Rollback any pending transactions for this connection.
-
- NOTE: This will reset any open cursors for this connection to their
- inital state. Any PostgreSQL portals in use by cursors will
- be closed and any remaining query results will be discarded.
-
- connection.binary(string) -> PgLargeObject
- Create a new PostgreSQL large object. If string is present, it is
- written to the new large object. The returned large object will
- not be opened (i.e. it will be closed).
-
- connection.un_link(OID|PgLargeObject)
- Un-links (removes) the PgLargeObject from the database.
-
- NOTE: This is a PostgreSQL extension to the Connection object.
- It is not safe to un-link PgLargeObjects while in a trans-
- action (in versions prior to 7.1) since this action can not
- be rollbacked completely. Therefore, an attempt to un-link
- while in a transaction (in versions prior to 7.1) will raise
- an exception.
-
- connection.version
- This instance of the PgVersion class contains information about
- the version of the PostgreSQL backend to which the connection
- object is connected to.
-
- NOTE: This is a PgSQL extension to the Connection object.
-
- cursor.execute(query[, param1[, param2, ..., paramN])
- Execute a query, binding the parameters if they are passed. The
- binding syntax is the same as the '%' operator except that only %s
- [or %(name)s] should be used and the %s [or %(name)s] should not be
- quoted. Any necessary quoting will be performed by the execute
- method.
-
- cursor.execute(query[, sequence])
- Execute a query, binding the contents of the sequence as parameters.
- The binding syntax is the same as the '%' operator except that only
- %s should be used and the %s should not be quoted. Any necessary
- quoting will be performed by the execute method.
-
- cursor.execute(query[, dictionary])
- Execute a query, binding the contents of the dictionary as para-
- meters. The binding syntax is the same as the '%' operator except
- that only %s [or %(name)s] should be used and the %s [or %(name)s]
- should not be quoted. Any necessary quoting will be performed by
- the execute method.
-
- NOTE: In order to use a PostgreSQL portal (i.e. DECLARE ... CURSOR
- FOR ...), the word SELECT must be the first word in the query,
- and can only be be proceeded by spaces and tabs. If this is not
- the case, a cursor will be simulated by reading the entire result
- set into memory and handing out the results as needed.
-
- NOTE: PostgreSQL cursors are read-only. SELECT ... FOR UPDATE queries
- will not use PostgreSQL cursors, but will simulate a cursor by
- reading the entire result set into memory and handing out the
- results as needed.
-
- NOTE: Setting the variable, PgSQL.noPostgresCursor, to 1 will cause
- PgSQL to NOT use PostgreSQL cursor (DECLARE ... CURSOR FOR ...),
- even if all the conditions for doing so are met. PgSQL will
- simulate a cursor by reading the entire result set into memory
- and handing out the results as needed.
-
- cursor.executemany(query, sequence_of_params)
- Execute a query many times, once for each params in the sequence.
-
- NOTE: The restriction on the use of PostgreSQL cursors described in
- the cursor.execute() note also applies to cursor.executemany().
- The params in the sequence of params can be a list, tuple or
- dictionary.
-
- cursor.fetchone() -> PgResultSet
- Fetch the next row of the query result set as a single PgResultSet
- containing the column data. Returns None when no more rows are
- available. A PgResultSet is a sequence that can be indexed by
- a column name in addition to an integer.
-
- cursor.fetchmany([size]) -> [PgResultSet, ...]
- Fetch the next set of rows from the query result, returning a
- sequence of PgResultSets. An empty sequence is returned when
- no more rows are available.
-
- The number of rows returned is determined by the size parameter.
- If the size parameter is ommited, the value of cursor.arraysize
- is used. If size is given, cursor.arraysize is set to that value.
-
- cursor.fetchall() -> [PgResultSet, ...]
- Fetch all (remaining) rows from the query result, returning a
- sequence of PgResultSets. An empty sequence is returned when
- no more rows are available.
-
- cursor.description -> [(column info), ... ]
- Returns a sequence of 8-item tuples. Each tuple describes one
- column of the result: (name, type code, display size, internal
- size, precision, scale, null_ok, isArray).
-
- NOTE: null_ok is not implemented.
- isArray is a PostgreSQL specific extension.
-
- cursor.rowcount
- The number of rows the last execute produced (for DQL statements)
- or affected (for DML statement).
-
- cursor.oidValue
- The object ID of the inserted record, if the last SQL command
- was an INSERT, otherwise it returns 0 (aka. InvalidOid)
-
- NOTE: oidValue is a PostgreSQL specific extension.
-
- cursor.close()
- Closes the cursor now (instead of when __del__ is called). The
- cursor will be unusable from this point forward.
-
- cursor.rewind()
- Moves the cursor back to the beginning of the query result.
- This is a PgSQL extension to the PyDB 2.0 API.
-
- PgResultSet.description() -> [(column info), ... ]
- Returns a sequence of 8-item tuples. Each tuple describes one
- column of the result: (name, type code, display size, internal
- size, precision, scale, null_ok. isArray).
-
- NOTE: null_ok is not implemented.
- isArray is a PostgreSQL specific extension.
-
- PgResultSet.<column name> -> value
- Column names are attributes to the PgResultSet.
-
- Note: Setting the variable, PgSQL.fetchReturnsList = 1 will cause
- the fetch*() methods to return a list instead of a PgResultSet.
-
- PgSQL.version
- This string object contains the version number of PgSQL.
-"""
-
-from types import *
-from sys import getrefcount, getdefaultencoding
-import sys
-import copy
-import string
-import re
-import new
-
-try:
- import weakref
- noWeakRef = 0
-except:
- noWeakRef = 1
-
-try:
- from mx import DateTime
-except ImportError:
- raise ImportError, \
- """You need to install mxDateTime
- (http://www.egenix.com/files/python/eGenix-mx-Extensions.html)"""
-from pyPgSQL.libpq import *
-
-version = '$Revision: 1.2 $'
-
-apilevel = '2.0'
-threadsafety = 1
-paramstyle = 'pyformat'
-
-# Setting this variable to 1 will cause the fetch*() methods to return
-# a list instead of a PgResultSet
-
-fetchReturnsList = 0
-
-# Setting this variable to 1 will prevent the use of a PostgreSQL Cursor
-# (via the "DECLARE ... CURSOR FOR ..." syntax). A cursor will be simulated
-# by reading all of the query result into memory and doling out the results
-# as needed.
-
-noPostgresCursor = 0
-
-re_DQL = re.compile('^[\s]*SELECT[\s]', re.I)
-re_DRT = re.compile('[\s]*DROP[\s]+TABLE[\s]', re.I)
-re_DRI = re.compile('[\s]*DROP[\s]+INDEX[\s]', re.I)
-re_4UP = re.compile('[\s]FOR[\s]+UPDATE', re.I)
-
-replace = string.replace
-
-#-----------------------------------------------------------------------+
-# Make the required Date/Time constructor visable in the PgSQL module. |
-#-----------------------------------------------------------------------+
-
-Date = DateTime.Date
-Time = DateTime.Time
-Timestamp = DateTime.Timestamp
-DateFromTicks = DateTime.DateFromTicks
-TimeFromTicks = DateTime.TimeFromTicks
-TimestampFromTicks = DateTime.TimestampFromTicks
-
-#-----------------------------------------------+
-# The DateTimeDelta type for PgInterval support |
-#-----------------------------------------------+
-
-DateTimeDelta = DateTime.DateTimeDelta
-
-#-------------------------------+
-# Also the DateTime types |
-#-------------------------------+
-
-DateTimeType = DateTime.DateTimeType
-DateTimeDeltaType = DateTime.DateTimeDeltaType
-DateTimeDelta = DateTime.DateTimeDelta
-
-#-----------------------------------------------------------------------+
-# Name: DBAPITypeObject |
-# |
-# Description: The DBAPITypeObject class allows implementing the re- |
-# quired DP-API 2.0 type objects even if their are multi- |
-# ple database types for the DP-API 2.0 types. |
-# |
-# Note: This object is taken from the Python DP-API 2.0 imple- |
-# mentation hints. |
-#-----------------------------------------------------------------------+
-
-class DBAPITypeObject:
- def __init__(self, name, *values):
- self.name = name
- self.values = values
-
- def __repr__(self):
- return self.name
-
- def __cmp__(self, other):
- if other in self.values:
- return 0
- elif other < self.values:
- return 1
- return -1
-
-# Define the object types required by the DB-API 2.0 specification.
-
-BINARY = DBAPITypeObject('BINARY', PG_OID, PG_BLOB, PG_BYTEA)
-
-DATETIME = DBAPITypeObject('DATETIME', PG_DATE, PG_TIME, PG_TIMETZ,
- PG_TIMESTAMP, PG_TIMESTAMPTZ,
- PG_ABSTIME, PG_RELTIME,
- PG_INTERVAL, PG_TINTERVAL)
-
-NUMBER = DBAPITypeObject('NUMBER', PG_INT8, PG_INT2, PG_INT4, PG_FLOAT4,
- PG_FLOAT8, PG_MONEY, PG_NUMERIC)
-
-ROWID = DBAPITypeObject('ROWID', PG_OID, PG_ROWID, PG_CID, PG_TID, PG_XID)
-
-STRING = DBAPITypeObject('STRING', PG_CHAR, PG_BPCHAR, PG_TEXT, PG_VARCHAR,
- PG_NAME)
-
-# BOOLEAN is the PostgreSQL boolean type.
-
-BOOLEAN = DBAPITypeObject('BOOLEAN', PG_BOOL)
-
-# OTHER is for PostgreSQL types that don't fit in the standard Objects.
-
-OTHER = DBAPITypeObject('OTHER', PG_POINT, PG_LSEG, PG_PATH, PG_BOX,
- PG_POLYGON, PG_LINE, PG_CIDR, PG_CIRCLE,
- PG_INET, PG_MACADDR, PG_ACLITEM,
- PG_REFCURSOR)
-
-#-----------------------------------------------------------------------+
-# Name: PgTypes |
-# |
-# Description: PgTypes is an object wrapper for the type OID's used by |
-# PostgreSQL. It is used to display a meaningful text |
-# description of the type while still allowing it to be |
-# compared as a numeric value. |
-#-----------------------------------------------------------------------+
-
-class PgTypes:
- def __init__(self, value):
- self.value = value
-
- def __coerce__(self, other):
- if type(other) in [IntType, LongType, FloatType]:
- return (self.value, int(other))
- return None
-
- def __cmp__(self, other):
- return cmp(self.value, other)
-
- def __repr__(self):
- return repr(self.value)
-
- def __str__(self):
- return PQftypeName(self.value)
-
- def __int__(self):
- return int(self.value)
-
- def __long__(self):
- return long(self.value)
-
- def __float__(self):
- return float(self.value)
-
- def __complex__(self):
- return complex(self.value)
-
-#-----------------------------------------------------------------------+
-# Name: TypeCache |
-# |
-# Description: TypeCache is an object that defines methods to: |
-# 1. Cast PostgreSQL result strings into the appropiate |
-# Python type or object [typecast()]. |
-# 2. Retrieve addition information about a type from the |
-# PostgreSQL system catalogs [getTypeInfo()]. This |
-# type information is maintained in a local cache so |
-# that subsequent request for the same information do |
-# not require a database query to fulfill it. |
-#-----------------------------------------------------------------------+
-
-class TypeCache:
- """Type cache -- used to cache postgreSQL data type information."""
-
- def __init__(self, conn):
- if noWeakRef:
- self.__conn = conn
- else:
- self.__conn = weakref.proxy(conn, self.__callback)
- self.__type_cache = {}
- self.__lo_cache = {}
-
- def __callback(self, o):
- self.__conn = None
-
- def interval2DateTimeDelta(self, s):
- """Parses PostgreSQL INTERVALs.
- The expected format is [[[-]YY years] [-]DD days] [-]HH:MM:SS.ss"""
- parser = DateTime.Parser.DateTimeDeltaFromString
-
- ydh = s.split()
- ago = 1
-
- result = DateTimeDelta(0)
-
- # Convert any years using 365.2425 days per year, which is PostgreSQL's
- # assumption about the number of days in a year.
- if len(ydh) > 1:
- if ydh[1].lower().startswith('year'):
- result += parser('%s days' % ((int(ydh[0]) * 365.2425),))
- ydh = ydh[2:]
-
- # Converts any days and adds it to the years (as an interval)
- if len(ydh) > 1:
- if ydh[1].lower().startswith('day'):
- result += parser('%s days' % (ydh[0],))
- ydh = ydh[2:]
-
- # Adds in the hours, minutes, seconds (as an interval)
- if len(ydh) > 0:
- result += parser(ydh[0])
-
- return result
-
- def parseArray(self, s):
- """Parse a PostgreSQL array strings representation.
- This parses a PostgreSQL array and return a list of the array
- elements as strings.
- """
- class LeaveLoopException(Exception): pass
-
- # Get rid of the escaping in the array string
- def convertEscapes(s):
- # If we're called with a list in a multi-dimensional
- # array, simply return the list. We only convert the
- # elements of the multi-dimensional array.
- if type(s) is ListType:
- return s
-
- schars = []
- escape = 0
- octdigits = []
-
- for char in s:
- if char == '\\':
- escape += 1
- if escape == 2:
- schars.append(char)
- escape = 0
- else:
- if escape:
- if char in string.digits:
- octdigits.append(char)
- else:
- if octdigits != []:
- curchar = chr(int(octdigits[0]) * 64) + \
- chr(int(octdigits[1]) * 8) + \
- chr(int(octdigits[2]))
- schars.append(curchar)
- octdigits = []
- schars.append(char)
- else:
- schars.append(char)
- escape = 0
- return "".join(schars)
-
- lst = PgArray()
- s = s[1:-1] # drop '{' and '}' at start/end
-
- # If the array is empty, return immediately
- if len(s) == 0:
- return lst
-
- pos = 0
- try:
- while 1:
- if s[pos] == '"':
- # A quoted element, find the end-quote, which is the next
- # quote char that is not escaped.
- end_quote_pos = pos + 1
- escape = 0
- while 1:
- if s[end_quote_pos] == '\\':
- escape = not escape
- elif s[end_quote_pos] == '"':
- if not escape:
- break
- escape = 0
- else:
- escape = 0
- end_quote_pos += 1
- lst.append(convertEscapes(s[pos + 1:end_quote_pos]))
-
- # Skip quote char and next comma
- pos = end_quote_pos + 2
-
- # If end-of-string. leave loop.
- if pos >= len(s):
- break
- else:
- # This array element is not quoted, so it ends either at
- # the next comma that isn't escaped, or at the end of the
- # string, or, if it contains a subarray, at the position
- # of the corresponding curly brace.
- if s[pos] != '{':
- next_comma_pos = pos + 1
- escape = 0
- while 1:
- if next_comma_pos >= len(s):
- # This is the last array element.
- lst.append(convertEscapes(s[pos:]))
- raise LeaveLoopException
-
- if s[next_comma_pos] == '\\':
- escape = not escape
- elif s[next_comma_pos] == ',':
- if not escape:
- break
- escape = 0
- else:
- escape = 0
- next_comma_pos += 1
-
- curelem = s[pos:next_comma_pos]
- if curelem.startswith("{"):
- lst.append(self.parseArray(curelem[1:-1]))
- else:
- lst.append(convertEscapes(curelem))
- pos = next_comma_pos + 1
- if s[pos] == ',':
- pos += 1
- else:
- # The current character is '{', which means we've
- # found a sub-array:
- # We find the end of the sub-array, then feed this
- # string into parseArray again.
- escape = 0
- open_braces = 1
- closing_brace_pos = pos + 1
- in_quotes = 0
- while 1:
- if s[closing_brace_pos] == '\\':
- escape = not escape
- elif s[closing_brace_pos] == '{':
- if (not escape) and (not in_quotes):
- open_braces += 1
- escape = 0
- elif s[closing_brace_pos] == '}':
- if (not escape) and (not in_quotes):
- open_braces -= 1
- if open_braces == 0:
- break
- escape = 0
- elif s[closing_brace_pos] == '"':
- if not escape:
- in_quotes = not in_quotes
- escape = 0
- else:
- escape = 0
- closing_brace_pos += 1
-
- curelem = s[pos:closing_brace_pos + 1]
- lst.append(self.parseArray(curelem))
- pos = closing_brace_pos + 1
- if pos >= len(s):
- break
- if s[pos] == ',':
- pos += 1
-
- except LeaveLoopException:
- pass
-
- #lst = map(convertEscapes, lst)
- return lst
-
- def typecast(self, colinfo, value):
- """
- typecast(rowinfo, value)
- Convert certain postgreSQL data types into the appropiate python
- object."""
-
- if value is None:
- return value
-
- _fn, _ft, _ds, _is, _p, _s, _nu, _ia = colinfo
-
- _ftv = _ft.value
-
- if _ia:
- # Convert string representation of the array into PgArray object.
- _list = self.parseArray(value)
- return self.handleArray(colinfo, _list)
-
- if _ftv == PG_INT2:
- if type(value) is PgInt2Type:
- return value
- else:
- return int(value)
- elif _ftv == PG_INT4:
- if type(value) is IntType:
- return value
- else:
- return int(value)
- elif _ftv == PG_INT8 or _ftv == ROWID:
- if type(PgInt8) is ClassType:
- if isinstance(value, PgInt8):
- return value
- else:
- if type(value) is PgInt8Type:
- return value
- return long(value)
- elif _ftv == PG_NUMERIC:
- if isinstance(value, PgNumeric):
- return value
- else:
- try:
- return PgNumeric(value, _p, _s)
- except OverflowError:
- # If we reached this point, then the precision and scale
- # of the current field does not match the precision and
- # scale of the first record in the result set (there are
- # a few reasons why this can happen). Let PgNumeric
- # figure out a precision and scale from the value.
- return PgNumeric(value)
- elif _ftv == PG_MONEY:
- if isinstance(value, PgMoney):
- return value
- else:
- return PgMoney(value).value
- elif _ftv == DATETIME:
- if type(value) in [DateTimeType, DateTimeDeltaType]:
- return value
- else:
- if _ftv == PG_INTERVAL:
- return value #self.interval2DateTimeDelta(value)
- else:
- return value #DateTime.ISO.ParseAny(value)
- elif _ftv == BINARY:
- if isinstance(value, PgBytea) or type(value) is PgLargeObjectType:
- return value
- elif type(value) is IntType:
- return PgLargeObject(self.conn, value)
- else:
- return PgBytea(value)
- elif _ftv == PG_REFCURSOR:
- return self.__conn.cursor(value, isRefCursor=PG_True)
- elif _ftv == OTHER:
- if isinstance(value, PgOther):
- return value
- else:
- return value
- elif self.__conn.unicode_results \
- and _ftv in (PG_CHAR, PG_BPCHAR, PG_TEXT, PG_VARCHAR, PG_NAME):
- return unicode(value, *self.__conn.client_encoding)
- # Other typecasting is not needed. It will be once support for
- # the other built-in types (ie. box, line, inet, cidr, etc) are added.
-
- return value
-
- def handleArray(self, colinfo, lst):
- # If the list is empty, just return the empty list.
-
- if len(lst) == 0:
- return lst
-
- _fn, _ft, _ds, _is, _p, _s, _nu, _ia = colinfo
-
- _ftv = _ft.value
-
- for _i in range(len(lst)):
- if isinstance(lst[_i], PgArray):
- lst[_i] = self.handleArray(colinfo, lst[_i])
- elif _ftv == PG_INT4 or _ftv == ROWID:
- lst[_i] = int(lst[_i])
- elif _ftv == PG_INT8:
- lst[_i] = PgInt8(lst[_i])
- elif _ftv == PG_NUMERIC:
- try:
- lst[_i] = PgNumeric(lst[_i], _p, _s)
- except OverflowError:
- # If we reached this point, then the precision and scale
- # of the current field does not match the precision and
- # scale of the first record in the result set (there are
- # a few reasons why this can happen). Let PgNumeric
- # figure out a precision and scale from the value.
- lst[_i] = PgNumeric(lst[_i])
- elif _ftv == PG_INT2:
- lst[_i] = PgInt2(lst[_i])
- elif _ftv == DATETIME:
- lst[_i] = DateTime.ISO.ParseAny(lst[_i])
- elif _ftv == PG_MONEY:
- if lst[_i][0] == '(':
- lst[_i] = PgMoney(-float(replace(lst[_i][2:-1], ',', '')))
- elif lst[_i][0] == '-':
- lst[_i] = PgMoney(-float(replace(lst[_i][2:], ',', '')))
- else:
- lst[_i] = PgMoney(float(replace(lst[_i][1:], ',', '')))
- elif _ftv == BINARY:
- if _ftv == PG_BYTEA:
- # There is no need to un-escape lst[_i], it's already been
- # done when the PostgreSQL array was converted to a list
- # via parseArray().
- lst[_i] = PgBytea(lst[_i])
- else:
- lst[_i] = PgLargeObject(self.conn, int(lst[_i]))
-
- return lst
-
- def getTypeInfo(self, pgtype):
- try:
- return self.__type_cache[pgtype.value]
- except KeyError:
- _nl = len(self.__conn.notices)
- _res = self.__conn.conn.query("SELECT typname, -1 , typelem "
- "FROM pg_type "
- "WHERE oid = %s" % pgtype.value)
-
- if len(self.__conn.notices) != _nl:
- raise Warning, self.__conn.notices.pop()
-
- _n = _res.getvalue(0, 0)
- _p = _res.getvalue(0, 1)
- _b = _res.getvalue(0, 2)
- if _n[0] == '_':
- _n = _n[1:]
- _i = 1
- else:
- _i = 0
-
- self.__type_cache[pgtype.value] = (_n, _p, _i, PgTypes(_b))
-
- return self.__type_cache[pgtype.value]
-
-#-----------------------------------------------------------------------+
-# Name: PgOther |
-# |
-# Description: A Python wrapper class for the PostgreSQL types that do |
-# not (yet) have an implementation in python. The number |
-# of types in this category will shrink as more wrappers |
-# are implemented. |
-# |
-# Note: A Python String is used to store the PostgreSQL type in |
-# class. |
-#-----------------------------------------------------------------------+
-
-class PgOther:
- def __init__(self, value):
- if type(value) is not StringType:
- raise TypeError, "argument must be a string."
-
- self.value = value
-
- if hasattr(value, '__methods__'):
- for i in self.value.__methods__:
- exec 'self.%s = self.value.%s' % (i, i)
-
- # This definition of __coerce__ will cause Python to always call the
- # (existing) arithmatic operators for this class. We can the perform the
- # appropiate operation on the base type, letting it decide what to do.
- def __coerce__(self, other):
- return (self, other)
-
- def __getitem__(self, index):
- if type(index) is SliceType:
- if index.step is None:
- return PgOther(self.value[index.start:index.stop])
- else:
- return PgOther(self.value[index.start:index.stop:index.step])
-
- return self.value[index];
-
- def __setitem__(self, index, item):
- raise TypeError, "object doesn't support slice assignment"
-
- def __delitem__(self, index):
- raise TypeError, "object doesn't support slice deletion"
-
- def __contains__(self, item):
- return (item in self.value)
-
- if sys.version_info < (2, 0):
- # They won't be defined if version is at least 2.0 final
- def __getslice__(self, i, j):
- return PgOther(self.value[max(0, i):max(0, j)])
-
- def __setslice__(self, i, j, seq):
- raise TypeError, "object doesn't support slice assignment"
-
- def __delslice__(self, i, j):
- raise TypeError, "object doesn't support slice deletion"
-
- # NOTE: A string is being concatenated to a PgOther, so the result type
- # is a PgOther
- def __add__(self, other):
- return PgOther((self.value + other))
-
- # NOTE: A PgOther is being concatenated to a string, so the result type
- # is a string.
- def __radd__(self, other):
- return (other + self.value)
-
- def __mul__(self, other):
- return PgOther((self.value * other))
-
- def __rmul__(self, other):
- return PgOther((self.value * other))
-
- def __repr__(self):
- return repr(self.value)
-
- def __str__(self):
- return str(self.value)
-
- def __hash__(self):
- return hash(self.value)
-
- def __cmp__(self, other):
- return cmp(self.value, other)
-
- def __rcmp__(self, other):
- return cmp(other, self.value)
-
- def __lt__(self, other):
- return self.value < other
-
- def __le__(self, other):
- return self.value <= other
-
- def __eq__(self, other):
- return self.value == other
-
- def __ne__(self, other):
- return self.value != other
-
- def __gt__(self, other):
- return self.value > other
-
- def __ge__(self, other):
- return self.value >= other
-
- # NOTE: A PgOther object will use the PgQuoteString() function in libpq.
- def _quote(self, forArray=0):
- if self.value:
- return PgQuoteString(self.value, forArray)
- return 'NULL'
-
-#-----------------------------------------------------------------------+
-# Name: PgArray |
-# |
-# Description: A Python wrapper class for PostgreSQL arrays. |
-# It is used so that the list type can be used as an arg- |
-# ument to Connection.execute() without being treated as |
-# a PostgreSQL array. |
-#-----------------------------------------------------------------------+
-
-class PgArray:
- def __init__(self, value=None):
- if value is None:
- self.value = []
- return
-
- if type(value) is not ListType and not isinstance(value, PgArray):
- raise TypeError, "argument must be a list or a PgArray."
-
- # We have to insure that nested mutable sequences (list and PgArray)
- # get copied.
- for i in range(len(value)):
- if type(value[i]) is ListType or isinstance(value[i], PgArray):
- value[i] = PgArray(value[i][:])
-
- self.value = value
-
- # Define the methods used
- def append(self, item):
- if type(item) is ListType:
- item = PgArray(item)
- self.value.append(item)
-
- def count(self, item):
- return self.value.count(item)
-
- def extend(self, item):
- if type(item) is ListType:
- item = PgArray(item)
- self.value.extend(item)
-
- def index(self, item):
- return self.value.index(item)
-
- def insert(self, key, item):
- if type(item) is ListType:
- item = PgArray(item)
- self.value.insert(key, item)
-
- def pop(self, key=-1):
- return self.value.pop(key)
-
- def remove(self, item):
- self.value.remove(item)
-
- def reverse(self):
- self.value.reverse()
-
- def sort(self, compfunc=None):
- if compfunc is None:
- self.value.sort()
- else:
- self.value.sort(compfunc)
-
- def __cmp__(self, other):
- if not isinstance(other, PgArray):
- return cmp(id(self), id(other))
-
- # PgArray objects are considered equal if:
- # 1. The lengh of the PgArray objects are equal and
- # 2. Each item[k] in the PgArray objects are equal.
-
- res = cmp(len(self), len(other))
- if res != 0:
- return res
-
- for i in range(len(self.value)):
- res = cmp(self.value[i], other.value[i])
- if res != 0:
- return res
-
- return 0
-
- def __len__(self):
- return len(self.value)
-
- def __getitem__(self, index):
- if type(index) is SliceType:
- if index.step is None:
- return PgArray(self.value[index.start:index.stop])
- else:
- return PgArray(self.value[index.start:index.stop:index.step])
-
- return self.value[index];
-
- def __setitem__(self, index, item):
- if type(item) is ListType:
- item = PgArray(item)
-
- if type(index) is SliceType:
- if index.step is None:
- self.value[index.start:index.stop] = item
- else:
- self.value[index.start:index.stop:index.step] = item
- else:
- self.value[index] = item
-
- def __delitem__(self, index):
- if type(index) is SliceType:
- if index.step is None:
- del self.value[index.start:index.stop]
- else:
- del self.value[index.start:index.stop:index.step]
- else:
- del self.value[index];
-
- def __contains__(self, item):
- return (item in self.value)
-
- if sys.version_info < (2, 0):
- # They won't be defined if version is at least 2.0 final
- def __getslice__(self, i, j):
- return PgArray(self.value[max(0, i):max(0, j)])
-
- def __setslice__(self, i, j, seq):
- if type(seq) is ListType:
- seq = PgArray(seq)
- self.value[max(0, i):max(0, j)] = seq.value
-
- def __delslice__(self, i, j):
- del self.value[max(0, i):max(0, j)]
-
- def __add__(self, other):
- return PgArray((self.value + other))
-
- def __radd__(self, other):
- return PgArray(other + self.value)
-
- def __iadd__(self, other):
- value = value + other
- return self
-
- def __mul__(self, other):
- return PgArray((self.value * other))
-
- def __rmul__(self, other):
- return PgArray((self.value * other))
-
- def __imul__(self, other):
- value = value * other
- return self
-
- def __repr__(self):
- return repr(self.value)
-
- def __str__(self):
- return str(self.value)
-
- # NOTE: A PgArray object will use the _handleArray() function to quote
- # itself.
- def _quote(self, forArray=0):
- if self.value:
- return _handleArray(self.value)
- return 'NULL'
-
-#-----------------------------------------------------------------------+
-# Name: PgBytea |
-# |
-# Description: A Python wrapper class for the PostgreSQL BYTEA type. |
-# |
-# Note: A Python String is used to store the PostgreSQL type in |
-# class. |
-#-----------------------------------------------------------------------+
-
-class PgBytea:
- def __init__(self, value):
- if type(value) is not StringType:
- raise TypeError, "argument must be a string."
-
- self.value = value
-
- if hasattr(value, '__methods__'):
- for i in self.value.__methods__:
- exec 'self.%s = self.value.%s' % (i, i)
-
- # This definition of __coerce__ will cause Python to always call the
- # (existing) arithmatic operators for this class. We can the perform the
- # appropiate operation on the base type, letting it decide what to do.
- def __coerce__(self, other):
- return (self, other)
-
- def __getitem__(self, index):
- if type(index) is SliceType:
- if index.step is None:
- return PgBytea(self.value[index.start:index.stop])
- else:
- return PgBytea(self.value[index.start:index.stop:index.step])
-
- return self.value[index];
-
- def __setitem__(self, index, item):
- raise TypeError, "object doesn't support slice assignment"
-
- def __delitem__(self, index):
- raise TypeError, "object doesn't support slice deletion"
-
- def __contains__(self, item):
- return (item in self.value)
-
- if sys.version_info < (2, 0):
- # They won't be defined if version is at least 2.0 final
- def __getslice__(self, i, j):
- return PgBytea(self.value[max(0, i):max(0, j)])
-
- def __setslice__(self, i, j, seq):
- raise TypeError, "object doesn't support slice assignment"
-
- def __delslice__(self, i, j):
- raise TypeError, "object doesn't support slice deletion"
-
- def __add__(self, other):
- return PgBytea((self.value + other))
-
- def __radd__(self, other):
- return PgBytea(other + self.value)
-
- def __mul__(self, other):
- return PgBytea((self.value * other))
-
- def __rmul__(self, other):
- return PgBytea((self.value * other))
-
- def __repr__(self):
- return repr(self.value)
-
- def __str__(self):
- return str(self.value)
-
- def __hash__(self):
- return hash(self.value)
-
- def __cmp__(self, other):
- return cmp(self.value, other)
-
- def __rcmp__(self, other):
- return cmp(other, self.value)
-
- def __lt__(self, other):
- return self.value < other
-
- def __le__(self, other):
- return self.value <= other
-
- def __eq__(self, other):
- return self.value == other
-
- def __ne__(self, other):
- return self.value != other
-
- def __gt__(self, other):
- return self.value > other
-
- def __ge__(self, other):
- return self.value >= other
-
- # NOTE: A PgBytea object will use the PgQuoteBytea() function in libpq
- def _quote(self, forArray=0):
- if self.value:
- return PgQuoteBytea(self.value, forArray)
- return 'NULL'
-
-#-----------------------------------------------------------------------+
-# Name: PgNumeric |
-# |
-# Description: A Python wrapper class for the PostgreSQL numeric type. |
-# It implements addition, subtraction, mulitplcation, and |
-# division of scaled, fixed precision numbers. |
-# |
-# Note: The PgNumeric class uses a Python Long type to store |
-# the PostgreSQL numeric type. |
-#-----------------------------------------------------------------------+
-
-class PgNumeric:
- def __init__(self, value, prec=None, scale=None):
- if type(value) in [IntType, LongType] or value is None:
- if prec is None or scale is None:
- raise TypeError, \
- "you must supply precision and scale when value is a " \
- "integer, long, or None"
- if value is None:
- self.__v = value
- else:
- self.__v = long(value)
- # Check to see if the value is too large for the given
- # precision/scale
- _v = str(abs(value))
- if _v[-1:] == 'L':
- _v = _v[:-1]
- if len(_v) > prec:
- raise OverflowError, "value too large for PgNumeric"
-
- self.__p = prec
- self.__s = scale
- elif type(value) in (FloatType, StringType):
- # Get the value to convert as a string.
- # The expected input is in the form of [+|-][d]*[.[d]*][e[d]+]
-
- # First get the value as a (trimmed) string
- if type(value) is FloatType:
- _v = str(value)
- else:
- _v = value.split()
- if len(_v) == 0 or len(_v) > 1:
- raise ValueError, \
- "invalid literal for PgNumeric: %s" % value
- _v = _v[0]
-
- # Save the sign character (if any) and remove from the string
- _sign = '+'
- if _v[0] in ('-', '+'):
- _sign = _v[0]
- _v = _v[1:]
-
- # Split the remaining string into int part, frac part and exponet
- _d = _v.rfind('.')
- _e = _v.rfind('e')
-
- # Ensure that _e and _v contains a sane value
- if _e < 0:
- _e = _v.rfind('E')
- if _e < 0:
- _e = len(_v)
-
- if _d < 0:
- _d = _e
-
- _ip = _v[:_d]
- _fp = _v[_d+1:_e]
- _xp = _v[_e+1:]
-
- # Check the validity of the input
- if len(_ip) == 0 and len(_fp) == 0:
- raise ValueError, \
- "invalid literal for PgNumeric: %s" % value
-
- if len(_xp) > 0:
- try:
- _exp = int(_xp)
- except:
- raise ValueError, \
- "invalid literal for PgNumeric: %s" % value
- else:
- _exp = 0
-
- if _exp > 999:
- raise OverflowError, "value too large for PgNumeric"
-
- if _exp < -999:
- raise OverflowError, "value too small for PgNumeric"
-
- # Create the string that will become the base (long) object
- _v = _ip + _fp
-
- _sc = len(_fp)
- if _exp > 0:
- if _exp > _sc:
- _v = _v + ("0" * (_exp - _sc))
- _sc = 0
- else:
- _sc = _sc - _exp
- else:
- _sc = _sc - _exp
-
- try:
- self.__v = long(_sign + _v)
- except:
- raise ValueError, \
- "invalid literal for PgNumeric: %s" % value
-
- self.__p = len(_v)
- if self.__p < _sc:
- self.__p = _sc
- self.__s = _sc
-
- # Now adjust for the inputted scale (if any)
- if scale is None:
- pass
- else:
- _adj = scale - self.__s
- if _adj > 0:
- self.__v = self.__v * (10L ** (scale - self.__s))
- elif _adj < 0:
- self.__v = self._round(self.__v, -_adj)
-
- self.__p = self.__p + _adj
- self.__s = scale
-
- # Apply the inputted precision (if any)
- if prec is None:
- pass
- else:
- if prec > 1000:
- raise ValueError, "precision too large for PgNumeric"
- elif self.__p > prec:
- raise OverflowError, "value too large for PgNumeric"
- else:
- self.__p = prec
- elif isinstance(value, PgNumeric):
- # This is used to "cast" a PgNumeric to the specified precision
- # and scale. It can also make a copy of a PgNumeric.
- self.__v = value.__v
- if scale:
- self.__s = scale
- _ds = scale - value.__s
- else:
- self.__s = value.__s
- _ds = 0
- if prec:
- self.__p = prec
- else:
- self.__p = value.__p
- # Now we adjust the value to reflect the new scaling factor.
- if _ds > 0:
- if _ds == 1:
- self.__v = self.__v * 10
- else:
- self.__v = self.__v * (10L ** _ds)
- elif _ds < 0:
- self.__v = self._round(self.__v, -_ds)
- if self.__v > (10L ** self.__p):
- raise OverflowError, "result exceeds precision of %d" % self.__p
- else:
- raise TypeError, "value can not be converted to a PgNumeric."
-
- if self.__s > self.__p:
- raise ValueError, \
- "scale of %d exceeds precision of %d" % (self.__s, self.__p)
-
- # The value (10L ** self.__s) is used a lot. Save it as a constant
- # to save a (small) bit of time.
-
- self.__sf = 10L ** self.__s
-
- def __fmtNumeric(self, value=None):
- if value is None:
- _v = self.__v
- else:
- _v = value
-
- # Check for a negative value and adjust if necessary
- if _v < 0:
- _sign = '-'
- _v = -_v
- else:
- _sign = ''
- _v = str(_v)
-
- # Check to see if the string representation of the python long has
- # a trailing 'L', if so, remove it. Python 1.5 has the trailing 'L',
- # Python 1.6 does not.
- if _v[-1:] == 'L':
- _v = _v[:-1]
-
- # Check to see if the numeric is less than one and fix string if so.
- if len(_v) <= self.__s:
- _v = ("0" * (self.__s - len(_v) + 1)) + _v
-
- if self.__s:
- _s = "%s%s.%s" % (_sign, _v[:-(self.__s)], _v[-(self.__s):])
- else:
- _s = "%s%s" % (_sign, _v)
- return _s
-
- def __repr__(self):
- return "<PgNumeric instance - precision: %d scale: %d value: %s>" % \
- (self.__p, self.__s, self.__fmtNumeric())
-
- def __str__(self):
- return self.__fmtNumeric()
-
- def getScale(self):
- return self.__s
-
- def getPrecision(self):
- return self.__p
-
- def __coerce__(self, other):
- if isinstance(other, PgNumeric):
- return self, other
- elif type(other) in [IntType, LongType]:
- _s = str(other)
- if _s[-1:] == 'L':
- _s = _s[:-1] # Work around v1.5/1.6 differences
- return (self, PgNumeric(_s))
- elif type(other) == FloatType:
- return (self, PgNumeric(other))
- return None
-
- def _round(self, value, drop):
- if drop == 1:
- return ((value + 5L) / 10L)
- elif drop > 1:
- return (((value / (10L ** (drop - 1))) + 5L) / 10L)
-
- return value
-
- def __add__(self, other):
- _c = self.__coerce__(other)
- if _c is None:
- return None
- self, other = _c
- if self.__s < other.__s:
- _s = self.__v * (other.__sf / self.__sf)
- _o = other.__v
- elif self.__s > other.__s:
- _s = self.__v
- _o = other.__v * (self.__sf / other.__sf)
- else:
- _s = self.__v
- _o = other.__v
-
- mp = max(self.__p - self.__s, other.__p - other.__s)
- ms = max(self.__s, other.__s)
- v = _s + _o
- # Check to see if the addition caused an increase in the precision
- # due to a carry. If so, compensate for it.
- if (v / (10L ** (mp + ms))) > 0:
- mp = mp + 1
-
- return PgNumeric((_s + _o), (mp + ms), ms)
-
- def __radd__(self, other):
- return self.__add__(other)
-
- def __iadd__(self, other):
- _r = self.__add__(other)
- if _r is None:
- return None
- self.__v = _r.__v
- self.__p = _r.__p
- self.__s = _r.__s
- self.__sf = _r.__sf
- return self
-
- def __sub__(self, other):
- _c = self.__coerce__(other)
- if _c is None:
- return None
- self, other = _c
- if self.__s < other.__s:
- _s = self.__v * (other.__sf / self.__sf)
- _o = other.__v
- elif self.__s > other.__s:
- _s = self.__v
- _o = other.__v * (self.__sf / other.__sf)
- else:
- _s = self.__v
- _o = other.__v
-
- mp = max(self.__p - self.__s, other.__p - other.__s)
- ms = max(self.__s, other.__s)
- return PgNumeric((_s - _o), (mp + ms), ms)
-
- def __rsub__(self, other):
- return other.__sub__(self)
-
- def __isub__(self, other):
- _r = self.__sub__(other)
- if _r is None:
- return None
- self.__v = _r.__v
- self.__p = _r.__p
- self.__s = _r.__s
- self.__sf = _r.__sf
- return self
-
- def __mul__(self, other):
- _c = self.__coerce__(other)
- if _c is None:
- return None
- self, other = _c
- _p = self.__v * other.__v
- return PgNumeric(_p, self.__p + other.__p, self.__s + other.__s)
-
- def __rmul__(self, other):
- return self.__mul__(self, other)
-
- def __imul__(self, other):
- _r = self.__mul__(other)
- if _r is None:
- return None
- self.__v = _r.__v
- self.__p = _r.__p
- self.__s = _r.__s
- self.__sf = _r.__sf
- return self
-
- def __div__(self, other):
- _c = self.__coerce__(other)
- if _c is None:
- return None
- self, other = _c
- _n = self.__v * other.__sf * self.__sf
- _d = other.__v
- _q = self._round((_n / _d), self.__s)
- return PgNumeric(_q, self.__p, self.__s)
-
- def __rdiv__(self, other):
- return other.__div__(self)
-
- def __idiv__(self, other):
- _r = self.__div__(other)
- if _r is None:
- return None
- self.__v = _r.__v
- self.__p = _r.__p
- self.__s = _r.__s
- self.__sf = _r.__sf
- return self
-
- def __cmp__(self, other):
- if other is None:
- return 1
- _c = self.__coerce__(other)
- if _c is None:
- return None
- self, other = _c
- if self.__s < other.__s:
- _s = self.__v * (other.__sf / self.__sf)
- _o = other.__v
- elif self.__s > other.__s:
- _s = self.__v
- _o = other.__v * (self.__sf / other.__sf)
- else:
- _s = self.__v
- _o = other.__v
- return cmp(_s, _o)
-
- def __neg__(self):
- return PgNumeric(-self.__v, self.__p, self.__s)
-
- def __nonzero__(self):
- return self.__v not in (None, 0)
-
- def __pos__(self):
- return PgNumeric(self.__v, self.__p, self.__s)
-
- def __abs__(self):
- if self.__v >= 0:
- return PgNumeric(self.__v, self.__p, self.__s)
- else:
- return PgNumeric(-self.__v, self.__p, self.__s)
-
- def _quote(self, forArray=0):
- if self.__v:
- if forArray:
- return '"%s"' % self.__fmtNumeric()
- else:
- return "'%s'" % self.__fmtNumeric()
- return 'NULL'
-
- def __int__(self):
- return int(self.__v / self.__sf)
-
- def __long__(self):
- return self.__v / self.__sf
-
- def __float__(self):
- v = self.__v
- s = self.__sf
- return (float(v / s) + (float(v % s) / float(s)))
-
- def __complex__(self):
- return complex(self.__float__())
-
- def __hash__(self):
- if self.__s == 0:
- return hash(self.__v)
- v = self.__v / self.__sf
- if (v * self.__sf) == self.__v:
- return hash(v)
- return hash(float(self))
-
-#-----------------------------------------------------------------------+
-# Name: PgMoney |
-# |
-# Description: A Python wrapper class for the PostgreSQL Money type. |
-# It's primary purpose it to check for overflow during |
-# calulations and to provide formatted output. |
-# |
-# Note: The PgMoney class uses a Python Floating point number |
-# represent a PostgreSQL money type. |
-#-----------------------------------------------------------------------+
-
-class PgMoney:
- def __init__(self, value):
- if value is None:
- self.value = value
- return
-
- if type(value) is StringType:
- if value[0] == '(':
- self.value = PgMoney(-float(replace(value[2:-1], ',', '')))
- elif value[0] == '-':
- self.value = PgMoney(-float(replace(value[2:], ',', '')))
- else:
- self.value = PgMoney(float(replace(value[1:], ',', '')))
- else:
- self.value = float(value)
-
- if self.value < -21474836.48 or self.value > 21474836.47:
- raise OverflowError, 'money initialization'
-
-
- def __checkresult(self, value, op):
- if value < -21474836.48 or value > 21474836.47:
- raise OverflowError, 'money %s' % op
- return PgMoney(value)
-
- def __coerce__(self, other):
- if other is None:
- return None
- res = coerce(self.value, other)
- if res is None:
- return None
- _s, _o = res
- return (self, _o)
-
- def __hash__(self):
- return hash(self.value)
-
- def __cmp__(self, other):
- return cmp(self.value, other)
-
- def __add__(self, other):
- return self.__checkresult(self.value + other, "addition")
-
- def __sub__(self, other):
- return self.__checkresult(self.value - other, "subtraction")
-
- def __mul__(self, other):
- return self.__checkresult(self.value * other, "mulitplication")
-
- def __div__(self, other):
- return self.__checkresult(self.value / other, "division")
-
- def __divmod__(self, other):
- _a, _b = divmod(self.value, other)
- return (self.__checkresult(_a, "divmod"), _b)
-
- def __pow__(self, other, modulus=None):
- return self.__checkresult(pow(self.value, other, modulus), "pow")
-
- def __radd__(self, other):
- return self.__checkresult(other + self.value, "addition")
-
- def __rsub__(self, other):
- return self.__checkresult(other - self.value, "subtraction")
-
- def __rmul__(self, other):
- return self.__checkresult(other * self.value, "multiplication")
-
- def __rdiv__(self, other):
- return self.__checkresult(other / self.value, "division")
-
- def __rdivmod__(self, other):
- _a, _b = divmod(other, self.value)
- return (self.__checkresult(_a, "divmod"), _b)
-
- def __rpow__(self, other, modulus=None):
- return self.__checkresult(pow(other, self.value, modulus), "pow")
-
- def __neg__(self):
- return self.__checkresult(self.value * -1, "negation")
-
- def __pos__(self):
- return self.value
-
- def __abs__(self):
- return self.__checkresult(abs(self.value), "abs")
-
- def __complex__(self):
- return complex(self.value)
-
- def __int__(self):
- return int(self.value)
-
- def __long__(self):
- return long(self.value)
-
- def __float__(self):
- return self.value # PgMoney is already a float :-)
-
- def __repr__(self):
- return '%.2f' % self.value
-
- def __str__(self):
- _s = '%.2f' % abs(self.value)
- _i = string.rfind(_s, '.')
- _c = (_i - 1) / 3
- for _j in range(_c):
- _i = _i - 3
- _s = '%s,%s' % (_s[:_i], _s[_i:])
- if self.value < 0.0:
- return '($%s)' % _s
- else:
- return '$%s' % _s
-
- def _quote(self, forArray=0):
- if self.value:
- if forArray:
- return '"%s"' % str(self.value)
- else:
- return "'%s'" % str(self.value)
- return 'NULL'
-
-#-----------------------------------------------------------------------+
-# Name: PgInt8 |
-# |
-# Description: A Python wrapper class for the PostgreSQL int8 type. |
-# It's primary purpose it to check for overflow during |
-# calulations. |
-# |
-# Note: The PgInt8 class uses a Python Long Integer to hold |
-# the PostgreSQL int8 type. |
-# |
-# Note: This class will only be defined if the C implementation |
-# of the PgInt8 object was not imported with/from libpq. |
-#-----------------------------------------------------------------------+
-
-if dir().count('PgInt8') == 0: # Only define this class is PgInt8 wasn't
- # brought in via libpq.
- class PgInt8:
- def __init__(self, value):
- if value is None:
- self.value = value
- return
-
- self.value = long(value)
- if self.value < -9223372036854775808L or \
- self.value > 9223372936854775807L:
- raise OverflowError, 'int8 initialization'
-
- def __checkresult(self, value, op):
- if value < -9223372036854775808L or value > 9223372936854775807L:
- raise OverflowError, 'int8 %s' % op
- return PgInt8(value)
-
- def __coerce__(self, other):
- if other is None:
- return None
- res = coerce(self.value, other)
- if res is None:
- return None
- _s, _o = res
- return (self, _o)
-
- def __hash__(self):
- return hash(self.value)
-
- def __cmp__(self, other):
- return cmp(self.value, other)
-
- def __nonzero__(self):
- return self.value != 0
-
- def __add__(self, other):
- return self.__checkresult(self.value + other, "addition")
-
- def __sub__(self, other):
- return self.__checkresult(self.value - other, "subtraction")
-
- def __mul__(self, other):
- return self.__checkresult(self.value * other, "mulitplication")
-
- def __div__(self, other):
- return self.__checkresult(self.value / other, "division")
-
- def __divmod__(self, other):
- _a, _b = divmod(self.value, other)
- return (self.__checkresult(_a, "divmod"), _b)
-
- def __pow__(self, other, modulus=None):
- return self.__checkresult(pow(self.value, other, modulus), "pow")
-
- def __lshift__(self, other):
- return self.__checkresult(self.value << other, 'lshift')
-
- def __rshift__(self, other):
- return self.__checkresult(self.value >> other, 'rshift')
-
- def __and__(self, other):
- return self.__checkresult(self.value & other, 'and')
-
- def __xor__(self, other):
- return self.__checkresult(self.value ^ other, 'xor')
-
- def __or__(self, other):
- return self.__checkresult(self.value | other, 'or')
-
- def __radd__(self, other):
- return self.__checkresult(other + self.value, "addition")
-
- def __rsub__(self, other):
- return self.__checkresult(other - self.value, "subtraction")
-
- def __rmul__(self, other):
- return self.__checkresult(other * self.value, "mulitplication")
-
- def __rdiv__(self, other):
- return self.__checkresult(other / self.value, "division")
-
- def __rdivmod__(self, other):
- _a, _b = divmod(other, self.value)
- return (self.__checkresult(_a, "divmod"),
- self.__checkresult(_b, "divmod"))
-
- def __rpow__(self, other, modulus=None):
- return self.__checkresult(pow(other, self.value, modulus), "pow")
-
- def __rlshift__(self, other):
- return self.__checkresult(other << self.value, 'lshift')
-
- def __rrshift__(self, other):
- return self.__checkresult(other >> self.value, 'rshift')
-
- def __rand__(self, other):
- return self.__checkresult(other & self.value, 'and')
-
- def __rxor__(self, other):
- return self.__checkresult(other ^ self.value, 'xor')
-
- def __ror__(self, other):
- return self.__checkresult(other | self.value, 'or')
-
- def __neg__(self):
- return self.__checkresult(neg(self.value), 'neg')
-
- def __pos__(self):
- return self.__checkresult(pos(self.value), 'pos')
-
- def __abs__(self):
- return self.__checkresult(abs(self.value), 'abs')
-
- def __complex__(self):
- return complex(self)
-
- def __int__(self):
- return int(self.value)
-
- def __long__(self):
- return self.value # PgInt8 is already a Long.
-
- def __float__(self):
- return float(self.value)
-
- def __complex__(self):
- return complex(self.value)
-
- def __hex__(self):
- return hex(self.value)
-
- def __oct__(self):
- return oct(self.value)
-
- def __repr__(self):
- return repr(self.value)
-
- def __str__(self):
- return str(self.value)
-
- def _quote(self, forArray=0):
- if self.value is not None:
- s = str(self.value)
- if s[-1:] == "L":
- s = s[:-1]
- return "%s" % s
- return 'NULL'
-
- PgInt8Type = PgInt8
-
-
-#-----------------------------------------------------------------------+
-# Name: PgResultSet |
-# |
-# Description: This class defines the DB-API query result set for a |
-# single row. It emulates a sequence with the added |
-# feature of being able to reference an attribute by |
-# column name in addition to a zero-based numeric index. |
-# |
-# This class isn't used directly, instead it's used as a |
-# base class for the actual result set class created with |
-# make_PgResultSetClass. |
-# |
-#-----------------------------------------------------------------------+
-
-class PgResultSet:
-
- # It may not be obvious what self.__class__ does:
- # Apart from the __init__ method, all methods are called on instances of a
- # class dynamically created make_PgResultSetClass, which means that when
- # you call a method, self.__class__ is *not* PgResultSet, but a subclass of
- # it created with make_PgResultSetClass (using the new module). The
- # subclass will have a class attribute called _xlatkey, which is a mapping
- # of column names to column positions.
-
- def __init__(self, value):
- self.__dict__['baseObj'] = value
-
- def __getattr__(self, key):
- # When retrieving column data by name as an attribute, we must be
- # aware that a column name can be defiend with mixed-case within the
- # database. Because of this we must first check for an exact match
- # with the given key. If that fails, then we match with the key that
- # has been changed to lower case. Note: we are relying on the fact
- # that PostgreSQL sends column names that are not defined with mixed-
- # case to the client as lower-case names.
- keyl = key.lower()
- if self._xlatkey.has_key(key):
- return self.baseObj[self._xlatkey[key]]
- if self._xlatkey.has_key(keyl):
- return self.baseObj[self._xlatkey[keyl]]
- raise AttributeError, key
-
- # We define a __setattr__ routine that will only allow the attributes that
- # are the column names to be updated. All other attributes are read-only.
- def __setattr__(self, key, value):
- if key in ('baseObj', '_xlatkey', '_desc_'):
- raise AttributeError, "%s is read-only." % key
-
- # Try an exact match first, then the case-insensitive match.
- # See comment in __getattr__ for details.
- keyl = key.lower()
- if self._xlatkey.has_key(key):
- self.__dict__['baseObj'][self._xlatkey[key]] = value
- elif self._xlatkey.has_key(keyl):
- self.__dict__['baseObj'][self._xlatkey[keyl]] = value
- else:
- raise AttributeError, key
-
- def __len__(self):
- return len(self.baseObj)
-
- def __getitem__(self, key):
- if isinstance(key, StringType):
- # Try an exact match first, then the case-insensitive match.
- # See comment in __getattr__ for details.
- try:
- key = self._xlatkey[key]
- except:
- key = self._xlatkey[key.lower()]
- return self.baseObj[key]
-
- def __setitem__(self, key, value):
- if isinstance(key, StringType):
- # Try an exact match first, then the case-insensitive match.
- # See comment in __getattr__ for details.
- try:
- key = self._xlatkey[key]
- except:
- key = self._xlatkey[key.lower()]
- self.baseObj[key] = value
-
- def __contains__(self, key):
- return self.has_key(key)
-
- def __getslice__(self, i, j):
- klass = make_PgResultSetClass(self._desc_[i:j])
- obj = klass(self.baseObj[i:j])
- return obj
-
- def __repr__(self):
- return repr(self.baseObj)
-
- def __str__(self):
- return str(self.baseObj)
-
- def __cmp__(self, other):
- return cmp(self.baseObj, other)
-
- def description(self):
- return self._desc_
-
- def keys(self):
- _k = []
- for _i in self._desc_:
- _k.append(_i[0])
- return _k
-
- def values(self):
- return self.baseObj[:]
-
- def items(self):
- _items = []
- for i in range(len(self.baseObj)):
- _items.append((self._desc_[i][0], self.baseObj[i]))
- return _items
-
- def has_key(self, key):
- # Try an exact match first, then the case-insensitive match.
- # See comment in __getattr__ for details.
- if not self._xlatkey.has_key(key):
- key = key.lower()
- return self._xlatkey.has_key(key)
-
- def get(self, key, defaultval=None):
- try:
- if isinstance(key, StringType):
- # Try an exact match first, then the case-insensitive match.
- # See comment in __getattr__ for details.
- try:
- key = self._xlatkey[key]
- except:
- key = self._xlatkey[key.lower()]
- return self[key]
- except:
- return defaultval
-
-def make_PgResultSetClass(description, mapname=None):
- """Dynamically create a new subclass of PgResultSet."""
- klass = new.classobj("PgResultSetConcreteClass", (PgResultSet,), {})
- klass.__dict__['_desc_'] = description
-
- klass.__dict__['_xlatkey'] = {}
- if mapname is None:
- for _i in range(len(description)):
- klass.__dict__['_xlatkey'][description[_i][0]] = _i
- else:
- for k, v in mapname.items():
- klass.__dict__['_xlatkey'][k] = v
- return klass
-
-
-#-----------------------------------------------------------------------+
-# Define the PgSQL function calls: |
-# |
-# connect() -- connect to a PostgreSQL database. |
-# _handleArray() -- Transform a PgArray class into a string rep- |
-# resenting a PostgreSQL array. |
-# _quote() -- Transform a Python object representing a |
-# PostgreSQL type into a appropiately quoted |
-# string that can be sent to the database in a |
-# UPDATE/INSERT statement. _quote() calls the |
-# _handleArray() function to quote arrays. |
-# _quoteall() -- transforms all elements of a list or diction- |
-# ary using _quote. |
-# dateTimeDelta2Interval() -- converts a DateTimeDelta type into |
-# a string PostgreSQL accepts as a interval. |
-#-----------------------------------------------------------------------+
-
-def connect(dsn=None, user=None, password=None, host=None, database=None,
- port=None, options=None, tty=None, client_encoding=None,
- unicode_results=None):
- """
-connection = PgSQL.connect(dsn[, user, password, host, database, port,
- options, tty] [, client_encoding]
- [, unicode_results])
- Opens a connection to a PostgreSQL database."""
-
- _d = {}
-
- # Try getting values from the DSN first.
- if dsn is not None:
- try:
- params = string.split(dsn, ":")
- if params[0] != '': _d["host"] = params[0]
- if params[1] != '': _d["port"] = params[1]
- if params[2] != '': _d["dbname"] = params[2]
- if params[3] != '': _d["user"] = params[3]
- if params[4] != '': _d["password"] = params[4]
- if params[5] != '': _d["options"] = params[5]
- if params[6] != '': _d["tty"] = params[6]
- except:
- pass
-
- # Override from the keyword arguments, if needed.
- if (user is not None): _d["user"] = user
- if (password is not None): _d["password"] = password
- if (host is not None):
- _d["host"] = host
- try:
- params = string.split(host, ":")
- _d["host"] = params[0]
- _d["port"] = params[1]
- except:
- pass
- if (database is not None): _d["dbname"] = database
- if (port is not None): _d["port"] = port
- if (options is not None): _d["options"] = options
- if (tty is not None): _d["tty"] = tty
-
- # Build up the connection info string passed to PQconnectdb
- # via the constructor to Connection.
-
- connInfo = ""
- for i in _d.keys():
- connInfo = "%s%s=%s " % (connInfo, i, _d[i])
-
- return Connection(connInfo, client_encoding, unicode_results)
-
-def _handleArray(value):
- """
-_handleArray(list) -> string
- This function handle the transformation of a Python list into a string that
- can be used to update a PostgreSQL array attribute."""
-
- #Check for, and handle an empty list.
- if len(value) == 0:
- return '{}'
-
- _j = "'{"
- for _i in value:
- if _i is None:
- _j += ","
- elif isinstance(_i, PgArray):
- _j = _j + _handleArray(_i)[1:-1] + ','
- elif hasattr(_i, '_quote'):
- _j = '%s%s,' % (_j, _i._quote(1))
- elif type(_i) is DateTimeType:
- _j = '%s"%s",' % (_j, _i)
- elif type(_i) is DateTime.DateTimeDeltaType:
- _j = '%s"%s",' % (_j, dateTimeDelta2Interval(_i))
- elif type(_i) is PgInt2Type or isinstance(_i, PgInt8Type):
- _j = '%s%s,' % (_j, str(_i))
- else:
- _j = '%s%s,' % (_j, PgQuoteString(str(_i), 1))
-
- return _j[:-1] + "}'"
-
-def _quote(value):
- """
-_quote(value) -> string
- This function transforms the Python value into a string suitable to send
- to the PostgreSQL database in a insert or update statement. This function
- is automatically applied to all parameter sent vis an execute() call.
- Because of this an update/insert statement string in an execute() call
- should only use '%s' [or '%(name)s'] for variable subsitution without any
- quoting."""
-
- if value is None:
- return 'NULL'
- elif hasattr(value, '_quote'):
- return value._quote()
- elif type(value) is DateTimeType:
- return "'%s'" % value
- elif type(value) is DateTimeDeltaType:
- return "'%s'" % dateTimeDelta2Interval(value)
- elif isinstance(value, StringType):
- return PgQuoteString(value)
- elif isinstance(value, LongType):
- return str(value)
- else:
- return repr(value)
-
-def _quoteall(vdict):
- """
-_quoteall(vdict)->dict
- Quotes all elements in a list or dictionary to make them suitable for
- insertion."""
-
- if type(vdict) is DictType or isinstance(vdict, PgResultSet):
- t = {}
- for k, v in vdict.items():
- t[k]=_quote(v)
- elif type(vdict) in (StringType, UnicodeType):
- # Note: a string is a SequenceType, but is treated as a single
- # entity, not a sequence of characters.
- t = (_quote(vdict), )
- elif type(vdict) in [ListType, TupleType]:
- t = tuple(map(_quote, vdict))
- else:
- raise TypeError, \
- "argument to _quoteall must be a sequence or dictionary!"
-
- return t
-
-def dateTimeDelta2Interval(interval):
- """
-DateTimeDelta2Interval - Converts a DateTimeDelta to an interval string\n
- The input format is [+-]DD:HH:MM:SS.ss\n
- The output format is DD days HH:MM:SS.ss [ago]\n
- """
-
- if type(interval) is DateTimeDeltaType:
- s = str(interval)
- ago = ''
- if s[0] == '-':
- ago = ' ago'
- s = s[1:]
- else:
- ago = ''
- s = s.split(':')
- if len(s) < 4:
- return '%s:%s:%s %s' % (s[0], s[1], s[2], ago)
-
- return '%s days %s:%s:%s %s' % (s[0], s[1], s[2], s[3], ago)
- else:
- raise TypeException, "DateTimeDelta2Interval requires a DataTimeDelta."
-
-#-----------------------------------------------------------------------+
-# Name: Connection |
-# |
-# Description: Connection defines the Python DB-API 2.0 connection |
-# object. See the DB-API 2.0 specifiaction for details. |
-#-----------------------------------------------------------------------+
-
-class Connection:
- """Python DB-API 2.0 Connection Object."""
-
- def __init__(self, connInfo, client_encoding=None, unicode_results=None):
- try:
- self.__dict__["conn"] = PQconnectdb(connInfo)
- except Exception, m:
- # The connection to the datadata failed.
- # Clean up the Connection object that was created.
- # Note: _isOpen must be defined for __del__ to work.
- self.__dict__["_isOpen"] = None
- del(self)
- raise DatabaseError, m
-
- self.__dict__["autocommit"] = 0
- self.__dict__["TransactionLevel"] = ""
- self.__dict__["notices"] = self.conn.notices
- self.__dict__["inTransaction"] = 0
- self.__dict__["version"] = self.conn.version
- self.__dict__["_isOpen"] = 1
- self.__dict__["_cache"] = TypeCache(self)
- if noWeakRef:
- self.__dict__["cursors"] = []
- else:
- self.__dict__["cursors"] = weakref.WeakValueDictionary()
-
- self.unicode_results = unicode_results
- if type(client_encoding) in (TupleType, ListType):
- self.client_encoding = client_encoding
- else:
- self.client_encoding = (client_encoding or getdefaultencoding(),)
-
- def __del__(self):
- if self._isOpen:
- self.close() # Ensure that the connection is closed.
-
- def __setattr__(self, name, value):
- if name == "autocommit":
- if value is None:
- raise InterfaceError, \
- "Can't delete the autocommit attribute."
- # Don't allow autocommit to change if there are any opened cursor
- # associated with this connection.
- if self.__anyLeft():
- if noWeakRef:
- # If the are cursors left, but weak references are not
- # available, garbage collect any cursors that are only
- # referenced in self.cursors.
-
- self.__gcCursors()
-
- if len(self.cursors) > 0:
- raise AttributeError, \
- "Can't change autocommit when a cursor is active."
- else:
- raise AttributeError, \
- "Can't change autocommit when a cursor is active."
-
- # It's possible that the connection can still have an open
- # transaction, even though there are no active cursors.
-
- if self.inTransaction:
- self.rollback()
-
- if value:
- self.__dict__[name] = 1
- else:
- self.__dict__[name] = 0
- elif name == "TransactionLevel":
- if value is None:
- raise InterfaceError, \
- "Can't delete the TransactinLevel attribute."
- # Don't allow TransactionLevel to change if there are any opened
- # cursors associated with this connection.
- if self.__anyLeft():
- if noWeakRef:
- # If the are cursors left, but weak references are not
- # available, garbage collect any cursors that are only
- # referenced in self.cursors.
-
- self.__gcCursors()
-
- if len(self.cursors) > 0:
- raise AttributeError, \
- "Can't change TransactionLevel when a cursor is active."
- else:
- raise AttributeError, \
- "Can't change TransactionLevel when a cursor is active."
-
- # It's possible that the connection can still have an open
- # transaction, even though there are no active cursors.
-
- if self.inTransaction:
- self.rollback()
-
- if type(value) is not StringType:
- raise ValueError, "TransactionLevel must be a string."
-
- if value.upper() in [ "", "READ COMMITTED", "SERIALIZABLE" ]:
- self.__dict__[name] = value.upper()
- else:
- raise ValueError, \
- 'TransactionLevel must be: "", "READ COMMITTED", or "SERIALIZABLE"'
- elif name in ('unicode_results', 'client_encoding'):
- self.__dict__[name] = value
- elif self.__dict__.has_key(name):
- raise AttributeError, "%s is read-only." % name
- else:
- raise AttributeError, name
-
- def __closeCursors(self, flag=0):
- """
- __closeCursors() - closes all cursors associated with this connection"""
- if self.__anyLeft():
- if noWeakRef:
- curs = self.cursors[:]
- else:
- curs = map(lambda x: x(), self.cursors.data.values())
-
- for i in curs:
- if flag:
- i.close()
- else:
- i._Cursor__reset()
-
- return self.inTransaction
-
- def __anyLeft(self):
- if noWeakRef:
- return len(self.cursors) > 0
-
- return len(self.cursors.data.keys()) > 0
-
- def __gcCursors(self):
- # This routine, which will be called only if weak references are not
- # available, will check the reference counts of the cursors in the
- # connection.cursors list and close any that are only referenced
- # from that list. This will clean up deleted cursors.
-
- for i in self.cursors[:]:
- # Check the reference count. It will be 4 if it only exists in
- # self.cursors. The magic number for is derived from the fact
- # that there will be 1 reference count for each of the follwoing:
- # self.cursors, self.cursors[:], i, and as the argument to
- # getrefcount(),
-
- if getrefcount(i) < 5:
- i.close()
-
- def __setupTransaction(self):
- """
- __setupTransaction()
- Internal routine that will set up a transaction for this connection.\n"""
- self.conn.query("BEGIN WORK")
- if self.TransactionLevel != "":
- _nl = len(self.notices)
- self.conn.query('SET TRANSACTION ISOLATION LEVEL %s' %
- self.TransactionLevel)
- if len(self.notices) != _nl:
- raise Warning, self.notices.pop()
- self.__dict__["inTransaction"] = 1
-
-
- def close(self):
- """
- close()
- Close the connection now (rather than whenever __del__ is called).
- Any active cursors for this connection will be closed and the connection
- will be unusable from this point forward.\n"""
-
- if not self._isOpen:
- raise InterfaceError, "Connection is already closed."
-
- if self.__closeCursors(1):
- try:
- _nl = len(self.conn.notices)
- self.conn.query("ROLLBACK WORK")
- if len(self.notices) != _nl:
- raise Warning, self.notices.pop()
- except:
- pass
-
- self.__dict__["_cache"] = None
- self.__dict__["_isOpen"] = 0
- self.__dict__["autocommit"] = None
- self.__dict__["conn"] = None
- self.__dict__["cursors"] = None
- self.__dict__["inTransaction"] = 0
- self.__dict__["TransactionLevel"] = None
- self.__dict__["version"] = None
- self.__dict__["notices"] = None
-
- def commit(self):
- """
- commit()
- Commit any pending transactions to the database.\n"""
-
- if not self._isOpen:
- raise InterfaceError, "Commit failed - Connection is not open."
-
- if self.autocommit:
- raise InterfaceError, "Commit failed - autocommit is on."
-
- if self.__closeCursors():
- self.__dict__["inTransaction"] = 0
- _nl = len(self.conn.notices)
- res = self.conn.query("COMMIT WORK")
- if len(self.notices) != _nl:
- raise Warning, self.notices.pop()
- if res.resultStatus != COMMAND_OK:
- raise InternalError, "Commit failed - reason unknown."
-
- def rollback(self):
- """
- rollback()
- Rollback to the start of any pending transactions.\n"""
-
- if not self._isOpen:
- raise InterfaceError, "Rollback failed - Connection is not open."
-
- if self.autocommit:
- raise InterfaceError, "Rollback failed - autocommit is on."
-
- if self.__closeCursors():
- self.__dict__["inTransaction"] = 0
- _nl = len(self.conn.notices)
- res = self.conn.query("ROLLBACK WORK")
- if len(self.notices) != _nl:
- raise Warning, self.notices.pop()
- if res.resultStatus != COMMAND_OK:
- raise InternalError, \
- "Rollback failed - %s" % res.resultErrorMessage
-
- def cursor(self, name=None, isRefCursor=PG_False):
- """
- cursor([name])
- Returns a new 'Cursor Object' (optionally named 'name')."""
-
- if not self._isOpen:
- raise InterfaceError, \
- "Create cursor failed - Connection is not open."
-
- return Cursor(self, name, isRefCursor)
-
- def binary(self, string=None):
- """
- binary([string])
- Returns a new 'Large Object'. If sting is present, it is used to
- initialize the large object."""
-
- if not self._isOpen:
- raise InterfaceError, \
- "Creation of large object failed - Connection is not open."
-
- if self.autocommit:
- raise InterfaceError, \
- "Creation of large object failed - autocommit is on."
-
- _nl = len(self.notices)
- _ct = 0
- # Ensure that we are in a transaction for working with large objects
- if not self.inTransaction:
- self.__setupTransaction()
- _ct = 1
-
- _lo = self.conn.lo_creat(INV_READ | INV_WRITE)
-
- if len(self.notices) != _nl:
- raise Warning, self.notices.pop()
-
- if string:
- _lo.open("w")
- _lo.write(string)
- _lo.close()
-
- if len(self.notices) != _nl:
- if self.inTransaction:
- self.conn.query("ROLLBACK WORK")
- raise Warning, self.conn.notices.pop()
-
- return _lo
-
- def unlink(self, lobj):
- """
- unlink(OID|PgLargeObject)
- Remove a large object from the database inversion file syste."""
-
- if not self._isOpen:
- raise InterfaceError, \
- "Unlink of large object failed - Connection is not open."
-
- if not self.version.post70 and self.inTransaction:
- raise NotSupportedError, \
- "unlink of a PostgreSQL Large Object in a transaction"
-
- if type(lobj) is IntType:
- oid = lobj
- elif type(lobj) is PgLargeObjectType:
- oid = lobj.oid
-
- _nl = len(self.conn.notices)
- res = self.conn.lo_unlink(oid)
- if len(self.notices) != _nl:
- raise Warning, self.notices.pop()
-
- return res
-
-#-----------------------------------------------------------------------+
-# Name: Cursor |
-# |
-# Description: Cursor defines the Python DB-API 2.0 cursor object. |
-# See the DB-API 2.0 specification for details. |
-#-----------------------------------------------------------------------+
-
-class Cursor:
- """Python DB-API 2.0 Cursor Object."""
-
- def __init__(self, conn, name, isRefCursor=PG_False):
- if not isinstance(conn, Connection):
- raise TypeError, "Cursor requires a connection."
-
- # Generate a unique name for the cursor is one is not given.
- if name is None:
- if isRefCursor:
- raise TypeError, "Reference cursor requires a name."
- name = "PgSQL_%08X" % id(self)
- elif type(name) is not StringType:
- raise TypeError, "Cursor name must be a string."
-
- # Define the public variables for this cursor.
- self.__dict__["arraysize"] = 1
-
- # Define the private variables for this cursor.
- if noWeakRef:
- self.__dict__["conn"] = conn
- else:
- self.__dict__["conn"] = weakref.proxy(conn)
- self.__dict__["name"] = name
-
- # This needs to be defined here sot that the initial call to __reset()
- # will work.
- self.__dict__["closed"] = None
- self.__reset()
-
- # _varhdrsz is the length (in bytes) of the header for variable
- # sized postgreSQL data types.
-
- self.__dict__["_varhdrsz"] = 4
-
- # Add ourselves to the list of cursors for our owning connection.
- if noWeakRef:
- self.conn.cursors.append(self)
- if len(self.conn.cursors) > 1:
- # We have additional cursors, garbage collect them.
- self.conn._Connection__gcCursors()
- else:
- self.conn.cursors[id(self)] = self
-
- if not self.conn.autocommit:
- # Only the first created cursor begins the transaction.
- if not self.conn.inTransaction:
- self.conn._Connection__setupTransaction()
- self.__dict__["PgResultSetClass"] = None
-
- if isRefCursor:
- # Ok -- we've created a cursor, we will pre-fetch the first row in
- # order to make the description array. Note: the first call to
- # fetchXXX will return the pre-fetched record.
- self.__dict__["closed"] = 0
- self.res = self.conn.conn.query('FETCH 1 FROM "%s"' % self.name)
- self._rows_ = self.res.ntuples
- self._idx_ = 0
- self.__makedesc__()
-
- def __del__(self):
- # Ensure that the cursor is closed when it is deleted. This takes
- # care of some actions that needs to be completed when a cursor is
- # deleted, such as disassociating the cursor from the connection
- # and closing an open transaction if this is the last cursor for
- # the connection.
- if not self.closed:
- self.close()
-
- def __reset(self):
- try:
- if (self.closed == 0) and self.conn.inTransaction:
- try:
- self.conn.conn.query('CLOSE "%s"' % self.name)
- except:
- pass
- except:
- pass
-
- self.__dict__["res"] = None
- # closed is a trinary variable:
- # == None => Cursor has not been opened.
- # == 0 => Cursor is open.
- # == 1 => Curosr is closed.
- self.__dict__["closed"] = None
- self.__dict__["description"] = None
- self.__dict__["oidValue"] = None
- self.__dict__["_mapname"] = None
- self.__dict__["_rows_"] = 0
- self.__dict__["_idx_"] = 1
- self.__dict__["rowcount"] = -1
-
- def __setattr__(self, name, value):
- if self.closed:
- raise InterfaceError, "Operation failed - the cursor is closed."
-
- if name in ["rowcount", "oidValue", "description"]:
- raise AttributeError, "%s is read-only." % name
- elif self.__dict__.has_key(name):
- self.__dict__[name] = value
- else:
- raise AttributeError, name
-
- def __unicodeConvert(self, obj):
- if type(obj) is StringType:
- return obj
- elif type(obj) is UnicodeType:
- return obj.encode(*self.conn.client_encoding)
- elif type(obj) in (ListType, TupleType):
- converted_obj = []
- for item in obj:
- if type(item) is UnicodeType:
- converted_obj.append(item.encode(*self.conn.client_encoding))
- else:
- converted_obj.append(item)
- return converted_obj
- elif type(obj) is DictType:
- converted_obj = {}
- for k, v in obj.items():
- if type(v) is UnicodeType:
- converted_obj[k] = v.encode(*self.conn.client_encoding)
- else:
- converted_obj[k] = v
- return converted_obj
- elif isinstance(obj, PgResultSet):
- obj = copy.copy(obj)
- for k, v in obj.items():
- if type(v) is UnicodeType:
- obj[k] = v.encode(*self.conn.client_encoding)
- return obj
- else:
- return obj
-
- def __fetchOneRow(self):
- if self._idx_ >= self._rows_:
- self.__dict__['rowcount'] = 0
- return None
-
- _j = []
- _r = self.res
- _c = self.conn._cache
- for _i in range(self.res.nfields):
- _j.append(_c.typecast(self.description[_i],
- _r.getvalue(self._idx_, _i)))
-
- self._idx_ = self._idx_ + 1
-
- self.__dict__['rowcount'] = 1
-
- if fetchReturnsList:
- # Return a list (This is the minimum required by DB-API 2.0
- # compliance).
- return _j
- else:
- return self.PgResultSetClass(_j)
-
- def __fetchManyRows(self, count, iList=[]):
- _many = iList
- if count < 0:
- while 1:
- _j = self.__fetchOneRow()
- if _j is not None:
- _many.append(_j)
- else:
- break
- elif count > 0:
- for _i in range(count):
- _j = self.__fetchOneRow()
- if _j is not None:
- _many.append(_j)
- else:
- break
-
- self.__dict__['rowcount'] = len(_many)
-
- return _many
-
- def __makedesc__(self):
- # Since __makedesc__ will only be called after a successful query or
- # fetch, self.res will contain the information needed to build the
- # description attribute even if no rows were returned. So, we always
- # build up the description.
- self.__dict__['description'] = []
- self._mapname = {}
- _res = self.res
- _cache = self.conn._cache
- for _i in range(_res.nfields):
- _j = []
-
- _j.append(_res.fname(_i))
-
- _typ = PgTypes(_res.ftype(_i))
- _mod = _res.fmod(_i)
- _tn, _pl, _ia, _bt = _cache.getTypeInfo(_typ)
- if _ia:
- _s, _pl, _s, _s = _cache.getTypeInfo(_bt)
- if _bt == PG_OID:
- _bt = PgTypes(PG_BLOB)
- _typ = _bt
- elif _typ.value == PG_OID:
- try:
- _p = _res.getvalue(0, _i)
- except (ValueError, TypeError), m:
- # We can only guess here ...
- _typ = PgTypes(PG_BLOB)
- else:
- if type(_p) in [PgLargeObjectType, NoneType]:
- _typ = PgTypes(PG_BLOB)
- else:
- _typ = PgTypes(PG_ROWID)
-
- _j.append(_typ)
-
- # Calculate Display size, Internal size, Precision and Scale.
- # Note: Precision and Scale only have meaning for PG_NUMERIC
- # columns.
- if _typ.value == PG_NUMERIC:
- if _mod == -1:
- # We have a numeric with no scale/precision.
- # Get them from by converting the string to a PgNumeric
- # and pulling them form the PgNumeric object. If that
- # fails default to a precision of 30 with a scale of 6.
- try:
- nv = PgNumeric(_res.getvalue(0, _i))
- _p = nv.getPrecision()
- _s = nv.getScale()
- except (ValueError, TypeError), m:
- _p = 30
- _s = 6
- else:
- # We hava a valid scale/precision value. Use them.
- _s = _mod - self._varhdrsz
- _p = (_s >> 16) & 0xffff
- _s = _s & 0xffff
- _j.append(None) # Display size (always None since PG7.3)
- _j.append(_p) # Internal (storage) size
- _j.append(_p) # Precision
- _j.append(_s) # Scale
- else:
- if _pl == -1:
- _pl = _res.fsize(_i)
- if _pl == -1:
- _pl = _mod - self._varhdrsz
- _j.append(None) # Display size (always None since PG7.3)
- _s = _res.fsize(_i)
- if _s == -1:
- _s = _mod
- _j.append(_s) # Internal (storage) size
- if _typ.value == PG_MONEY:
- _j.append(9) # Presicion and Scale (from
- _j.append(2) # the PostgreSQL doco.)
- else:
- _j.append(None) # Preision
- _j.append(None) # Scale
-
- _j.append(None) # nullOK is not implemented (yet)
- _j.append(_ia) # Array indicator (PostgreSQL specific)
-
- self.__dict__["description"].append(_j)
-
- # Add the fieldname:fieldindex to the _mapname dictionary
- self._mapname[_j[0]] = _i
-
- # Create a subclass of PgResultSet. Note that we pass a copy of the
- # description to this class.
- self.PgResultSetClass = make_PgResultSetClass(self.description[:], self._mapname)
-
- def callproc(self, proc, *args):
- if self.closed:
- raise InterfaceError, "callproc failed - the cursor is closed."
-
- if self.conn is None:
- raise Error, "connection is closed."
-
- if self.closed == 0:
- raise InterfaceError, "callproc() failed - cursor is active."
-
- if self.conn.autocommit:
- pass
- else:
- if not self.conn.inTransaction:
- self.conn._Connection__setupTransaction()
-
- proc = self.__unicodeConvert(proc)
- args = self.__unicodeConvert(args)
-
- _qstr = "select %s(" % proc
- for _i in range(len(args)):
- _qstr = '%s%s, ' % (_qstr, _quote(args[_i]))
- if len(args) == 0:
- _qstr = '%s)' % _qstr
- else:
- _qstr = '%s)' % _qstr[:-2]
-
- _nl = len(self.conn.notices)
-
- try:
- self.res = self.conn.conn.query(_qstr)
- self._rows_ = self.res.ntuples
- self._idx_ = 0
- if type(self.res) is not PgResultType:
- self.__dict__['rowcount'] = -1
- else:
- self.__dict__['oidValue'] = self.res.oidValue
- if self.res.resultType == RESULT_DQL:
- pass
- elif self.res.resultType == RESULT_DML:
- self.__dict__['rowcount'] = self.res.cmdTuples
- else:
- self.__dict__['rowcount'] = -1
- except OperationalError, msg:
- # Uh-oh. A fatal error occurred. This means the current trans-
- # action has been aborted. Try to recover to a sane state.
- if self.conn.inTransaction:
- self.conn.conn.query('END WORK')
- self.conn.__dict__["inTransaction"] = 0
- self.conn._Connection__closeCursors()
- raise OperationalError, msg
- except InternalError, msg:
- # An internal error occured. Try to get to a sane state.
- self.conn.__dict__["inTransaction"] = 0
- self.conn._Connection__closeCursors_()
- self.conn.close()
- raise InternalError, msg
-
- if len(self.conn.notices) != _nl:
- _drop = self.conn.notices[-1]
- if _drop.find('transaction is aborted') > 0:
- raise Warning, self.conn.notices.pop()
-
- self._rows_ = self.res.ntuples
- self._idx_ = 0
- self.__dict__['rowcount'] = -1 # New query - no fetch occured yet.
- self.__makedesc__()
-
- return None
-
- def close(self):
- if self.closed:
- raise InterfaceError, "The cursor is already closed."
-
- # Dis-associate ourselves from our cursor.
- self.__reset()
- try:
- _cc = self.conn.cursors
- if noWeakRef:
- _cc.remove(self)
- if (len(_cc) > 0):
- # We have additional cursors, garbage collect them.
- _cc._Connection__gcCursors()
- else:
- del _cc.data[id(self)]
- except:
- pass
- self.conn = None
- self.closed = 1
-
- def execute(self, query, *parms):
- if self.closed:
- raise InterfaceError, "execute failed - the cursor is closed."
-
- if self.conn is None:
- raise Error, "connection is closed."
-
- if self.closed == 0:
- if re_DQL.search(query):
- # A SELECT has already been executed with this cursor object,
- # which means a PostgreSQL portal (may) have been opened.
- # Trying to open another portal will cause an error to occur,
- # so we asusme that the developer is done with the previous
- # SELECT and reset the cursor object to it's inital state.
- self.__reset()
-
- _qstr = query
- if self.conn.autocommit:
- pass
- else:
- _badQuery = (self.conn.version < 70100) and \
- (re_DRT.search(query) or re_DRI.search(query))
- if not self.conn.inTransaction:
- if _badQuery:
- pass # PostgreSQL version < 7.1 and not in transaction,
- # so DROP TABLE/INDEX is ok.
- else:
- self.conn._Connection__setupTransaction()
-
- if re_DQL.search(query) and \
- not (noPostgresCursor or re_4UP.search(query)):
- _qstr = 'DECLARE "%s" CURSOR FOR %s' % (self.name, query)
- self.closed = 0
- elif _badQuery and self.conn.inTransaction:
- raise NotSupportedError, \
- "DROP [TABLE|INDEX] within a transaction"
- if not self.conn.inTransaction:
- if _badQuery:
- pass # not in transaction so DROP TABLE/INDEX is ok.
- else:
- self.conn._Connection__setupTransaction()
-
- _nl = len(self.conn.notices)
-
- try:
- _qstr = self.__unicodeConvert(_qstr)
- if len(parms) == 0:
- # If there are no paramters, just execute the query.
- self.res = self.conn.conn.query(_qstr)
- else:
- if len(parms) == 1 and \
- (type(parms[0]) in [DictType, ListType, TupleType] or \
- isinstance(parms[0], PgResultSet)):
- parms = (self.__unicodeConvert(parms[0]),)
- parms = _quoteall(parms[0])
- else:
- parms = self.__unicodeConvert(parms)
- parms = tuple(map(_quote, parms));
- self.res = self.conn.conn.query(_qstr % parms)
- self._rows_ = self.res.ntuples
- self._idx_ = 0
- self.__dict__['rowcount'] = -1 # New query - no fetch occured yet.
- if type(self.res) is not PgResultType:
- self.__dict__['rowcount'] = -1
- else:
- self.__dict__['oidValue'] = self.res.oidValue
- if self.res.resultType == RESULT_DQL:
- pass
- elif self.res.resultType == RESULT_DML:
- self.__dict__['rowcount'] = self.res.cmdTuples
- else:
- self.__dict__['rowcount'] = -1
- except OperationalError, msg:
- # Uh-oh. A fatal error occurred. This means the current trans-
- # action has been aborted. Try to recover to a sane state.
- if self.conn.inTransaction:
- _n = len(self.conn.notices)
- self.conn.conn.query('ROLLBACK WORK')
- if len(self.conn.notices) != _n:
- raise Warning, self.conn.notices.pop()
- self.conn.__dict__["inTransaction"] = 0
- self.conn._Connection__closeCursors()
- raise OperationalError, msg
- except InternalError, msg:
- # An internal error occured. Try to get to a sane state.
- self.conn.__dict__["inTransaction"] = 0
- self.conn._Connection__closeCursors_()
- self.conn.close()
- raise InternalError, msg
-
- if len(self.conn.notices) != _nl:
- _drop = self.conn.notices[-1]
- if _drop.find('transaction is aborted') > 0:
- raise Warning, self.conn.notices.pop()
-
- if self.res.resultType == RESULT_DQL:
- self.__makedesc__()
- elif _qstr[:8] == 'DECLARE ':
- # Ok -- we've created a cursor, we will pre-fetch the first row in
- # order to make the description array. Note: the first call to
- # fetchXXX will return the pre-fetched record.
- self.res = self.conn.conn.query('FETCH 1 FROM "%s"' % self.name)
- self._rows_ = self.res.ntuples
- self._idx_ = 0
- self.__makedesc__()
-
- if len(self.conn.notices) != _nl:
- _drop = self.conn.notices[-1]
- if _drop.find('transaction is aborted') > 0:
- raise Warning, self.conn.notices.pop()
-
- def executemany(self, query, parm_sequence):
- if self.closed:
- raise InterfaceError, "executemany failed - the cursor is closed."
-
- if self.conn is None:
- raise Error, "connection is closed."
-
- for _i in parm_sequence:
- self.execute(query, _i)
-
- def fetchone(self):
- if self.closed:
- raise InterfaceError, "fetchone failed - the cursor is closed."
-
- if self.conn is None:
- raise Error, "connection is closed."
-
- if self.res is None:
- raise Error, \
- "fetchone() failed - cursor does not contain a result."
- elif self.res.resultType != RESULT_DQL:
- if self.closed is None:
- raise Error, \
- "fetchone() Failed - cursor does not contain any rows."
-
- if self._idx_ < self._rows_:
- pass # Still data in result buffer, use it.
- elif self.closed == 0:
- _nl = len(self.conn.notices)
- self.res = self.conn.conn.query('FETCH 1 FROM "%s"' % self.name)
- self._rows_ = self.res.ntuples
- self._idx_ = 0
-
- if len(self.conn.notices) != _nl:
- _drop = self.conn.notices[-1]
- if _drop.find('transaction is aborted') > 0:
- raise Warning, self.conn.notices.pop()
-
- return self.__fetchOneRow()
-
- def fetchmany(self, sz=None):
- if self.closed:
- raise InterfaceError, "fetchmany failed - the cursor is closed."
-
- if self.conn is None:
- raise Error, "connection is closed."
-
- if self.res is None:
- raise Error, \
- "fetchmany() failed - cursor does not contain a result."
- elif self.res.resultType != RESULT_DQL:
- if self.close is None:
- raise Error, \
- "fetchmany() Failed - cursor does not contain any rows."
-
- if sz is None:
- sz = self.arraysize
- else:
- self.__dict__["arraysize"] = abs(sz)
-
- if sz < 0:
- return self.fetchall()
-
- _list = []
-
- # While there are still results in the PgResult object, append them
- # to the list of results.
- while self._idx_ < self._rows_ and sz > 0:
- _list.append(self.__fetchOneRow())
- sz = sz - 1
-
- # If still need more results to fullfill the request, fetch them from
- # the PostgreSQL portal.
- if self.closed == 0 and sz > 0:
- _nl = len(self.conn.notices)
- self.res = self.conn.conn.query('FETCH %d FROM "%s"' %
- (sz, self.name))
- self._rows_ = self.res.ntuples
- self._idx_ = 0
-
- if len(self.conn.notices) != _nl:
- _drop = self.conn.notices[-1]
- if _drop.find('transaction is aborted') > 0:
- raise Warning, self.conn.notices.pop()
-
- return self.__fetchManyRows(sz, _list)
-
- def fetchall(self):
- if self.closed:
- raise InterfaceError, "fetchall failed - the cursor is closed."
-
- if self.conn is None:
- raise Error, "connection is closed."
-
- if self.res is None:
- raise Error, \
- "fetchall() failed - cursor does not contain a result."
- elif self.res.resultType != RESULT_DQL:
- if self.closed is None:
- raise Error, \
- "fetchall() Failed - cursor does not contain any rows."
-
- _list = []
-
- # While there are still results in the PgResult object, append them
- # to the list of results.
- while self._idx_ < self._rows_:
- _list.append(self.__fetchOneRow())
-
- # Fetch the remaining results from the PostgreSQL portal.
- if self.closed == 0:
- _nl = len(self.conn.notices)
- self.res = self.conn.conn.query('FETCH ALL FROM "%s"' % self.name)
- self._rows_ = self.res.ntuples
- self._idx_ = 0
-
- if len(self.conn.notices) != _nl:
- _drop = self.conn.notices[-1]
- if _drop.find('transaction is aborted') > 0:
- raise Warning, self.conn.notices.pop()
-
- return self.__fetchManyRows(self._rows_, _list)
-
- def rewind(self):
- if self.closed:
- raise InterfaceError, "rewind failed - the cursor is closed."
-
- if self.conn is None:
- raise Error, "connection is closed."
-
- if self.res is None:
- raise Error, "rewind() failed - cursor does not contain a result."
- elif self.res.resultType != RESULT_DQL:
- if self.closed is None:
- raise Error, \
- "rewind() Failed - cursor does not contain any rows."
-
- if self.closed == 0:
- _nl = len(self.conn.notices)
- self.res = self.conn.conn.query('MOVE BACKWARD ALL IN "%s"' %
- self.name)
- self._rows_ = 0
- if len(self.conn.notices) != _nl:
- _drop = self.conn.notices[-1]
- if _drop.find('transaction is aborted') > 0:
- raise Warning, self.conn.notices.pop()
-
- self.__dict__["rowcount"] = -1
- self._idx_ = 0
-
- def setinputsizes(self, sizes):
- if self.closed:
- raise InterfaceError, "setinputsize failed - the cursor is closed."
-
- def setoutputsize(self, size, column=None):
- if self.closed:
- raise InterfaceError, "setoutputsize failed - the cursor is closed."
+++ /dev/null
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id
-#
-import string
-import pygtk
-import gtk
-
-import pgw
-
-
-class RunSQL:
- """Execute the SQL buffer and fill the GtkNotebook with the results."""
-
- def __init__(self, execute, txtSQL, tabResult, result):
- self.execute = execute
- self.txtSQL = txtSQL
- self.lblResult = result
- self.tabResult = tabResult
- self.error_font = {}
-
-
- def result(self, msg):
- """update status bar text"""
- self.lblResult.set_markup('<span foreground="#004400">' + msg + '</span>')
-
-
- def str_limit(self, s, max = 20):
- s = string.replace(s, '\\\\', '\\')
- if (len(s) > (max + 3)):
- return s[:max] + "..."
- return s
-
-
- def error(self, msg, title = None):
- """update the error messages"""
- if (title is None):
- title = '<span foreground="#880000">' + _('Errors') + '</span>'
- scroll = gtk.ScrolledWindow()
- txt = gtk.TextView()
- txt.get_buffer().set_text(unicode(msg, pgw.get_user_encoding()))
- pgw.set_proportional(txt.get_buffer())
- txt.set_editable(False)
- scroll.add(txt)
- scroll.show_all()
- label = gtk.Label()
- label.set_markup(title)
- self.tabResult.append_page(scroll, label)
-
-
- def add_treeview(self, title):
- """Add a new tree view"""
- scroll = gtk.ScrolledWindow()
- tvw = gtk.TreeView()
- scroll.add(tvw)
- scroll.show()
- label = gtk.Label(title)
- self.tabResult.append_page(scroll, label)
- return (tvw, label)
-
-
- def fill_treeview(self, sql, result):
- """build the tree view columns and rows"""
- sql = sql.strip()
- treeview, label = self.add_treeview(self.str_limit(sql))
- type_str = ""
- cell = gtk.CellRendererText()
- ncol = 0
- for col in result.description:
- column = gtk.TreeViewColumn(string.replace(col[0], "_", "__"))
- column.pack_start(cell, True)
- column.add_attribute(cell, 'text', ncol)
- column.set_sort_column_id(ncol)
- treeview.append_column(column)
- ncol = ncol + 1
- if (type_str == ""):
- type_str = "str"
- else:
- type_str = type_str + ",str"
- pass
-
- # dynamically create the liststore
- code = "liststore = gtk.ListStore(" + type_str + ")"
- exec compile(code, "<string>", 'exec')
-
- # fill the tree view
- row = result.fetchone()
- rows = 0
- while ( row is not None ) :
- c = 0
- while c < len(row):
- if (row[c] is None) and type(row) == list:
- row[c] = '(null)'
- c = c + 1
- liststore.append(row)
- row = result.fetchone()
- rows = rows + 1
- result.close()
- treeview.set_model(liststore)
- treeview.set_reorderable(True)
- treeview.columns_autosize()
- treeview.show()
- label.set_markup(label.get_text() + ' <span foreground="#555555">: ' +
- str(rows) + "</span>")
- return rows
-
-
- def run(self):
- """Run the query and update the results"""
- # clear the treeviews and the error text buffers
- for child in self.tabResult.get_children():
- self.tabResult.remove(child)
- self.result(_("Please wait, executing the query and fetching the results..."))
- # update the display
- while (gtk.events_pending() == True):
- gtk.main_iteration_do(False)
-
- # execute the query
- sqlbuffer= self.txtSQL.get_buffer()
- notices = []
- try:
- result = self.execute.execute(sqlbuffer)
- rows = 0
- if (isinstance(result, list)):
- # multiple queries and multiple results...
- parts = self.execute.split(sqlbuffer)
- sqls = []
- have_errors = 0
- for sql in parts :
- sql = string.strip(sql)
- if (len(sql) > 0) :
- sqls.append(sql)
- for res in result:
- sql = sqls.pop(0)
- try:
- if (res['cursor'].description is not None):
- rows += self.fill_treeview(sql, res['cursor'])
- else:
- rows += res['cursor'].rowcount
- notices = res['notices']
- except KeyError:
- try:
- self.error(res['text'], "psql : " + self.str_limit(sql))
- except KeyError:
- notices = res['notices']
- self.error(res['error'],
- '<span foreground="#880000">' + _('Errors :') + '</span> ' +
- self.str_limit(sql))
- have_errors += 1
- self.tabResult.set_current_page(0)
- if (rows > 1):
- res_str = _("%d results or affected rows") % rows
- elif (rows == 1):
- res_str = _("1 result or affected row")
- else:
- res_str = _("No result or no affected row")
- if (have_errors > 1) :
- res_str += ', <span foreground="#880000">' + \
- _('%d queries failed') % have_errors + '</span>'
- elif (have_errors > 0) :
- res_str += ', <span foreground="#880000">' + \
- _('1 query failed') + '</span>'
- self.result(res_str)
- else: # one query
- sql = sqlbuffer.get_text(sqlbuffer.get_start_iter(),
- sqlbuffer.get_end_iter())
- try:
- if (result is None):
- self.result(_("No result"))
- elif (result['cursor'].description is None):
- # not a SELECT
- rows = result['cursor'].rowcount
- notices = result['notices']
- if (rows > 1):
- self.result(_("%d rows affected") % rows)
- elif (rows == 1):
- self.result(_("1 row affected"))
- else:
- self.result(_("No row affected"))
- else:
- # only one SELECT
- rows = self.fill_treeview(sql, result['cursor'])
- notices = result['notices']
- if (rows > 1):
- self.result(_("%d results") % rows)
- elif (rows == 1):
- self.result(_("1 result"))
- else:
- self.result(_("No result"))
- except KeyError:
- try:
- self.error(result['text'], "psql : " + self.str_limit(sql))
- self.result("")
- except KeyError:
- self.error(result['error'])
- self.result('<span foreground="#880000">' + _('query failed') + '</span>')
- notices = result['notices']
- buffer = self.txtSQL.get_buffer()
- buffer.move_mark_by_name('selection_bound', buffer.get_start_iter());
- buffer.move_mark_by_name('insert', buffer.get_end_iter());
-
- except KeyError, errstr:
- self.result('<span foreground="#880000">' + _('query failed') + '</span>')
- self.error(str(errstr))
-
- if (len(notices) > 0):
- msg = ""
- while len(notices):
- msg += notices.pop()
- self.error(msg, '<span foreground="#000088">'+ _("log") + '</span>')
-
- # restore the focus
- self.txtSQL.grab_focus()
-
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: Syntax.py,v 1.30 2008/03/12 20:26:23 hmichelon Exp $
-#
-import gtk.gdk
-import pango
-import pgw
-import pgw.Lexical
-
-# built-in data types and extensions
-TYPES = [ 'BIGINT', 'INT8', 'BIGSERIAL', 'SERIAL8', 'BIT', 'VARYING',
- 'VARBIT', 'BOOL', 'BOOLEAN', 'BOX', 'BYTEA', 'CHARACTER', 'VARCHAR',
- 'CHAR', 'CIDR', 'CIRCLE', 'DATE', 'DOUBLE', 'PRECISION',
- 'INET', 'INTEGER', 'FLOAT8', 'INT', 'INT4', 'INTERVAL',
- 'LINE', 'LSEG', 'MACADDR', 'MONEY', 'NUMERIC', 'DECIMAL',
- 'PATH', 'POINT', 'POLYGON', 'REAL', 'FLOAT4', 'SMALLINT',
- 'INT2', 'SERIAL', 'SERIAL4', 'TEXT','TIME', 'TIMESTAMP',
- 'TIMETZ', 'TIMESTAMPTZ', 'WITHOUT', 'TIME', 'ZONE'
- ]
-
-# special constants
-SPECIALS = [ 'FALSE', 'NULL', 'TRUE', 'UNKNOWN', 'ALL', 'ANY', 'SOME' ]
-
-
-# named operators, constructs, conditionals and subqueries
-OPERATORS2 = [ 'OVERLAPS', 'AND', 'OR', 'NOT', 'BETWEEN', 'IS', 'ISNULL',
- 'NOTNULL', 'CAST', 'LIKE', 'ILIKE', 'SIMILAR',
- 'EXISTS', 'IN',
- 'CASE', 'WHEN', 'THEN', 'ELSE', 'END',
- 'UNION', 'ARRAY',
- ]
-
-# SQL and PostgreSQL statements
-STATEMENTS = [ 'ABORT', 'ALTER', 'ANALYSE', 'BEGIN', 'CHECKPOINT', 'CLOSE',
- 'CLUSTER', 'COMMENT', 'COMMIT', 'COPY', 'CREATE', 'DEALLOCATE',
- 'DECLARE', 'DELETE', 'DROP', 'END', 'EXECUTE', 'EXPLAIN',
- 'FETCH', 'GRANT', 'INSERT', 'LISTEN', 'LOAD', 'LOCK', 'MOVE',
- 'NOTIFY', 'PREPARE', 'REINDEX', 'RELEASE', 'RESET', 'REVOKE',
- 'ROLLBACK', 'SAVEPOINT', 'SELECT', 'SET', 'SHOW', 'START',
- 'TRUNCATE', 'UNLISTEN', 'UPDATE', 'VACUUM' ]
-
-# built-in functions
-BUILTINS = [
- # MATH
- 'ABS', 'CBTR', 'CEIL', 'CEILING', 'DEGREES', 'EXP', 'FLOOR', 'LN',
- 'LOG', 'MOD', 'PI', 'POWER', 'RADIANS', 'RANDOM', 'ROUND',
- 'SETSEED', 'SIGN', 'SQRT', 'TRUNC', 'WIDTH_BUCKET', 'ACOS',
- 'ASIN', 'ATAN', 'ATAN2', 'COS', 'COT', 'SIN', 'TAN',
- # STRING
- 'BIT_LENGTH', 'CHAR_LENGTH', 'CONVERT', 'LOWER', 'OCTET_LENGTH',
- 'OVERLAY', 'POSITION', 'SUBSTRING', 'TRIM', 'UPPER', 'ASCII',
- 'BTRIM', 'CHR', 'DECODE', 'ENCODE', 'INITCAP', 'LENGTH',
- 'LPAD', 'LTRIM', 'MD5', 'PG_CLIENT_ENCODING', 'QUOTE_IDENT',
- 'QUOTE_LITERAL', 'REPEAT', 'RPAD', 'RTRIM', 'SPLIT_PART',
- 'STRPOS', 'TO_ASCII', 'TO_HEX', 'TRANSLATE',
- # BSTRING
- 'GET_BYTE', 'SET_BYTE', 'GET_BIT', 'SET_BIT',
- # DATE
- 'TO_CHAR', 'TO_DATE', 'TO_TIMESTAMP', 'TO_NUMBER',
- 'AGE', 'DATE_PART', 'DATE_TRUNC', 'EXTRACT', 'ISFINITE',
- 'NOW', 'TIMEOFDAY',
- # GEOMETRIC
- 'AREA', 'BOX_INTERSECT', 'CENTER', 'DIAMETER', 'HEIGHT',
- 'ISCLOSED', 'ISOPEN', 'LENGTH', 'NPOINTS', 'PCLOSE',
- 'POPEN', 'RADIUS', 'WIDTH', 'BOX', 'CIRCLE', 'LSEG',
- 'PATH', 'POINT', 'POLYGON',
- # NETWORK
- 'BROADCAST', 'HOST', 'MASKLEN', 'SET_MASKLEN',
- 'NETMASK', 'HOSTMASK', 'NETWORK', 'TEXT', 'ABBREV',
- 'FAMILY', 'TRUNC',
- # SEQUENCES
- 'NEXTVAL', 'CURRVAL',
- # CONDITIONAL
- 'COALESCE', 'NULLIF',
- # ARRAY
- 'ARRAY_CAT', 'ARRAY_APPEND', 'ARRAY_PREPEND', 'ARRAY_DIMS',
- 'ARRAY_LOWER', 'ARRAY_UPPER', 'ARRAY_TO_STRING',
- 'STRING_TO_ARRAY'
- # AGGREGATE
- 'AVG', 'BIT_AND', 'BIT_OR', 'BOOL_AND', 'BOOL_OR',
- 'COUNT', 'EVERY', 'MAX', 'MIN', 'STDDEV', 'SUM',
- 'VARIANCE'
- # SETS
- 'GENERATE_SERIES',
- # SYSTEM
- 'CURRENT_DATABASE', 'CURRENT_SCHEMA',
- 'INET_CLIENT_ADDR', 'INET_CLIENT_PORT',
- 'INET_SERVER_ADDR', 'INET_SERVER_PORT', 'VERSION',
- 'HAS_TABLE_PRIVILEGE', 'HAS_DATABASE_PRIVILEGE',
- 'HAS_FUNCTION_PRIVILEGE', 'HAS_LANGUAGE_PRIVILEGE',
- 'HAS_SCHEMA_PRIVILEGE', 'HAS_TABLESPACE_PRIVILEGE'
- 'PG_TABLE_IS_VISIBLE', 'PG_TYPE_IS_VISIBLE',
- 'PG_FUNCTION_IS_VISIBLE', 'PG_OPERATOR_IS_VISIBLE',
- 'PG_OPCLASS_IS_VISIBLE', 'PG_CONVERSION_IS_VISIBLE',
- 'FORMAT_TYPE', 'PG_GET_VIEWDEF', 'PG_GET_RULEGET',
- 'PG_GET_INDEXDEF', 'PG_GET_TRIGGERDEF',
- 'PG_GET_CONSTRAINTDEF', 'PG_GET_EXPR', 'PG_GET_USERBYID',
- 'PG_GET_SERIAL_SEQUENCE', 'PG_TABLESPACE_DATABASES',
- 'OBJ_DESCRIPITION',
- # SYSTEM ADMIN
- 'CURRENT_SETTINGS', 'SET_CONFIG', 'PG_CANCEL_BACKEND',
- 'PG_START_BACKUP', 'PG_STOP_BACKUP',
-
- ]
-
-# built-in functions that can be used without ()
-# or conditional expressions
-BUILTINS2 = [ # DATE
- 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP',
- 'LOCALTIME', 'LOCALTIMESTAMP',
- # SYSTEM
- 'CURRENT_USER', 'SESSION_USER', 'USER',
- ]
-
-
-"""
-# PL/PgSQL keywords
-PLPGSQL = [ # structure
- 'DECLARE', 'BEGIN', 'END',
- # declarations
- 'RECORD', 'DEFAULT', 'CONSTANT', 'ALIAS', 'FOR', 'TYPE', 'ROWTYPE',
- 'RENAME', 'TO',
- # Statements
- 'FOUND', 'PERFORM', 'GET', 'DIAGNOSTICS',
- # Control Structures
- 'RETURN', 'NEXT', 'IF', 'THEN', 'ELSE', 'END', 'ELSIF', 'LOOP',
- 'EXIT', 'WHILE', 'REVERSE', 'IN', 'EXCEPTION', 'WHEN',
- # Cursors
- 'CURSOR', 'OPEN', 'EXECUTE', 'FETCH', 'INTO', 'CLOSE',
- # Errors
- 'RAISE', 'DEBUG', 'LOG', 'INFO', 'NOTICE', 'WARNING', 'EXCEPTION',
- # Trigger Procedures
- 'NEW', 'OLD', 'TG_NAME', 'TG_WHEN', 'TG_LEVEL', 'TG_OP',
- 'TG_RELID', 'TG_RELNAME', 'TG_NARGS', 'TG_ARGV',
- ]
-"""
-
-# keywords for each statement
-KEYWORDS = { 'ABORT' : [ 'WORK', 'TRANSACTION' ],
- 'ALTER' : { 'AGGREGATE' : [ [ 'RENAME', 'TO' ],
- [ 'OWNER', 'TO' ]
- ],
- 'CONVERSION' : [ [ 'RENAME', 'TO' ],
- [ 'OWNER', 'TO' ]
- ],
- 'DATABASE' : [ 'SET', 'TO', 'DEFAULT',
- 'RESET', [ 'RENAME', 'TO' ],
- [ 'OWNER', 'TO' ]
- ],
- 'DOMAIN' : [ [ 'SET', 'DEFAULT' ],
- [ 'DROP', 'DEFAULT' ],
- 'ADD',
- [ 'DROP', 'CONSTRAINT' ],
- 'RESTRICT', 'CASCADE',
- [ 'OWNER', 'TO' ]
- ],
- 'FUNCTION' : [ [ 'RENAME', 'TO' ],
- [ 'OWNER', 'TO' ]
- ],
- 'GROUP' : [ [ 'ADD', 'USER' ],
- [ 'DROP', 'USER' ],
- [ 'RENAME', 'TO' ]
- ],
- 'INDEX' : [ [ 'RENAME', 'TO' ],
- [ 'OWNER', 'TO' ],
- [ 'SET', 'TABLESPACE' ]
- ],
- 'LANGUAGE' : [ [ 'RENAME', 'TO' ] ],
- 'OPERATOR' : [ 'NONE', [ 'OWNER', 'TO' ],
- 'CLASS', 'USING'
- ],
- 'SCHEMA' : [ [ 'RENAME', 'TO' ],
- [ 'OWNER', 'TO' ]
- ],
- 'SEQUENCE' : [ [ 'INCREMENT', 'BY' ],
- 'MINVALUE', 'MAXVALUE',
- [ 'RESTART', 'WITH' ], 'CACHE',
- 'NO', 'CYCLE'
- ],
- 'TABLE' : [ 'ONLY', [ 'RENAME', 'COLUMN' ],
- [ 'RENAME', 'TO' ], 'TO',
- [ 'ADD', 'COLUMN' ],
- [ 'DROP', 'COLUMN' ], 'RESTRICT',
- 'CASCADE', 'TYPE', 'USING',
- [ 'ALTER', 'COLUMN' ],
- [ 'SET', 'DEFAULT' ],
- [ 'DROP', 'DEFAULT' ],
- 'SET', 'DROP',
- [ 'SET', 'STATISTICS' ],
- [ 'SET', 'STORAGE' ], 'PLAIN',
- 'EXTERNAL', 'EXTERNAL', 'MAIN',
- [ 'DROP', 'CONSTRAINT' ],
- [ 'CLUSTER', 'ON' ],
- [ 'SET', 'WITHOUT', 'CLUSTER' ],
- [ 'SET', 'WITHOUT', 'OIDS' ],
- [ 'OWNER', 'TO' ],
- [ 'SET', 'TABLESPACE' ],
- 'CHECK',
- [ 'FOREIGN', 'KEY' ], 'CASCADE',
- [ 'PRIMARY', 'KEY' ],
- [ 'ON', 'DELETE' ],
- [ 'ON', 'UPDATE' ], 'REFERENCES',
- ],
- 'TABLESPACE' : [ [ 'RENAME', 'TO' ],
- [ 'OWNER', 'TO' ]
- ],
- 'TRIGGER' : [ 'ON', [ 'RENAME', 'TO' ] ],
- 'TYPE' : [ [ 'OWNER', 'TO' ] ],
- 'USER' : [ 'WITH', 'CREATEDB', 'NOCREATEDB',
- 'CREATEUSER', 'NOCREATEUSER',
- 'ENCRYPTED', 'UNENCRYPTED',
- 'PASSWORD', [ 'VALID', 'UNTIL' ],
- [ 'RENAME', 'TO' ], 'SET',
- 'TO', 'DEFAULT', 'RESET'
- ],
- },
- 'ANALYZE' : [ 'VERBOSE' ],
- 'BEGIN' : [ 'WORK', 'TRANSACTION', [ 'ISOLATION', 'LEVEL' ],
- 'SERIALIZABLE', [ 'REPEATABLE', 'READ' ],
- [ 'READ', 'COMMITTED' ], [ 'READ', 'UNCOMMITTED' ],
- [ 'READ', 'WRITE' ], [ 'READ', 'ONLY' ]
- ],
- 'CHECKPOINT' : [],
- 'CLOSE' : [],
- 'CLUSTER' : [ 'ON' ],
- 'COMMENT' : { 'ON' : [ 'TABLE', 'COLUMN', 'AGGREGATE', 'CAST',
- 'AS', 'CONSTRAINT', 'ON', 'CONVERSION',
- 'DATABASE', 'DOMAIN', 'FUNCTION', 'INDEX',
- [ 'LARGE', 'OBJECT' ], 'OPERATOR',
- [ 'OPERATOR', 'CLASS' ], 'USING',
- 'PROCEDURAL', 'LANGUAGE', 'RULE',
- 'SCHEMA', 'SEQUENCE', 'TRIGGER',
- 'TYPE', 'VIEW', 'IS'
- ]
- },
- 'COMMIT' : [ 'WORK', 'TRANSACTION' ],
- 'COPY' : [ 'FROM', 'STDIN', 'WITH', 'BINARY', 'OIDS',
- 'DELIMITER', 'CSV', 'AS', 'ESCAPE'
- 'FORCE', 'TO', 'STDOUT', 'QUOTE'
- ],
- 'CREATE' : { 'AGGREGATE' : [ 'BASETYPE', 'SFUNC', 'STYPE',
- 'FINALFUNC', 'INITCOND' ],
- 'CAST' : [ [ 'WITH', 'FUNCTION' ],
- [ 'AS', 'ASSIGNMENT' ],
- [ 'AS', 'IMPLICIT' ],
- [ 'WITHOUT', 'FUNCTION']
- ],
- 'CONSTRAINT' : [ 'TRIGGER', 'AFTER', 'ON',
- [ 'FOR', 'EACH', 'ROW', 'EXECUTE'
- 'PROCEDURE' ]
- ],
- 'CONVERSION' : [ 'FOR', 'TO', 'FROM' ],
- 'DEFAULT' : [ 'CONVERSION', 'FOR',
- 'TO', 'FROM' ],
- 'DATABASE' : [ 'WITH', 'OWNER', 'TEMPLATE',
- 'ENCODING', 'TABLESPACE',
- 'DEFAULT' ],
- 'DOMAIN' : [ 'AS', 'DEFAULT', 'CONSTRAINT',
- 'CHECK'
- ],
- 'OR' : [ 'REPLACE', 'FUNCTION',
- 'RETURNS', 'LANGUAGE', 'AS',
- 'IMMUTABLE', 'STABLE',
- 'VOLATILE', [ 'CALLED', 'ON',
- 'NULL', 'INPUT' ], [ 'RETURNS',
- 'NULL', 'ON', 'NULL', 'INPUT' ],
- 'STRICT', 'EXTERNAL',
- [ 'SECURITY', 'INVOKER' ],
- [ 'SECURITY', 'DEFINER' ], 'WITH',
- # XXX: for RULE
- 'RULE',
- 'AS', 'ON', 'TO', 'WHERE',
- 'DO', 'ALSO', 'INSTEAD',
- 'NOTHING',
- 'VIEW'
- ],
- 'FUNCTION' : [ 'RETURNS', 'LANGUAGE', 'AS',
- 'IMMUTABLE', 'STABLE',
- 'VOLATILE', [ 'CALLED', 'ON',
- 'NULL', 'INPUT' ], [ 'RETURNS',
- 'NULL', 'ON', 'NULL', 'INPUT' ],
- 'STRICT', 'EXTERNAL',
- [ 'SECURITY', 'INVOKER' ],
- [ 'SECURITY', 'DEFINER' ], 'WITH'
- ],
- 'GROUP' : [ 'WITH', 'SYSID', 'USER' ],
- 'UNIQUE' : [ 'ON', 'USING', 'TABLESPACE',
- 'WHERE', 'INDEX' ],
- 'INDEX' : [ 'ON', 'USING', 'TABLESPACE',
- 'WHERE', 'RTREE', 'HASH' ],
- 'LANGUAGE' : [ 'HANDLER', 'VALIDATOR' ],
- 'PROCEDURAL' : [ 'HANDLER', 'VALIDATOR',
- 'LANGUAGE' ],
- 'TRUSTED' : [ 'HANDLER', 'VALIDATOR',
- 'LANGUAGE' ],
- 'OPERATOR' : [ 'PROCEDURE', 'LEFTARG',
- 'RIGHTARG', 'COMMUTATOR',
- 'NEGATOR', 'RESTRICT',
- 'JOIN', 'HASHES', 'MERGES',
- 'SORT1', 'SORT2', 'LTCMP',
- 'GTCMP',
- 'CLASS', 'DEFAULT',
- [ 'FOR', 'TYPE' ], 'USING',
- 'AS', 'OPERATOR', 'RECHECK',
- 'FUNCTION', 'STORAGE' ],
- 'RULE' : [ 'AS', 'ON', 'TO', 'WHERE',
- 'DO', 'ALSO', 'INSTEAD',
- 'NOTHING' ],
- 'SCHEMA' : [ 'AUTHORIZATION' ],
- 'SEQUENCE' : [ 'INCREMENT', 'BY', 'MINVALUE',
- 'NO', 'MAXVALUE', [ 'START',
- 'WITH' ], 'CACHE', 'CYCLE' ],
- 'TEMPORARY' : [ 'SEQUENCE',
- 'INCREMENT', 'BY', 'MINVALUE',
- 'NO', 'MAXVALUE', [ 'START',
- 'WITH' ], 'CACHE', 'CYCLE',
- # CREATE TABLE
- 'TABLE',
- 'DEFAULT', 'INCLUDING',
- 'EXCLUDING', 'DEFAULTS',
- 'INHERITS', 'WITH',
- 'WITHOUT', 'OIDS',
- [ 'ON', 'COMMIT' ],
- [ 'PRESERVE', 'ROWS' ],
- [ 'DELETE', 'ROWS' ], 'DROP',
- 'TABLESPACE', 'CONSTRAINT',
- 'UNIQUE',
- [ 'USING', 'INDEX', 'TABLESPACE'],
- [ 'PRIMARY', 'KEY' ], 'CHECK',
- 'REFERENCES', [ 'MATCH', 'FULL' ],
- [ 'MATCH', 'PARTIAL' ],
- [ 'MATCH', 'SIMPLE' ],
- [ 'ON', 'DELETE' ],
- [ 'ON', 'UPDATE' ], 'DEFERRABLE',
- [ 'INITIALLY', 'DEFERRED' ],
- [ 'INITIALLY', 'IMMEDIATE' ],
- [ 'FOREIGN', 'KEY' ],
- 'CHECK',
- ],
- 'TEMP' : [ 'SEQUENCE',
- 'INCREMENT', 'BY', 'MINVALUE',
- 'NO', 'MAXVALUE', [ 'START',
- 'WITH' ], 'CACHE', 'CYCLE',
- # CREATE TABLE
- 'TABLE',
- 'DEFAULT', 'INCLUDING',
- 'EXCLUDING', 'DEFAULTS',
- 'INHERITS', 'WITH', 'OIDS',
- 'WITHOUT',
- [ 'ON', 'COMMIT' ],
- [ 'PRESERVE', 'ROWS' ],
- [ 'DELETE', 'ROWS' ], 'DROP',
- 'TABLESPACE', 'CONSTRAINT',
- 'UNIQUE',
- [ 'USING', 'INDEX', 'TABLESPACE'],
- [ 'PRIMARY', 'KEY' ], 'CHECK',
- 'REFERENCES', [ 'MATCH', 'FULL' ],
- [ 'MATCH', 'PARTIAL' ],
- [ 'MATCH', 'SIMPLE' ],
- [ 'ON', 'DELETE' ],
- [ 'ON', 'UPDATE' ], 'DEFERRABLE',
- [ 'INITIALLY', 'DEFERRED' ],
- [ 'INITIALLY', 'IMMEDIATE' ],
- [ 'FOREIGN', 'KEY' ],
- 'CHECK',
- ],
- 'LOCAL' : [ 'TABLE',
- 'DEFAULT', 'INCLUDING',
- 'EXCLUDING', 'DEFAULTS',
- 'INHERITS', 'WITH',
- 'WITHOUT', 'OIDS',
- [ 'ON', 'COMMIT' ],
- [ 'PRESERVE', 'ROWS' ],
- [ 'DELETE', 'ROWS' ], 'DROP',
- 'TABLESPACE', 'CONSTRAINT',
- 'UNIQUE',
- [ 'USING', 'INDEX', 'TABLESPACE'],
- [ 'PRIMARY', 'KEY' ], 'CHECK',
- 'REFERENCES', [ 'MATCH', 'FULL' ],
- [ 'MATCH', 'PARTIAL' ],
- [ 'MATCH', 'SIMPLE' ],
- [ 'ON', 'DELETE' ],
- [ 'ON', 'UPDATE' ], 'DEFERRABLE',
- [ 'INITIALLY', 'DEFERRED' ],
- [ 'INITIALLY', 'IMMEDIATE' ],
- [ 'FOREIGN', 'KEY' ],
- 'CHECK',
- ],
- 'GLOBAL' : [ 'TABLE',
- 'DEFAULT', 'INCLUDING',
- 'EXCLUDING', 'DEFAULTS',
- 'INHERITS', 'WITH', 'OIDS',
- 'WITHOUT',
- [ 'ON', 'COMMIT' ],
- [ 'PRESERVE', 'ROWS' ],
- [ 'DELETE', 'ROWS' ], 'DROP',
- 'TABLESPACE', 'CONSTRAINT',
- 'UNIQUE',
- [ 'USING', 'INDEX', 'TABLESPACE'],
- [ 'PRIMARY', 'KEY' ], 'CHECK',
- 'REFERENCES', [ 'MATCH', 'FULL' ],
- [ 'MATCH', 'PARTIAL' ],
- [ 'MATCH', 'SIMPLE' ],
- [ 'ON', 'DELETE' ],
- [ 'ON', 'UPDATE' ], 'DEFERRABLE',
- [ 'INITIALLY', 'DEFERRED' ],
- [ 'INITIALLY', 'IMMEDIATE' ],
- [ 'FOREIGN', 'KEY' ],
- 'CHECK',
- ],
- 'TABLE' : [ 'DEFAULT', 'INCLUDING',
- 'EXCLUDING', 'DEFAULTS',
- 'INHERITS', 'WITH', 'OIDS',
- 'WITHOUT',
- [ 'ON', 'COMMIT' ],
- [ 'PRESERVE', 'ROWS' ],
- [ 'DELETE', 'ROWS' ], 'DROP',
- 'TABLESPACE', 'CONSTRAINT',
- 'UNIQUE',
- [ 'USING', 'INDEX', 'TABLESPACE'],
- [ 'PRIMARY', 'KEY' ], 'CHECK',
- 'REFERENCES', [ 'MATCH', 'FULL' ],
- [ 'MATCH', 'PARTIAL' ],
- [ 'MATCH', 'SIMPLE' ],
- [ 'ON', 'DELETE' ],
- [ 'ON', 'UPDATE' ], 'DEFERRABLE',
- 'INITIALLY', 'DEFERRED',
- 'IMMEDIATE', 'RESTRICT',
- [ 'FOREIGN', 'KEY' ], 'CASCADE'
- 'CHECK',
- ],
- 'TABLESPACE' : [ 'OWNER', 'LOCATION' ],
- 'TRIGGER' : [ 'BEFORE', 'AFTER', 'OR', 'ON',
- 'FOR', 'EACH', 'ROW', 'STATEMENT',
- [ 'EXECUTE', 'PROCEDURE' ]
- ],
- 'TYPE' : [ 'AS', 'INPUT', 'OUTPUT',
- 'RECEIVE', 'SEND', 'ANALYZE',
- 'INTERNALLENGTH', 'VARIABLE',
- 'PASSEDBYVALUE', 'ALIGNMENT',
- 'STORAGE', 'DEFAULT',
- 'ELEMENT', 'DELIMITER' ],
- 'USER' : [ 'WITH', 'SYSID', 'CREATEDB',
- 'NOCREATEDB', 'CREATEUSER',
- 'NOCREATEUSER', [ 'IN', 'GROUP' ],
- 'ENCRYPTED', 'UNENCRYPTED',
- 'PASSWORD', [ 'VALID', 'UNTIL' ]
- ],
- 'VIEW' : [ 'AS' ,
- # select
- 'SELECT', 'DISTINCT', 'ON',
- 'AS', 'FROM', 'WHERE',
- [ 'GROUP', 'BY' ], 'HAVING',
- 'UNION', 'INTERSECT', 'EXCEPT',
- [ 'ORDER', 'BY' ], 'ASC',
- 'DESC', 'USING', 'LIMIT',
- 'OFFSET', [ 'FOR', 'UPDATE',
- 'OF' ], 'ONLY', 'NATURAL',
- 'USING', 'INNER', 'LEFT', 'OUTER',
- 'RIGHT', 'FULL', 'JOIN', 'CROSS',
- 'INTO' ],
- },
- 'DEALLOCATE': [ 'PREPARE' ],
- 'DECLARE' : [ 'BINARY', 'INSENSITIVE', 'NO', 'SCROLL',
- 'CURSOR', 'WITH', 'WITHOUT', 'HOLD', 'FOR',
- [ 'READ', 'ONLY' ], 'UPDATE', 'OF' ],
- 'DELETE' : { 'FROM' : [ 'ONLY', 'WHERE' ] },
- 'DROP' : { 'AGGREGATE' : [ 'CASCADE', 'RESTRICT' ],
- 'CAST' : [ 'AS', 'CASCADE', 'RESTRICT' ],
- 'CONVERSION' : [ 'CASCADE', 'RESTRICT' ],
- 'DATABASE' : [],
- 'DOMAIN' : [ 'CASCADE', 'RESTRICT' ],
- 'FUNCTION' : [ 'CASCADE', 'RESTRICT' ],
- 'GROUP' : [],
- 'INDEX' : [ 'CASCADE', 'RESTRICT' ],
- 'LANGUAGE' : [ 'CASCADE', 'RESTRICT' ],
- 'PROCEDURAL' : ['LANGUAGE', 'CASCADE', 'RESTRICT'],
- 'OPERATOR' : [ 'NONE', 'CASCADE', 'RESTRICT',
- 'CLASS', 'USING' ],
- 'RULE' : [ 'ON', 'CASCADE', 'RESTRICT' ],
- 'SCHEMA' : [ 'CASCADE', 'RESTRICT' ],
- 'SEQUENCE' : [ 'CASCADE', 'RESTRICT' ],
- 'TABLE' : [ 'CASCADE', 'RESTRICT' ],
- 'TABLESPACE' : [],
- 'TRIGGER' : [ 'ON', 'CASCADE', 'RESTRICT' ],
- 'TYPE' : [ 'CASCADE', 'RESTRICT' ],
- 'USER' : [],
- 'VIEW' : [ 'CASCADE', 'RESTRICT' ],
- },
- 'END' : [ 'WORK', 'TRANSACTION' ],
- 'EXECUTE' : [],
- 'EXPLAIN' : [ 'ANALYZE', 'VERBOSE' ],
- 'FETCH' : [ 'FROM', 'IN', 'NEXT', 'PRIOR', 'FIRST', 'LAST',
- 'ABSOLUTE', 'RELATIVE', 'FORWARD',
- 'BACKWARD' ],
- 'GRANT' : [ 'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE',
- 'REFERENCES', 'TRIGGER', 'PRIVILEGES',
- 'ON', 'TABLE', 'TO', 'GROUP', 'PUBLIC',
- [ 'WITH', 'GRANT', 'OPTION' ],
- 'CREATE', 'TEMPORARY', 'TEMP', 'PRIVILEGES',
- 'DATABASE', 'EXECUTE', 'FUNCTION', 'USAGE',
- 'LANGUAGE', 'SCHEMA', 'TABLESPACE' ],
- 'INSERT' : { 'INTO' : [ 'DEFAULT', 'VALUES',
- 'SELECT', 'DISTINCT', 'ON', 'AS',
- 'FROM', 'WHERE', [ 'GROUP', 'BY' ],
- 'HAVING', 'UNION', 'INTERSECT', 'EXCEPT',
- [ 'ORDER', 'BY' ],
- 'ASC', 'DESC', 'USING', 'LIMIT',
- 'OFFSET', [ 'FOR', 'UPDATE', 'OF' ],
- 'ONLY', 'NATURAL', 'USING', 'INNER',
- 'LEFT', 'OUTER', 'RIGHT', 'FULL',
- 'JOIN', 'CROSS', 'INTO',
- ]
- },
- 'LISTEN' : [],
- 'LOAD' : [],
- 'LOCK' : [ 'TABLE', 'IN', 'MODE', 'NOWAIT',
- [ 'ACCESS', 'SHARE' ], [ 'ROW', 'SHARE' ],
- [ 'ROW', 'EXCLUSIVE' ],
- [ 'SHARE', 'UPDATE', 'EXCLUSIVE' ],
- 'SHARE', [ 'SHARE', 'ROW', 'EXCLUSIVE' ],
- 'EXCLUSIVE', [ 'ACCESS', 'EXCLUSIVE' ] ],
- 'MOVE' : [ 'FROM', 'IN' ],
- 'NOTIFY' : [],
- 'PREPARE' : [ 'AS' ],
- 'REINDEX' : [ 'DATABASE', 'TABLE', 'TABLE', 'FORCE' ],
- 'RELEASE' : [ 'SAVEPOINT' ],
- 'RESET' : [ ],
- 'REVOKE' : [ [ 'GRANT', 'OPTION', 'FOR' ], 'SELECT', 'INSERT',
- 'UPDATE', 'DELETE', 'RULE', 'REFERENCES',
- 'TRIGGER', 'PRIVILEGES', 'ON', 'TABLE',
- 'FROM', 'GROUP', 'PUBLIC', 'CASCADE', 'RESTRICT',
- 'CREATE', 'TEMPORARY', 'TEMP', 'EXECUTE',
- 'USAGE', 'SCHEMA', 'LANGUAGE', 'TABLESPACE' ],
- 'ROLLBACK' : [ 'WORK', 'TRANSACTION', 'TO', 'SAVEPOINT' ],
- 'SAVEPOINT': [],
-
- 'SELECT' : [ 'SELECT', 'INTO', 'DISTINCT', 'ON', 'AS', 'FROM',
- 'WHERE', [ 'GROUP', 'BY' ], 'HAVING', 'UNION',
- 'INTERSECT', 'EXCEPT', [ 'ORDER', 'BY' ],
- 'ASC', 'DESC', 'USING', 'LIMIT', 'OFFSET',
- [ 'FOR', 'UPDATE', 'OF' ], 'ONLY', 'NATURAL',
- 'USING', 'INNER', 'LEFT', 'OUTER', 'RIGHT', 'FULL',
- 'JOIN', 'CROSS', 'INTO' ],
- 'SET' : [ 'SESSION', 'LOCAL', 'TO', 'DEFAULT', 'LOCAL',
- [ 'TIME', 'ZONE' ], 'CONSTRAINTS',
- 'DEFERRED', 'IMMEDIATE', 'AUTHORIZATION',
- 'TRANSACTION', 'CHARACTERISTICS',
- [ 'ISOLATION', 'LEVEL' ], 'SERIALIZABLE',
- [ 'REPEATABLE', 'READ' ], [ 'READ', 'COMMITTED' ],
- [ 'READ', 'UNCOMMITTED' ], [ 'READ', 'WRITE' ],
- [ 'READ', 'ONLY' ]
- ],
- 'SHOW' : [ ],
- 'START' : { 'TRANSACTION' : [ [ 'ISOLATION', 'LEVEL' ],
- 'SERIALIZABLE',
- [ 'REPEATABLE', 'READ' ],
- [ 'READ', 'COMMITTED' ],
- [ 'READ', 'UNCOMMITTED' ],
- [ 'READ', 'WRITE' ],
- [ 'READ', 'ONLY' ]
- ],
- },
- 'TRUNCATE' : [ 'TABLE' ],
- 'UNLISTEN' : [],
- 'UPDATE' : [ 'ONLY', 'SET', 'DEFAULT', 'FROM', 'WHERE' ],
- 'VACUUM' : [ 'FULL', 'FREEZE', 'VERBOSE', 'ANALYSE' ]
- }
-
-
-class Syntax:
- """Syntax highlight"""
-
- def __init__(self, buffer):
- self.lexical = pgw.Lexical.Lexical()
- self.buffer = buffer
- self.last = None
- # default colors
- self.tag = {}
- self.tag['font'] = self.buffer.create_tag("font")
- if (pgw.mswindows()):
- self.tag['font'].set_property('font', 'Courier New 10')
- else:
- self.tag['font'].set_property('family', 'monospace')
- self.tag['function'] = self.buffer.create_tag("function")
- self.tag['function'].set_property('foreground', '#009999')
- self.tag['dollarquote'] = self.buffer.create_tag("dollarquote")
- self.tag['dollarquote'].set_property('foreground', '#000000')
- self.tag['identifier'] = self.buffer.create_tag("identifier")
- self.tag['identifier'].set_property('foreground', '#000000')
- self.tag['keyword'] = self.buffer.create_tag("keyword")
- self.tag['keyword'].set_property('foreground', '#0000FF')
- self.tag['type'] = self.buffer.create_tag("type")
- self.tag['type'].set_property('foreground', '#009900')
- self.tag['string'] = self.buffer.create_tag("string")
- self.tag['string'].set_property('foreground', '#F700BF')
- self.tag['numeric_constant'] = self.buffer.create_tag("numeric_constant")
- self.tag['numeric_constant'].set_property('foreground', '#c53838')
- self.tag['special'] = self.buffer.create_tag("special")
- self.tag['special'].set_property('foreground', '#c53838')
- self.tag['comment'] = self.buffer.create_tag("comment")
- self.tag['comment'].set_property('foreground', '#999999')
- self.tag['comment2'] = self.buffer.create_tag("comment2")
- self.tag['comment2'].set_property('style', pango.STYLE_ITALIC)
- self.tag['operator'] = self.buffer.create_tag("operator")
- self.tag['operator'].set_property('foreground', '#555555')
- self.tag['psql'] = self.buffer.create_tag("psql")
- self.tag['psql'].set_property('background', '#d0d4df')
-
-
- def start_of_prev_statement(self, iter):
- """Find the first character of a statement"""
- if (self.last is not None):
- self.found = None
- for token in self.last:
- if (token.start_iter.compare(iter) >= 0):
- if (self.found is None):
- break
- return self.found.start_iter
- if (token.value == ';'):
- self.found = token
- if (self.found):
- return self.found.start_iter
- return iter.get_buffer().get_start_iter()
-
-
- def start_of_next_statement(self, iter):
- """Find the last character of a statement"""
- if (self.last is not None):
- self.found = None
- for token in reversed(self.last):
- if (token.end_iter.compare(iter) <= 0):
- if (self.found is None):
- break
- return self.found.end_iter
- if (token.value == ';'):
- self.found = token
- if (self.found):
- return self.found.end_iter
- return iter.get_buffer().get_end_iter()
-
-
- def text_inserted(self, buffer, iter, text, length):
- """Called by Gtk when text is inserted in the buffer:
- prepare 'start' and 'end' for text_changed()"""
- self.start = self.start_of_prev_statement(iter.copy()).get_offset()
- self.end = self.start_of_next_statement(iter.copy()).get_offset() + length
-
-
- def text_deleted(self, buffer, start, end):
- """Called by Gtk when text is deleted from the buffer:
- prepare 'start' and 'end' for text_changed()"""
- self.start = self.start_of_prev_statement(start.copy()).get_offset()
- self.end = self.start_of_next_statement(end.copy()).get_offset()
-
-
- def text_changed(self, buffer):
- """Called by Gtk when (after) text is deleted or inserted
- in the buffer. Uses 'start' and 'end' prepared in text_inserted
- and text_deleted then run the analyse"""
- start = self.buffer.get_iter_at_offset(self.start)
- end = self.buffer.get_iter_at_offset(self.end)
- self.analyse(start, end)
-
-
- def refresh(self):
- """Used when you want to manually refresh the syntax highlight
- of the whole buffer"""
- start = self.buffer.get_start_iter()
- end = self.buffer.get_end_iter()
- self.analyse(start, end)
-
-
- def analyse(self, start, end):
- """Run the lexical and syntaxical analysers then
- apply the syntax highlight to the buffer"""
- self.tokens = self.lexical.analyse(self.buffer, start, end)
- self.syntaxical_analyser()
- self.buffer.remove_all_tags(start, end)
- self.buffer.apply_tag(self.tag['font'], start, end)
- self.last = self.tokens
- for token in self.tokens:
- self.buffer.apply_tag(self.tag[token.token], \
- token.start_iter, \
- token.end_iter)
- if (token.token == 'comment'):
- self.buffer.apply_tag(self.tag['comment2'], \
- token.start_iter, \
- token.end_iter)
-
-
- def syntaxical_analyser(self):
- """Find keywords"""
- tokens = self.tokens
- self.tokens = []
- try:
- while (len(tokens) > 0):
- token = tokens.pop(0)
- # only statements, other tokens
- # are analysed below or already founds in
- # the lexical analyser
- if (token.value in STATEMENTS):
- token.token = 'keyword'
- self.tokens.append(token)
- statement = token.value
- token = tokens.pop(0)
- # get the list containing the
- # keywords for this statement
- try:
- keywords = KEYWORDS[statement]
- except KeyError:
- continue
- if (type(keywords) is dict):
- try:
- keywords = keywords[token.value]
- token.token = 'keyword'
- self.tokens.append(token)
- token = tokens.pop(0)
- except KeyError:
- pass
- self.tokens.append(token)
- # identify each token inside the statement
- while (token.value != ';'):
- # only identifiers, other tokens are
- # analysed in the lexical analyser
- if (token.token == 'identifier'):
- # special constants
- if (token.value in SPECIALS):
- token.token = 'special'
- # buit-in data types
- elif (token.value in TYPES):
- # TODO : manage composed types names
- token.token = 'type'
- # names operators
- elif (token.value in OPERATORS2):
- token.token = 'operator'
- # built-in functions
- elif (token.value in BUILTINS):
- next = tokens.pop(0)
- if (next.value == '('):
- token.token = 'function'
- self.tokens.append(next)
- else:
- tokens.insert(0, next)
- # built-in function that can be used without ()
- if (token.value in BUILTINS2):
- token.token = 'function'
- # everything else : keywords or indentifiers
- else:
- for keyword in keywords:
- if (type(keyword) is list):
- # TODO : write the right code here
- # to manage composed keywords
- if (token.value in keyword):
- token.token = 'keyword'
- elif (token.value == keyword):
- token.token = 'keyword'
- self.tokens.append(token)
- token = tokens.pop(0)
- self.tokens.append(token)
- except IndexError:
- pass
+++ /dev/null
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: UI.py,v 1.26 2008/03/12 20:26:23 hmichelon Exp $
-#
-import os
-import ConfigParser
-import pygtk
-import gtk
-
-import pgw
-import pgw.Syntax
-import pgw.Undo
-
-PGW_CONNECT = 'pgw-connect'
-PGW_CONNECTNOW = 'pgw-connectnow'
-PGW_DISCONNECT = 'pwg-disconnect'
-PGW_SELECTALL = 'pwg-selectall'
-PGW_ENCODING = 'pwg-encoding'
-PGW_ABOUT = 'pwg-about'
-
-
-class UI:
- """UI Construction and management"""
-
- def __init__(self, app, app_name, app_version, pixmap_path):
- self.pixmap_path = pixmap_path
- self.app_version = app_version
- self.register_stock_items()
- self.file_dialog_path = None # last used path
- self.file_dialog_filename = None # last used filename
- self.define_ui(app)
-
- self.wndmain = gtk.Window(gtk.WINDOW_TOPLEVEL)
- self.wndmain.set_title(_('PgWorksheet - PostgreSQL SQL Tool'))
- self.wndmain.set_icon_from_file(os.path.join(pixmap_path,
- "pgworksheet-32.png"))
- self.wndmain.add_accel_group(self.accelgroup)
- self.wndmain.set_default_size(640, 480)
- self.wndmain.connect('window-state-event', self.on_wndmain_state)
- self.wndmain.connect('configure-event', self.on_wndmain_configure)
- self.wndmain.connect('destroy', app.on_wndmain_destroy)
- self.wndmain.connect('delete_event', app.on_wndmain_delete)
-
- hbox = gtk.VBox(False)
- hbox.pack_start(self.uimanager.get_widget('/MenuBar'), False)
- hbox.pack_start(self.uimanager.get_widget('/ToolBar'), False)
- hbox.pack_start(self.vmain, True, True)
- hbox.pack_start(self.statusbar, False, False)
- self.wndmain.add(hbox)
-
- self.load_user_parameters()
- self.wndmain.show_all()
-
-
- def load_user_parameters(self):
- cp = ConfigParser.ConfigParser()
- try:
- cp.readfp(open(pgw.get_config_path(), 'r'))
- maximized = 0
- try:
- maximized = int(cp.get("window", "maximized"))
- except:
- pass
- if (maximized):
- self.wndmain.maximize()
- else:
- width, height = self.wndmain.get_size()
- try:
- width = int(cp.get("window", "width"))
- except:
- pass
- try:
- height = int(cp.get("window", "height"))
- except:
- pass
- self.wndmain.resize(width, height)
- x, y = self.wndmain.get_position()
- try:
- x = int(cp.get("window", "x"))
- except:
- pass
- try:
- y = int(cp.get("window", "y"))
- except:
- pass
- self.wndmain.move(x, y)
- try:
- self.vmain.set_position(int(cp.get("window", "divider")))
- except:
- pass
- except IOError:
- pass
- return cp
-
-
- def message_dialog(self, msg, type, buttons = gtk.BUTTONS_OK):
- dialog = gtk.MessageDialog(self.wndmain,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- type, buttons, msg)
- result = dialog.run()
- dialog.destroy()
- return result
-
-
- def message_box(self, msg):
- self.message_dialog(msg, gtk.MESSAGE_INFO)
-
-
- def error_box(self, msg):
- self.message_dialog(msg, gtk.MESSAGE_ERROR)
-
-
- def yesno_box(self, msg):
- return self.message_dialog(msg, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO)
-
-
- def file_dialog(self, msg,
- action = gtk.FILE_CHOOSER_ACTION_OPEN,
- button = gtk.STOCK_OPEN):
- fb = gtk.FileChooserDialog(msg, self.wndmain,
- action,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- button, gtk.RESPONSE_OK))
- if (self.file_dialog_path is not None):
- fb.set_current_folder_uri(self.file_dialog_path)
- if (self.file_dialog_filename is not None):
- fb.set_filename(self.file_dialog_filename)
- fb.show_all()
- result = fb.run()
- if (result == gtk.RESPONSE_OK):
- filename = fb.get_filename()
- else:
- filename = None
- self.file_dialog_path = fb.get_current_folder_uri()
- self.file_dialog_filename = fb.get_filename()
- fb.destroy()
- return filename
-
-
- def register_stock_items(self):
- gtk.stock_add([
- (PGW_CONNECT, _('_Connect Server...'), gtk.gdk.CONTROL_MASK,
- gtk.gdk.keyval_from_name('N'), None),
- (PGW_DISCONNECT, _('_Disconnect'), 0, -1, None),
- (PGW_SELECTALL, _('Select _All'), gtk.gdk.CONTROL_MASK,
- gtk.gdk.keyval_from_name('A'), None),
- (PGW_ENCODING, _('Character _Encoding'), 0, -1, None),
- (PGW_ABOUT, _('_About...'), 0, -1, None),
- (PGW_CONNECTNOW, _('_Connect'), 0, -1, None),
- ])
- self.factory = gtk.IconFactory()
- self.factory.add_default()
- self.factory.add(PGW_CONNECTNOW, gtk.IconSet(gtk.gdk.pixbuf_new_from_file(
- os.path.join(self.pixmap_path, "connect.png"))))
- self.factory.add(PGW_CONNECT, gtk.IconSet(gtk.gdk.pixbuf_new_from_file(
- os.path.join(self.pixmap_path, "connect.png"))))
- self.factory.add(PGW_DISCONNECT, gtk.IconSet(gtk.gdk.pixbuf_new_from_file(
- os.path.join(self.pixmap_path, "disconnect.png"))))
- self.factory.add(PGW_ABOUT, gtk.IconSet(gtk.gdk.pixbuf_new_from_file(
- os.path.join(self.pixmap_path, "about.png"))))
-
-
- def define_ui(self, app):
- self.ui = '''<ui>
- <menubar name="MenuBar">
- <menu action="File">
- <menuitem action="Connect"/>
- <menuitem action="Disconnect"/>
- <separator/>
- <menuitem action="Open SQL"/>
- <menuitem action="Save SQL"/>
- <menuitem action="Save Results"/>
- <menuitem action="Save All Results"/>
- <separator/>
- <menuitem action="Quit"/>
- </menu>
- <menu action="Edit">
- <menuitem action="Undo"/>
- <menuitem action="Redo"/>
- <separator/>
- <menuitem action="Cut"/>
- <menuitem action="Copy"/>
- <menuitem action="Paste"/>
- <separator/>
- <menuitem action="Select All"/>
- </menu>
- <menu action="Tools">
- <menuitem action="Run SQL"/>
- </menu>
- <menu action="History">
- <menuitem action="Prev Statement"/>
- <menuitem action="Next Statement"/>
- </menu>
- <menu action="Help">
- <menuitem action="About"/>
- </menu>
- </menubar>
- <toolbar name="ToolBar">
- <toolitem action="Connect"/>
- <toolitem action="Run SQL"/>
- <toolitem action="Prev Statement"/>
- <toolitem action="Next Statement"/>
- <separator/>
- <toolitem action="Open SQL"/>
- <toolitem action="Save SQL"/>
- <separator/>
- <toolitem action="Cut"/>
- <toolitem action="Copy"/>
- <toolitem action="Paste"/>
- </toolbar>
- <accelerator action="Run SQL 2"/>
- <accelerator action="Run SQL 3"/>
- </ui>'''
-
- self.undo = pgw.Undo.Undo()
- self.uimanager = gtk.UIManager()
- self.accelgroup = self.uimanager.get_accel_group()
- self.actiongroup = gtk.ActionGroup('UIManagerMenuBar')
- self.actiongroup.add_actions([
- ('File', None, _('_File')),
- ('Connect', PGW_CONNECT, None, None,
- _('Connect to a database'), app.on_menu_connect),
- ('Disconnect', PGW_DISCONNECT, None, '',
- _('Disconnect from the database'), app.on_menu_disconnect),
- ('Open SQL', gtk.STOCK_OPEN, _('_Open SQL queries...'), None,
- _('Load SQL queries from a file'), app.on_menu_opensql),
- ('Save SQL', gtk.STOCK_SAVE, _('_Save SQL queries...'), None,
- _('Save SQL queries to a file'), app.on_menu_savesql),
- ('Save Results', gtk.STOCK_SAVE, _('Save _Results...'), '<control>R',
- _('Save the results to a file'), app.on_menu_saveresults),
- ('Save All Results', gtk.STOCK_SAVE, _('Save _All Results...'),
- '<control><shift>R',
- _('Save all the results to a file'), app.on_menu_saveallresults),
- ('Quit', gtk.STOCK_QUIT, None, None,
- _('Quit the Program'), app.on_wndmain_destroy),
- ('Edit', None, _('_Edit')),
- ('Undo', gtk.STOCK_UNDO, None, '<control>Z',
- _('Undo the last action'), lambda w: self.undo.undo()),
- ('Redo', gtk.STOCK_REDO, None, '<shift><control>Z',
- _('Redo the last action'), lambda w: self.undo.redo()),
- ('Cut', gtk.STOCK_CUT, None, None,
- _('Cut selection to the clipboard'), app.on_menu_cut),
- ('Copy', gtk.STOCK_COPY, None, None,
- _('Copy selection to the clipboard'), app.on_menu_copy),
- ('Paste', gtk.STOCK_PASTE, None, None,
- _('Paste the content of the clipboard'), app.on_menu_paste),
- ('Select All', PGW_SELECTALL, None, None,
- _('Select all the text'), app.on_menu_selectall),
- ('View', None, _('_View')),
- ('Encoding', PGW_ENCODING, None, None, _('Select the character \
- encoding for display'), app.on_menu_selectall),
- ('Tools', None, _('_Tools')),
- ('Run SQL', gtk.STOCK_EXECUTE, None, "<control>Return",
- _('Execute the content of the SQL buffer'), app.on_menu_runsql),
- ('History', None, _('_History')),
- ('Prev Statement', gtk.STOCK_GO_BACK, _('_Previous statements'),
- "<control>Page_Down",
- _('Go backward in the executed statements history'),
- app.on_menu_prevsql),
- ('Next Statement', gtk.STOCK_GO_FORWARD, _('_Next statements'),
- "<control>Page_Up",
- _('Go forward in the executed statements history'),
- app.on_menu_nextsql),
- ('Help', None, _('_Help')),
- ('About', PGW_ABOUT, None, '',
- _('More information about this application'), app.on_menu_about),
- ('Run SQL 2', gtk.STOCK_EXECUTE, None, "F9", None, app.on_menu_runsql),
- ('Run SQL 3', gtk.STOCK_EXECUTE, None, "F5", None, app.on_menu_runsql),
- ])
- self.uimanager.insert_action_group(self.actiongroup, 0)
- self.uimanager.add_ui_from_string(self.ui)
- self.uimanager.get_widget('/MenuBar/Help').\
- set_right_justified(True)
- self.enable_disconnect(False)
- self.enable_runsql(False)
- self.enable_prevsql(False)
- self.enable_nextsql(False)
- self.enable_saveresult(False)
- self.create_vpaned(app)
- self.create_statusbar()
- self.uimanager.get_widget('/ToolBar').set_style(gtk.TOOLBAR_ICONS)
-
-
- def create_vpaned(self, app):
- self.vmain = gtk.VPaned()
- self.vmain.connect("event", self.on_vpaned_accept)
- scroll = gtk.ScrolledWindow()
- scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scroll.set_property("height-request", 150)
- scroll.set_shadow_type(gtk.SHADOW_NONE)
- self.sqlbuffer = gtk.TextBuffer()
- self.sqlbuffer.connect('delete-range', self.undo.text_deleted)
- self.sqlbuffer.connect('insert-text', self.undo.text_inserted)
- self.syntax = pgw.Syntax.Syntax(self.sqlbuffer)
- self.buffer_handlers = []
- self.buffer_handlers.append(self.sqlbuffer.connect('delete-range',
- self.syntax.text_deleted))
- self.buffer_handlers.append(self.sqlbuffer.connect('insert-text',
- self.syntax.text_inserted))
- self.buffer_handlers.append(self.sqlbuffer.connect('changed',
- self.syntax.text_changed))
- self.buffer_handlers.append(self.sqlbuffer.connect('changed',
- app.on_text_change))
- self.sqlview = gtk.TextView(self.sqlbuffer)
- self.sqlview.connect('focus-in-event', app.on_sqlview_focus_in)
- self.sqlview.connect('focus-out-event', app.on_sqlview_focus_out)
- self.sqlview.connect('key-press-event', app.on_sqlview_keypress)
- scroll.add(self.sqlview)
- self.vmain.add1(scroll)
- self.resulttab = gtk.Notebook()
- self.resulttab.set_tab_pos(gtk.POS_BOTTOM)
- self.resulttab.set_scrollable(True)
- resultview = gtk.Viewport()
- resultview.set_shadow_type(gtk.SHADOW_IN)
- resultview.add(self.resulttab)
- self.vmain.add2(resultview)
-
-
- def create_statusbar(self):
- self.statusbar = gtk.Statusbar()
- self.statusbar.set_border_width(0)
-
- f = self.statusbar.get_children()[0]
- f.set_shadow_type(gtk.SHADOW_IN)
- self.status_result = f.get_children()
- self.status_result = self.status_result[0]
- self.statusbar.set_child_packing(f, True, True, 0, gtk.PACK_START)
-
- self.status_connect = gtk.Label();
- self.status_connect.set_justify(gtk.JUSTIFY_LEFT)
- f = gtk.Frame()
- f.set_shadow_type(gtk.SHADOW_IN)
- f.add(self.status_connect)
- self.statusbar.pack_start(f, False, False)
-
- self.status_version = gtk.Label();
- f = gtk.Frame()
- f.set_shadow_type(gtk.SHADOW_IN)
- f.add(self.status_version)
- self.statusbar.pack_start(f, False, False)
-
- f = gtk.Label(' ')
- self.statusbar.pack_start(f, False, False)
-
-
- def status(self, connect, version):
- """Update the status bar text"""
- self.status_connect.set_markup(" " + connect + " ")
- self.status_version.set_markup(" " + version + " ")
-
-
- def setfocus_sqlbuffer(self):
- self.sqlview.grab_focus()
-
-
- def enable_disconnect(self, state = True):
- self.uimanager.get_widget('/MenuBar/File/Disconnect').\
- set_sensitive(state)
-
-
- def enable_saveresult(self, state = True):
- self.uimanager.get_widget('/MenuBar/File/Save All Results').\
- set_sensitive(state)
- self.uimanager.get_widget('/MenuBar/File/Save Results').\
- set_sensitive(state)
-
-
- def enable_runsql(self, state = True):
- self.uimanager.get_widget('/MenuBar/Tools/Run SQL').\
- set_sensitive(state)
- self.uimanager.get_widget('/ToolBar/Run SQL').\
- set_sensitive(state)
-
-
- def enable_cut(self, state = True):
- self.uimanager.get_widget('/MenuBar/Edit/Cut').\
- set_sensitive(state)
- self.uimanager.get_widget('/ToolBar/Cut').\
- set_sensitive(state)
-
-
- def enable_copy(self, state = True):
- self.uimanager.get_widget('/MenuBar/Edit/Copy').\
- set_sensitive(state)
- self.uimanager.get_widget('/ToolBar/Copy').\
- set_sensitive(state)
-
-
- def enable_selectall(self, state = True):
- self.uimanager.get_widget('/MenuBar/Edit/Select All').\
- set_sensitive(state)
-
-
- def enable_paste(self, state = True):
- self.uimanager.get_widget('/MenuBar/Edit/Paste').\
- set_sensitive(state)
- self.uimanager.get_widget('/ToolBar/Paste').\
- set_sensitive(state)
-
-
- def enable_prevsql(self, state = True):
- self.uimanager.get_widget('/MenuBar/History/Prev Statement').\
- set_sensitive(state)
- self.uimanager.get_widget('/ToolBar/Prev Statement').\
- set_sensitive(state)
-
-
- def enable_nextsql(self, state = True):
- self.uimanager.get_widget('/MenuBar/History/Next Statement').\
- set_sensitive(state)
- self.uimanager.get_widget('/ToolBar/Next Statement').\
- set_sensitive(state)
-
-
- def get_text(self, buffer):
- """Return the text of a widget"""
- c1 = buffer.get_start_iter()
- c2 = buffer.get_end_iter()
- return buffer.get_slice(c1, c2, True)
-
-
- def get_sqlbuffer_text(self):
- return self.get_text(self.sqlbuffer)
-
-
- def set_sqlbuffer_text(self, text):
- self.sqlbuffer.set_text(text)
-
-
- def on_connect_event(self, widget, event):
- if ((not self.entry_password.is_focus()) and
- self.viewconn.is_focus()):
- self.entry_password.grab_focus()
-
-
- def connect_dialog(self, app, host, port, user, database, overwrite_entry):
- dlg = gtk.Dialog(_('Database connection'),
- self.wndmain,
- gtk.DIALOG_MODAL or gtk.DIALOG_DESTROY_WITH_PARENT,
- ((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- PGW_CONNECTNOW, gtk.RESPONSE_OK)))
- dlg.set_resizable(False)
-
- mbox = gtk.HBox()
- box = gtk.Frame()
- self.storeconn = gtk.ListStore(str, str)
- self.viewconn = gtk.TreeView(self.storeconn)
- cell = gtk.CellRendererText()
- column = gtk.TreeViewColumn(_('5 Previous connections'))
- column.pack_start(cell, True)
- column.add_attribute(cell, 'text', 0)
- column.set_sort_column_id(0)
- self.viewconn.append_column(column)
- self.viewconn.connect('cursor-changed', app.on_dlgconnect_change)
- box.add(self.viewconn)
- mbox.pack_start(box, True, True)
-
- hbox = gtk.HBox(False)
- hbox.set_border_width(10)
- hbox.set_spacing(10)
-
- box = gtk.VBox(True)
- box.set_spacing(10)
- lbl = gtk.Label(_('Host'))
- lbl.set_alignment(0, 0.1)
- box.pack_start(lbl, True, True)
- lbl = gtk.Label(_('Port'))
- lbl.set_alignment(0, 0.1)
- box.pack_start(lbl, True, True)
- lbl = gtk.Label(_('User'))
- lbl.set_alignment(0, 0.1)
- box.pack_start(lbl, True, True)
- lbl = gtk.Label(_('Password'))
- lbl.set_alignment(0, 0.1)
- box.pack_start(lbl, True, True)
- lbl = gtk.Label(_('Database'))
- lbl.set_alignment(0, 0.1)
- box.pack_start(lbl, True, True)
- hbox.pack_start(box, False, False)
-
- box = gtk.VBox(False)
- box.set_spacing(10)
- self.entry_host = gtk.Entry()
- self.entry_host.connect('activate', lambda w: dlg.response(gtk.RESPONSE_OK))
- box.pack_start(self.entry_host, True, True)
- self.entry_port = gtk.Entry()
- self.entry_port.connect('activate', lambda w: dlg.response(gtk.RESPONSE_OK))
- box.pack_start(self.entry_port, True, True)
- self.entry_user = gtk.Entry()
- self.entry_user.connect('activate', lambda w: dlg.response(gtk.RESPONSE_OK))
- box.pack_start(self.entry_user, True, True)
- self.entry_password = gtk.Entry()
- self.entry_password.set_visibility(False)
- self.entry_password.connect('activate', lambda w: dlg.response(gtk.RESPONSE_OK))
- box.pack_start(self.entry_password, True, True)
- self.entry_database = gtk.Entry()
- self.entry_database.connect('activate', lambda w: dlg.response(gtk.RESPONSE_OK))
- box.pack_start(self.entry_database, True, True)
- hbox.pack_start(box, True, True)
-
- mbox.pack_start(hbox, True, True)
-
- dlg.vbox.pack_start(mbox, True, True, 0)
- app.on_dlgconnect_map(None)
- if (overwrite_entry or (not self.storeconn.iter_n_children(None))):
- self.entry_host.set_text(host)
- self.entry_port.set_text(str(port))
- self.entry_user.set_text(user)
- self.entry_database.set_text(database)
- dlg.set_default_response(gtk.RESPONSE_OK)
- dlg.connect('event-after', self.on_connect_event)
- dlg.vbox.show_all()
- self.entry_password.grab_focus()
- result = None
- if (dlg.run() == gtk.RESPONSE_OK):
- result = (self.entry_host.get_text(),
- self.entry_port.get_text(),
- self.entry_user.get_text(),
- self.entry_password.get_text(),
- self.entry_database.get_text())
- dlg.destroy()
- return result
-
-
- def write_config(self, cp):
- try:
- cp.write(open(pgw.get_config_path(), 'w'))
- except IOError:
- pass
-
-
- def on_vpaned_accept(self, widget, event):
- cp = ConfigParser.ConfigParser()
- try:
- cp.readfp(open(pgw.get_config_path(), 'r'))
- except IOError:
- pass
- if (not cp.has_section("window")):
- cp.add_section("window")
- cp.set("window", "divider", str(widget.get_position()))
- self.write_config(cp)
-
-
- def on_wndmain_state(self, widget, event):
- cp = ConfigParser.ConfigParser()
- try:
- cp.readfp(open(pgw.get_config_path(), 'r'))
- except IOError:
- pass
- if (not cp.has_section("window")):
- cp.add_section("window")
- if (event.new_window_state == gtk.gdk.WINDOW_STATE_MAXIMIZED):
- cp.set("window", "maximized", "1")
- else:
- cp.set("window", "maximized", "0")
- self.write_config(cp)
-
-
- def on_wndmain_configure(self, widget, event):
- cp = ConfigParser.ConfigParser()
- try:
- cp.readfp(open(pgw.get_config_path(), 'r'))
- except IOError:
- pass
- if (not cp.has_section("window")):
- cp.add_section("window")
- x, y = widget.get_position()
- cp.set("window", "x", x)
- cp.set("window", "y", y)
- width, height = widget.get_size()
- cp.set("window", "width", width)
- cp.set("window", "height", height)
- self.write_config(cp)
-
-
- def about_dialog(self):
- dlg = gtk.Dialog(_('About PgWorksheet'),
- self.wndmain,
- gtk.DIALOG_MODAL or gtk.DIALOG_DESTROY_WITH_PARENT,
- ((gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)))
- nbk = gtk.Notebook()
-
- box = gtk.VBox(False)
-
- lbl = gtk.Label()
- lbl.set_markup('''
-<b>PgWorksheet - PostgreSQL Front End</b>
-Version ''' + self.app_version + '''
-<u>http://pgworksheet.projects.postgresql.org/</u>
-
-Copyright © 2004-2006, Henri Michelon and CML
-<u>http://www.e-cml.org/</u>
- ''')
- lbl.set_justify(gtk.JUSTIFY_CENTER)
- lbl.set_padding(10, 10)
- box.pack_start(lbl, True)
- lbl = gtk.Label()
- lbl.set_markup('''
-<b>Internationalization:</b>
-French : Henri Michelon <
[email protected]>
-Japanese : Tadashi Jokagi <
[email protected]>
- ''')
- lbl.set_padding(10, 0)
- box.pack_start(lbl, True)
- pix = gtk.Image()
- pix.set_from_file(os.path.join(self.pixmap_path, "pgworksheet.png"))
- pix.set_padding(10, 10)
- box.pack_start(pix, False)
- nbk.append_page(box, gtk.Label(_('About')))
-
- txt = gtk.TextBuffer()
- txt.set_text('''
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, 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 or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-\f
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-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 give any other recipients of the Program a copy of this License
-along with the Program.
-
-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 Program or any portion
-of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-\f
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-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 Program, 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 Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) 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; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, 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 executable. However, as a
-special exception, the source code 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.
-
-If distribution of executable or 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 counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-\f
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program 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.
-
- 5. 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 Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program 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 to
-this License.
-
- 7. 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 Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program 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 Program.
-
-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.
-\f
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program 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.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the 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 Program
-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 Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, 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
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-\f
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
- ''')
- scroll = gtk.ScrolledWindow()
- scroll.add(gtk.TextView(txt))
- nbk.append_page(scroll, gtk.Label(_('License')))
-
- dlg.vbox.pack_start(nbk, True, True, 0)
- nbk.show_all()
- dlg.run()
- dlg.destroy()
+++ /dev/null
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: Undo.py,v 1.4 2008/03/12 20:26:23 hmichelon Exp $
-#
-
-# maxium size of the undo stack
-UNDO_MAX = 1000
-
-
-class UndoAction:
- """A user action : insert or delete"""
-
- def __init__(self, buffer, type, start, end, value):
- self.buffer = buffer
- self.type = type
- self.start = start
- self.end = end
- self.value = value
-
-
-class Undo:
- """Records user actions and apply or revert them"""
-
- def __init__(self):
- self.undo_stack = []
- self.redo_stack = []
- # locking is used to avoid recording
- # our own undo/redo actions
- self.lock = False
-
-
- def reset(self):
- """empty the stacks to restart the undo action"""
- self.undo_stack = []
- self.redo_stack = []
-
-
- def text_inserted(self, buffer, iter, text, length):
- """Called by Gtk when text is inserted in a buffer"""
- if (not self.lock):
- if (len(self.undo_stack) >= UNDO_MAX):
- self.undo_stack.pop(0)
- self.undo_stack.append(UndoAction(buffer, "insert",
- iter.get_offset(), iter.get_offset() + length,
- text))
-
-
- def text_deleted(self, buffer, start, end):
- """Called by Gtk when text is deleted from a buffer"""
- if (not self.lock):
- if (len(self.undo_stack) >= UNDO_MAX):
- self.undo_stack.pop(0)
- self.undo_stack.append(UndoAction(buffer, "delete",
- start.get_offset(), end.get_offset(),
- buffer.get_text(start, end)))
-
-
- def undo(self):
- """Revert the last action"""
- if (len(self.undo_stack) == 0):
- return
- self.lock = True
- action = self.undo_stack.pop()
- if (action.type == "insert"):
- action.buffer.delete(action.buffer.get_iter_at_offset(action.start),
- action.buffer.get_iter_at_offset(action.end))
- elif (action.type == "delete"):
- action.buffer.insert(action.buffer.get_iter_at_offset(action.start),
- action.value)
- self.redo_stack.append(action)
- self.lock = False
-
-
- def redo(self):
- """Apply the last reverted action"""
- if (len(self.redo_stack) == 0):
- return
- self.lock = True
- action = self.redo_stack.pop()
- if (action.type == "insert"):
- action.buffer.insert(action.buffer.get_iter_at_offset(action.start),
- action.value)
- elif (action.type == "delete"):
- action.buffer.delete(action.buffer.get_iter_at_offset(action.start),
- action.buffer.get_iter_at_offset(action.end))
- self.undo_stack.append(action)
- self.lock = False
+++ /dev/null
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: __init__.py,v 1.10 2008/03/12 20:26:23 hmichelon Exp $
-#
-import sys
-import os
-import string
-
-
-def mswindows():
- """Return TRUE is we run under Microsoft Windows"""
- return sys.platform == "win32"
-
-
-def get_user_encoding():
- if (mswindows()):
- return "ISO-8859-1" #"UTF-8"
- try:
- enc = os.environ['MM_CHARSET']
- return enc
- except KeyError:
- try:
- enc = os.environ['LANG']
- except KeyError:
- try:
- enc = os.environ['LC_LANG']
- except KeyError:
- try:
- enc = os.environ['LC_ALL']
- except KeyError:
- return "ISO8859-1"
- parts = string.split(enc, '.')
- if (len(parts) > 1) :
- return parts[1]
- return "ISO8859-1"
-
-
-def set_proportional(buffer):
- """Change the font of a widget to proportional"""
- tagname = 'font-' + str(buffer)
- try:
- font = buffer.create_tag(tagname)
- if (mswindows()):
- font.set_property('font', 'Courier New 10')
- else:
- font.set_property('family', 'monospace')
- buffer.apply_tag(font, buffer.get_start_iter(), buffer.get_end_iter())
- except TypeError: # tag already exists
- buffer.apply_tag_by_name(tagname, buffer.get_start_iter(), buffer.get_end_iter())
-
-
-def get_user_configdir():
- """Return the directory where the configuration file is stored"""
- if (mswindows()):
- try:
- return os.environ['USERPROFILE']
- except KeyError:
- try:
- return os.environ['ALLUSERSPROFILE']
- except KeyError:
- return os.environ['SYSTEMROOT']
- else:
- return os.environ['HOME']
-
-
-def get_config_path():
- return os.path.join(get_user_configdir(), '.pgworksheet')
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: latin-1; -*-
-#
-# PgWorksheet - PostgreSQL Front End
-# http://pgworksheet.projects.postgresql.org/
-#
-# Copyright © 2004-2008 Henri Michelon & CML http://www.e-cml.org/
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program 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 General Public License for more details (read LICENSE.txt).
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# $Id: pgworksheet,v 1.94 2008/03/13 11:05:32 hmichelon Exp $
-#
-import os
-import sys
-# Application parameters
-app_name = 'pgworksheet'
-app_version = '1.9'
-# Default pixmap path
-pixmap_path = '@PIXMAP_PATH@'
-# Find current pixmap path
-if (not os.access(os.path.join(pixmap_path, 'pgworksheet-32.png'), os.F_OK)):
- pixmap_path = os.path.join(sys.prefix, 'share/pixmaps/pgworksheet')
-if (not os.access(os.path.join(pixmap_path, 'pgworksheet-32.png'), os.F_OK)):
- pixmap_path = os.path.join(os.path.dirname(sys.argv[0]), 'pixmaps/pgworksheet')
-# Find current locale path
-locale_path = '@LOCALE_PATH@'
-if (not os.access(os.path.join(locale_path, 'fr/LC_MESSAGES/pgworksheet.mo'), os.F_OK)):
- locale_path = os.path.join(sys.prefix, 'share/locale')
-if (not os.access(os.path.join(locale_path, 'fr/LC_MESSAGES/pgworksheet.mo'), os.F_OK)):
- locale_path = os.path.join(os.path.dirname(sys.argv[0]), 'locale')
-
-import string
-import gettext
-import locale
-
-# Initialize I18N
-if sys.platform == 'win32':
- try:
- # try to get the default language
- lang = gettext.translation(app_name, locale_path,
- [ locale.getdefaultlocale()[0] ])
- lang.install()
- except IOError:
- # fallback to the default method
- gettext.bindtextdomain(app_name, locale_path)
- gettext.textdomain(app_name)
- gettext.install(app_name, locale_path, unicode=1)
-else:
- gettext.bindtextdomain(app_name, locale_path)
- gettext.textdomain(app_name)
- gettext.install(app_name, locale_path, unicode=1)
-
-
-if sys.platform == 'win32':
- # win32 platform, add the "lib" folder to the system path
- os.environ['PATH'] = "gtk/bin;gtk/lib;" + os.environ['PATH']
- import pygtk
- # search for the postgresql installation
- import _winreg
- reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
- try:
- keyname = "SOFTWARE\PostgreSQL\Installations"
- key = _winreg.OpenKey(reg, keyname)
- keyname = keyname + "\\" + _winreg.EnumKey(key, 0);
- _winreg.CloseKey(key)
- key = _winreg.OpenKey(reg, keyname)
- val, typ = _winreg.QueryValueEx(key, "Base Directory")
- _winreg.CloseKey(key)
- os.environ['PATH'] = val + "\\bin;" + os.environ['PATH']
- except WindowsError:
- msg = _("Please install PostgreSQL.\n\nDownload it at http://www.postgresql.org/.\n\nNote : if you use PgWorksheet to connect to remote databases only, you don't need to install PostgreSQL as a service.")
- print(msg)
- import gtk
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
- result = dialog.run()
- dialog.destroy()
- sys.exit(0)
-else:
- import pygtk
- # not win32, ensure version 2.0 of pygtk is imported
- pygtk.require('2.0')
-
-import gtk
-import ConfigParser
-
-import pgw
-import pgw.UI
-import pgw.RunSQL
-import pgw.Execute
-import pgw.DBConnection
-
-# maximum entries in the SQL queries history
-PGW_MAX_HISTORY = 100
-# maximum entries in the connection parameters history
-PGW_MAX_CONNECTION_HISTORY = 5
-
-
-class PgWorksheet:
-
- def __init__(self, app_name, app_version, pixmap_path):
- # Build UI
- self.ui = pgw.UI.UI(self, app_name, app_version, pixmap_path)
- # Default connection state : not connected
- self.db = None
- self.disconnect()
- # Initialize prev/next query lifos for history
- self.prev_statements = []
- self.next_statements = []
- self.load_history()
- self.prev_saved = 1
- # Display connection dialog on startup
- self.on_menu_connect(None)
- # Start application
- gtk.main()
-
-
- def add_prevstatement(self, sql):
- """Add a query to the previous queries lifo"""
- # do not add the same query two times
- if (len(self.prev_statements) > 0):
- prev = self.prev_statements[len(self.prev_statements)-1]
- if (prev == sql): return
- # add the query to the lifo
- self.prev_statements.append(sql)
- self.ui.enable_prevsql(len(self.prev_statements) > 0)
-
-
- def get_history_path(self):
- """Returns the path to the configuration file"""
- return os.path.join(pgw.get_user_configdir(), '.pgworksheet_history');
-
-
- def save_history(self):
- """Save the history in a text file"""
- try:
- fd = open(self.get_history_path(), 'w')
- self.add_prevstatement(self.ui.get_sqlbuffer_text())
- for sql in self.prev_statements:
- fd.write("\n#$#\n")
- fd.write(string.rstrip(sql))
- for sql in self.next_statements:
- fd.write("\n#$#\n")
- fd.write(string.rstrip(sql))
- fd.write("\n#$#\n")
- fd.close()
- except IOError:
- pass
-
-
- def load_history(self):
- """Load the history from a text file"""
- try:
- fd = open(self.get_history_path(), 'r')
- sql = ''
- count = 0
- for line in fd:
- line = string.rstrip(line)
- if (line == '') :
- continue
- try:
- line = unicode(line, 'UTF-8')
- except UnicodeDecodeError:
- try:
- line = unicode(line, pgw.get_user_encoding())
- except UnicodeDecodeError:
- pass
- if (line == '#$#'):
- if (len(sql) > 0):
- self.prev_statements.append(sql)
- count = count + 1
- sql = ''
- continue
- sql += line
- sql += '\n'
- if (len(sql) > 0):
- self.prev_statements.append(sql)
- fd.close()
- if (count > PGW_MAX_HISTORY):
- self.prev_statements = self.prev_statements[count - PGW_MAX_HISTORY : count]
- self.ui.enable_prevsql(len(self.prev_statements) > 0)
- except IOError:
- pass
-
-
- def is_connected(self):
- """Return TRUE if connected to a database"""
- if (self.db is None):
- return None
- else:
- return self.db.is_connected()
-
-
- def connect(self, host = None, port = None, db = None,
- user = None, password = None):
- """Connect to a database"""
- self.ui.wndmain.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
- self.ui.status(_('Trying to connect as <b>%(user)s</b> to <b>%(db)s</b> on <b>%(host)s</b>') %
- {'user':user, 'db':db, 'host':host}, _('connecting...'))
- while (gtk.events_pending() == True):
- gtk.main_iteration_do(False)
- # Disconnect then re-connect
- self.disconnect()
- self.db = pgw.DBConnection.DBConnection(host, port, db, user, password)
- # we are connected
- if (self.is_connected()):
- # update the UI to reflect the connection state
- self.ui.enable_disconnect()
- self.ui.enable_runsql()
- self.ui.status(_('connected as <b>%(user)s</b> to <b>%(db)s</b> on <b>%(host)s</b>') %
- {'user':user, 'db':db, 'host':host}, self.db.pgversion())
- new_conn = "%s,%s,%s,%s" % (host, port, db, user)
- # update the connection history
- n = 0
- for conn in self.all_connections:
- # remove the connection from the history if it already exists
- if (conn == new_conn):
- self.all_connections.pop(n)
- break
- n = n + 1
- # add the connection to the history, making it the first of the list
- self.all_connections.insert(0, new_conn)
- # save the connection history in the config file
- cp = ConfigParser.ConfigParser()
- try:
- cp.readfp(open(pgw.get_config_path(), 'r'))
- except IOError:
- pass
- if (not cp.has_section("connections")):
- cp.add_section("connections")
- n = 0
- while ((n <= PGW_MAX_CONNECTION_HISTORY) and
- (n < len(self.all_connections))):
- cp.set("connections", "conn%d" % (n + 1), self.all_connections[n])
- n = n + 1
- try:
- cp.write(open(pgw.get_config_path(), 'w'))
- except IOError:
- pass
- # ready to type queries, give the focus to the text field
- self.ui.setfocus_sqlbuffer()
- # initialize the objects used to execute the queries
- self.execute = pgw.Execute.Execute(self.db)
- self.run = pgw.RunSQL.RunSQL(self.execute,
- self.ui.sqlview,
- self.ui.resulttab,
- self.ui.status_result)
- self.ui.wndmain.window.set_cursor(None)
-
-
- def disconnect(self):
- """Disconnect from the current database"""
- # disconnect from the database
- if (self.is_connected()): self.db.disconnect()
- # destroy the objects used for this connection
- self.db = None
- self.execute = None
- self.run = None
- # update the UI to reflect the connection state
- self.ui.status(_('not connected'), 'PgWorksheet v' + app_version)
- self.ui.enable_disconnect(False)
- self.ui.enable_runsql(False)
-
-
- def on_wndmain_destroy(self, widget):
- """Called when the application quits"""
- self.disconnect()
- self.save_history()
- gtk.main_quit()
- sys.exit(0)
-
-
- def on_wndmain_delete(self, widget, event):
- """Called when the user wants to close the main window"""
- return False
-
-
- def on_menu_connect(self, widget):
- """Called when the user want the connection dialog box"""
- # fill the connection dialog box with default parameters
- try:
- self.username = os.environ['USERNAME']
- except KeyError:
- try:
- self.username = os.environ['USER']
- except KeyError:
- pass
- host = 'localhost'
- port = '5432'
- database = 'template1'
- username = 'postgres'
- self.display_connect_dialog(host, port, username, database, 0)
-
-
- def display_connect_dialog(self, host, port, username, database, overwrite_entry):
- # display and execute the connection dialog box
- params = self.ui.connect_dialog(self, host, port, username, database, overwrite_entry)
- # check if the user have clicked "Cancel"
- if (params is not None):
- # connect to the database
- host, port, username, passwd, database = params;
- self.connect(host, port, database, username, passwd)
- # error connecting to the database, retry
- if (not self.is_connected()):
- self.ui.error_box(_('Error connecting to %s:%s@%s:%s') %
- (username, database, host, port))
- self.display_connect_dialog(host, port, username, database, 1)
-
-
- def on_dlgconnect_map(self, widget):
- """Called when the connection dialog box is displayed"""
- # clear the connections history
- self.all_connections = []
- # load the connections history from the config file
- cp = ConfigParser.ConfigParser()
- try :
- cp.readfp(open(pgw.get_config_path(), 'r'))
- n = 1
- while n <= PGW_MAX_CONNECTION_HISTORY:
- try:
- line = cp.get("connections", "conn%d" % n)
- # add the connection to the connections history
- self.all_connections.append(line)
- host, port, db, user = string.split(line, ',')
- # add the connections to the connections history list of the dialog box
- self.ui.storeconn.append(["%s:%s@%s" % (user, db, host), line])
- n = n + 1
- except:
- break
- # if we have at least one connection in the history, made it the default
- if (n > 1) :
- # select the last used connection
- self.ui.viewconn.set_cursor(self.ui.storeconn.get_path(
- self.ui.storeconn.get_iter_first()))
- except IOError:
- pass
-
-
- def on_dlgconnect_change(self, treeview):
- """Called when the user choose a connection in the connection history list"""
- # fill the connection dialog with the selected connection parameters
- model, iter = treeview.get_selection().get_selected()
- host, port, db, user = string.split(model.get(iter, 1)[0], ',')
- self.ui.entry_host.set_text(host)
- self.ui.entry_port.set_text(port)
- self.ui.entry_database.set_text(db)
- self.ui.entry_user.set_text(user)
- self.ui.entry_password.set_text('')
-
-
- def on_menu_disconnect(self, widget):
- """Called when the user wants to disconnect from the database"""
- self.disconnect()
-
-
- def on_menu_opensql(self, widget):
- """The user wants to open a file with some queries"""
- filename = self.ui.file_dialog(_('Select a SQL text file'));
- if (filename is not None):
- self.ui.undo.lock = True
- for handler in self.ui.buffer_handlers:
- self.ui.sqlbuffer.handler_block(handler)
- self.ui.set_sqlbuffer_text('')
- try:
- input = open(filename, 'r')
- for line in input:
- try:
- self.ui.sqlbuffer.insert_at_cursor(unicode(line, 'UTF-8'))
- except UnicodeDecodeError:
- try:
- self.ui.sqlbuffer.insert_at_cursor(unicode(line, pgw.get_user_encoding()))
- except UnicodeDecodeError:
- self.ui.sqlbuffer.insert_at_cursor(line)
- except IOError:
- self.ui.error_box(_('Error while opening or reading from %s') %filename)
- for handler in self.ui.buffer_handlers:
- self.ui.sqlbuffer.handler_unblock(handler)
- self.ui.undo.reset()
- self.ui.undo.lock = False
- self.ui.syntax.refresh()
- pgw.set_proportional(self.ui.sqlbuffer)
-
-
- def file_overwrite(self, title):
- """Display a "Save As" dialopg box and prompt a confirmation if the selected file exists"""
- filename = self.ui.file_dialog(title, gtk.FILE_CHOOSER_ACTION_SAVE,
- gtk.STOCK_SAVE_AS);
- if (filename is not None):
- try:
- os.stat(filename)
- if (self.ui.yesno_box(_('%s already exists, overwrite ?') % filename) ==
- gtk.RESPONSE_YES):
- return filename
- return self.file_overwrite(title)
- except OSError: # file does not exists
- return filename
- return None
-
-
- def on_menu_savesql(self, widget):
- """The user wants to save his queries"""
- filename = self.file_overwrite(_('Save SQL queries'))
- if (filename is not None):
- try:
- output = open(filename, 'w')
- output.write(self.ui.get_sqlbuffer_text())
- except IOError:
- self.ui.error_box(_('Error while creating or writing %s') % filename)
-
-
- def save_list_row(self, model, path, iter, output):
- """Save a row of a TreeView in a tabular form"""
- col = 0
- while (col < model.get_n_columns()):
- val = string.replace(model.get_value(iter, col), '"', '\"')
- output.write('"' + val + '"')
- col = col + 1
- if (col < model.get_n_columns()):
- output.write('\t')
- output.write('\n')
-
-
- def saveresults(self, widget, output):
- """Save the content of a TreeView to a tab separated file"""
- widget = widget.get_child()
- if (isinstance(widget, gtk.TextView)):
- buffer = widget.get_buffer()
- output.write(buffer.get_text(buffer.get_start_iter(),
- buffer.get_end_iter()))
- elif (isinstance(widget, gtk.TreeView)):
- widget.get_model().foreach(self.save_list_row, output)
-
-
- def on_menu_saveallresults(self, widget):
- """The user wants to save ALL the results"""
- if (self.ui.resulttab.get_n_pages() > 0):
- filename = self.file_overwrite(_('Save all the results'))
- if (filename is not None):
- try:
- output = open(filename, 'w')
- page = 0
- while page < self.ui.resulttab.get_n_pages() :
- self.saveresults(self.ui.resulttab.get_nth_page(page), output)
- page = page + 1
- except IOError:
- self.ui.error_box(_('Error while creating or writing %s') % filename)
-
-
- def on_menu_saveresults(self, widget):
- """The user wants to save the current result"""
- if (self.ui.resulttab.get_n_pages() > 0):
- filename = self.file_overwrite(_('Save the results'))
- if (filename is not None):
- try:
- output = open(filename, 'w')
- self.saveresults(self.ui.resulttab.get_nth_page(
- self.ui.resulttab.get_current_page()), output)
- except IOError:
- self.ui.error_box(_('Error while creating or writing %s') % filename)
-
-
- def on_menu_cut(self, widget):
- """Cut text to the clipboard"""
- w = self.ui.wndmain.get_focus()
- if (isinstance(w, gtk.TextView)):
- w.emit('cut-clipboard')
-
-
- def on_menu_copy(self, widget):
- """Copy text to the clipboard"""
- w = self.ui.wndmain.get_focus()
- if (isinstance(w, gtk.TextView)):
- w.emit('copy-clipboard')
- elif (isinstance(w, gtk.TreeView)):
- model, iter = w.get_selection().get_selected()
- if (iter is not None):
- col = 0
- result = ''
- while (col < model.get_n_columns()):
- val = string.replace(model.get_value(iter, col), '"', '\"')
- result = result + val
- col = col + 1
- if (col < model.get_n_columns()):
- result = result + '\t'
- clip = gtk.Clipboard()
- clip.set_text(result)
-
-
- def on_menu_paste(self, widget):
- """Paste from the clipboard"""
- w = self.ui.wndmain.get_focus()
- if (isinstance(w, gtk.TextView)):
- w.emit('paste-clipboard')
-
-
- def on_menu_selectall(self, widget):
- """Select the entire text"""
- w = self.ui.wndmain.get_focus()
- if (isinstance(w, gtk.TextView)):
- buffer = w.get_buffer()
- buffer.move_mark_by_name('selection_bound', buffer.get_start_iter())
- buffer.move_mark_by_name('insert', buffer.get_end_iter())
-
-
- def on_sqlview_focus_in(self, widget, event):
- self.ui.enable_cut()
- self.ui.enable_paste()
-
-
- def on_sqlview_focus_out(self, widget, event):
- self.ui.enable_cut(False)
- self.ui.enable_paste(False)
-
-
- def on_sqlview_keypress(self, widget, event):
- """Save the last statement in the history
- if needed (after an execution"""
- if (event is None) : return
- if (event.keyval != 65507):
- if (self.prev_saved == 0):
- self.add_prevstatement(self.ui.get_sqlbuffer_text())
- self.prev_saved = 1
-
-
- def on_menu_about(self, widget):
- self.ui.about_dialog()
-
-
- def on_menu_runsql(self, widget):
- """Execute the SQL queries"""
- if (not self.is_connected()):
- if (self.ui.yesno_box(_('Not connected to a database.\nDo you want to connect now ?')) ==
- gtk.RESPONSE_NO):
- return
- self.on_menu_connect(widget)
- if (not self.is_connected()):
- return
- self.on_text_change(widget)
- self.prev_saved = 0
- self.ui.wndmain.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
- self.run.run()
- self.ui.enable_saveresult(self.ui.resulttab.get_n_pages() > 0)
- self.ui.wndmain.window.set_cursor(None)
-
-
- def on_menu_prevsql(self, widget):
- """Display the previous statement from the history"""
- self.ui.undo.lock = True
- if (len(self.prev_statements) > 0):
- s = self.prev_statements.pop()
- self.next_statements.append(self.ui.get_sqlbuffer_text())
- self.ui.set_sqlbuffer_text(s)
- self.prev = s
- self.ui.enable_prevsql(len(self.prev_statements) > 0)
- self.ui.enable_nextsql(len(self.next_statements) > 0)
- self.ui.undo.lock = False
-
-
- def on_menu_nextsql(self, widget):
- """Display the next statement from the history"""
- self.ui.undo.lock = True
- if (len(self.next_statements) > 0):
- s = self.next_statements.pop()
- self.prev_statements.append(self.ui.get_sqlbuffer_text())
- self.ui.set_sqlbuffer_text(s)
- self.prev = s
- self.ui.enable_prevsql(len(self.prev_statements) > 0)
- self.ui.enable_nextsql(len(self.next_statements) > 0)
- self.ui.undo.lock = False
-
-
- def on_text_change(self, widget):
- """The text have been changed after navigation the history"""
- if (self.ui.undo.lock):
- return
- if (len(self.next_statements) > 0):
- if (self.next_statements[0] == ''):
- self.next_statements.pop(0)
- self.prev_statements.append(self.prev)
- for i in reversed(self.next_statements):
- self.prev_statements.append(i)
- self.next_statements = []
- self.ui.enable_prevsql(len(self.prev_statements) > 0)
- self.ui.enable_nextsql(len(self.next_statements) > 0)
-
-
-# Start program
-p = None
-try:
- p = PgWorksheet(app_name, app_version, pixmap_path)
-except KeyboardInterrupt:
- if (p is not None):
- p.on_wndmain_destroy(None)
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: latin-1; -*-
-#
-# Copyright (c) 2004-2008
-# Henri Michelon
-#
-# $Id: setup.py,v 1.14 2008/03/12 20:26:23 hmichelon Exp $
-#
-from distutils.core import setup, Extension
-
-__version__ = "1.9"
-
-setup (name = 'pgworksheet',
- version = __version__,
- description = 'PgWorksheet - SQL Tool for PostgreSQL',
- url = 'http://pgworksheet.projects.postgresql.org/',
- license = 'GPL',
- author = 'E-CML http://www.e-cml.org/',
- scripts = [ 'pgworksheet' ],
- packages = [ 'pgw' ],
- data_files = [
- ('share/pixmaps',
- [ 'pixmaps/pgworksheet.png' ]),
- ('share/pixmaps/pgworksheet',
- [ 'pixmaps/pgworksheet/connect.png',
- 'pixmaps/pgworksheet/disconnect.png',
- 'pixmaps/pgworksheet/about.png',
- 'pixmaps/pgworksheet/pgworksheet-32.png',
- 'pixmaps/pgworksheet/pgworksheet.png' ]),
- ('share/locale/fr/LC_MESSAGES',
- [ 'locale/fr/LC_MESSAGES/pgworksheet.mo' ]),
- ('share/locale/ja/LC_MESSAGES',
- [ 'locale/ja/LC_MESSAGES/pgworksheet.mo' ]),
- ('share/applications',
- [ 'applications/pgworksheet.desktop' ]),
- ]
- )
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2007-09-08 16:50+CEST\n"
+"POT-Creation-Date: 2008-10-26 17:34+CET\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <
[email protected]>\n"
@@ -15,51 +15,55 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
-#: ./pgw/ConnectDialog.py:98
+#: ./pgw/ConnectDialog.py:66
msgid "Open ConnectionParameter"
msgstr ""
-#: ./pgw/ConnectDialog.py:117
+#: ./pgw/ConnectDialog.py:87
+msgid "Enter Connection details:"
+msgstr ""
+
+#: ./pgw/ConnectDialog.py:90
+msgid "Local Connection"
+msgstr ""
+
+#: ./pgw/ConnectDialog.py:91
+msgid "Network Connection"
+msgstr ""
+
+#: ./pgw/ConnectDialog.py:99
msgid "Session Name"
msgstr ""
-#: ./pgw/ConnectDialog.py:118
+#: ./pgw/ConnectDialog.py:101
msgid "Username"
msgstr ""
-#: ./pgw/ConnectDialog.py:119 ./pgw/UI.py:454
+#: ./pgw/ConnectDialog.py:103 ./pgw/UI.py:471
msgid "Password"
msgstr ""
-#: ./pgw/ConnectDialog.py:120 ./pgw/UI.py:457
+#: ./pgw/ConnectDialog.py:105 ./pgw/UI.py:474
msgid "Database"
msgstr ""
-#: ./pgw/ConnectDialog.py:121 ./pgw/UI.py:445
+#: ./pgw/ConnectDialog.py:107 ./pgw/UI.py:462
msgid "Host"
msgstr ""
-#: ./pgw/ConnectDialog.py:122 ./pgw/UI.py:448
+#: ./pgw/ConnectDialog.py:109 ./pgw/UI.py:465
msgid "Port"
msgstr ""
#: ./pgw/ConnectDialog.py:131
-msgid "Local Connection"
-msgstr ""
-
-#: ./pgw/ConnectDialog.py:132
-msgid "Network Connection"
-msgstr ""
-
-#: ./pgw/ConnectDialog.py:143
msgid "Last Connections"
msgstr ""
-#: ./pgw/ConnectDialog.py:149
+#: ./pgw/ConnectDialog.py:137
msgid "Connect"
msgstr ""
-#: ./pgw/ConnectDialog.py:154
+#: ./pgw/ConnectDialog.py:142
msgid "Cancel"
msgstr ""
@@ -67,27 +71,27 @@ msgstr ""
msgid "Snippets"
msgstr ""
-#: ./pgw/QueryView.py:31
+#: ./pgw/QueryView.py:33
msgid "Autocompletion"
msgstr ""
-#: ./pgw/QueryView.py:95 ./pgw/RunSQL.py:122
+#: ./pgw/QueryView.py:97 ./pgw/RunSQL.py:122
msgid "Errors :"
msgstr ""
-#: ./pgw/QueryView.py:104 ./pgw/RunSQL.py:131
+#: ./pgw/QueryView.py:106 ./pgw/RunSQL.py:131
msgid "No result"
msgstr ""
-#: ./pgw/QueryView.py:129 ./pgw/RunSQL.py:154
+#: ./pgw/QueryView.py:131 ./pgw/RunSQL.py:154
msgid "query failed"
msgstr ""
-#: ./pgw/QueryView.py:137 ./pgw/RunSQL.py:162
+#: ./pgw/QueryView.py:139 ./pgw/RunSQL.py:162
msgid "log"
msgstr ""
-#: ./pgw/QueryView.py:190
+#: ./pgw/QueryView.py:197
msgid "Add"
msgstr ""
@@ -99,19 +103,19 @@ msgstr ""
msgid "Tabellenansicht"
msgstr ""
-#: ./pgw/ResultView.py:120
+#: ./pgw/ResultView.py:155
msgid "Persistent"
msgstr ""
-#: ./pgw/ResultView.py:124
+#: ./pgw/ResultView.py:159
msgid "Close"
msgstr ""
-#: ./pgw/ResultView.py:172 ./pgw/ResultView.py:214
+#: ./pgw/ResultView.py:207 ./pgw/ResultView.py:253
msgid "No Title"
msgstr ""
-#: ./pgw/ResultView.py:230
+#: ./pgw/ResultView.py:269
msgid "Error"
msgstr ""
@@ -119,71 +123,83 @@ msgstr ""
msgid "Please wait, executing the query and fetching the results..."
msgstr ""
-#: ./pgw/UI.py:213
+#: ./pgw/UI.py:120
+msgid "Doing disconnect..."
+msgstr ""
+
+#: ./pgw/UI.py:127
+msgid "Connection Failed:"
+msgstr ""
+
+#: ./pgw/UI.py:134
+msgid "Connection Failed"
+msgstr ""
+
+#: ./pgw/UI.py:225
msgid "_Connect Server..."
msgstr ""
-#: ./pgw/UI.py:215
+#: ./pgw/UI.py:227
msgid "_Disconnect"
msgstr ""
-#: ./pgw/UI.py:216
+#: ./pgw/UI.py:228
msgid "Select _All"
msgstr ""
-#: ./pgw/UI.py:218
+#: ./pgw/UI.py:230
msgid "Character _Encoding"
msgstr ""
-#: ./pgw/UI.py:219
+#: ./pgw/UI.py:231
msgid "_About..."
msgstr ""
-#: ./pgw/UI.py:220
+#: ./pgw/UI.py:232
msgid "_Connect"
msgstr ""
-#: ./pgw/UI.py:261
+#: ./pgw/UI.py:273
msgid "_File"
msgstr ""
-#: ./pgw/UI.py:263
+#: ./pgw/UI.py:275
msgid "Connect to a database"
msgstr ""
-#: ./pgw/UI.py:265
+#: ./pgw/UI.py:277
msgid "Disconnect from the database"
msgstr ""
-#: ./pgw/UI.py:267
+#: ./pgw/UI.py:279
msgid "Quit the Program"
msgstr ""
-#: ./pgw/UI.py:277
+#: ./pgw/UI.py:289
msgid "PgWorksheet - PostgreSQL SQL Tool"
msgstr ""
-#: ./pgw/UI.py:418
+#: ./pgw/UI.py:435
msgid "Database connection"
msgstr ""
-#: ./pgw/UI.py:430
+#: ./pgw/UI.py:447
msgid "5 Previous connections"
msgstr ""
-#: ./pgw/UI.py:451
+#: ./pgw/UI.py:468
msgid "User"
msgstr ""
-#: ./pgw/UI.py:526
+#: ./pgw/UI.py:543
msgid "About PgWorksheet"
msgstr ""
-#: ./pgw/UI.py:558
+#: ./pgw/UI.py:575
msgid "About"
msgstr ""
-#: ./pgw/UI.py:905
+#: ./pgw/UI.py:922
msgid "License"
msgstr ""
@@ -33,6 +33,7 @@ class ListBox(gtk.ScrolledWindow):
if tuple == None:
return None
return self.store[tuple[0]]
+
class TextWidget(gtk.ScrolledWindow):
def __init__(self):
gtk.ScrolledWindow.__init__(self)