pax_global_header00006660000000000000000000000064141410541640014512gustar00rootroot0000000000000052 comment=5728cf534e24319640b52ea0e93c5042fe210f22 python-cx_Oracle-8.3.0/000077500000000000000000000000001414105416400147405ustar00rootroot00000000000000python-cx_Oracle-8.3.0/.gitattributes000066400000000000000000000001411414105416400176270ustar00rootroot00000000000000* text=auto *.c text *.h text *.in text *.py text *.rst text *.txt text python-cx_Oracle-8.3.0/.github/000077500000000000000000000000001414105416400163005ustar00rootroot00000000000000python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001414105416400204635ustar00rootroot00000000000000python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/announcements.md000066400000000000000000000004361414105416400236650ustar00rootroot00000000000000--- name: Announcements about: Use this if you are sharing something interesting title: '' labels: announcement assignees: '' --- python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000026461414105416400231650ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- 1. What versions are you using? 2. Is it an error or a hang or a crash? 3. What error(s) or behavior you are seeing? 4. Include a runnable Python script that shows the problem. python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000000341414105416400224500ustar00rootroot00000000000000blank_issues_enabled: false python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/documentation-and-example-improvements.md000066400000000000000000000010551414105416400305760ustar00rootroot00000000000000--- name: Documentation and Example Improvements about: Use this to suggest changes to documentation and examples title: '' labels: enhancement assignees: '' --- 1. What is the link to the documentation section that needs improving? 2. Describe the confusion 3. Suggest changes that would help python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/enhancement-requests.md000066400000000000000000000011631414105416400251440ustar00rootroot00000000000000--- name: Enhancement Requests about: Use this for enhancement requests title: '' labels: enhancement assignees: '' --- 1. Describe your new request in detail 2. Give supporting information about tools and operating systems. Give relevant product version numbers python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/general-questions-and-runtime-problems.md000066400000000000000000000026521414105416400305210ustar00rootroot00000000000000--- name: Questions and Runtime Problems about: For general cx_Oracle questions title: '' labels: question assignees: '' --- 1. What versions are you using? 2. Describe the problem 3. Include a runnable Python script that shows the problem. python-cx_Oracle-8.3.0/.github/ISSUE_TEMPLATE/installation-questions.md000066400000000000000000000030641414105416400255410ustar00rootroot00000000000000--- name: Installation Problems about: Use this for cx_Oracle installation questions title: '' labels: install & configuration assignees: '' --- 1. What versions are you using? 2. Describe the problem 3. Show the directory listing where your Oracle Client libraries are installed (e.g. the Instant Client directory). Is it 64-bit or 32-bit? 4. Show what the `PATH` environment variable (on Windows) or `LD_LIBRARY_PATH` (on Linux) is set to? 5. Show any Oracle environment variables set (e.g. ORACLE_HOME, ORACLE_BASE). python-cx_Oracle-8.3.0/.github/SECURITY.md000066400000000000000000000022531414105416400200730ustar00rootroot00000000000000# Reporting Security Vulnerabilities Oracle values the independent security research community and believes that responsible disclosure of security vulnerabilities helps us ensure the security and privacy of all our users. Please do NOT raise a GitHub Issue to report a security vulnerability. If you believe you have found a security vulnerability, please submit a report to secalert_us@oracle.com preferably with a proof of concept. We provide additional information on [how to report security vulnerabilities to Oracle](https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html) which includes public encryption keys for secure email. We ask that you do not use other channels or contact project contributors directly. Non-vulnerability related security issues such as great new ideas for security features are welcome on GitHub Issues. ## Security-Related Information We will provide security related information such as a threat model, considerations for secure use, or any known security issues in our documentation. Please note that labs and sample code are intended to demonstrate a concept and may not be sufficiently hardened for production use. python-cx_Oracle-8.3.0/.github/SUPPORT.md000066400000000000000000000016611414105416400200020ustar00rootroot00000000000000# Python cx_Oracle Support cx_Oracle is an Open Source project, so do some searching and reading before asking questions. ## cx_Oracle Installation issues Read the [Installation instructions](http://cx-oracle.readthedocs.io/en/latest/installation.html) ## SQL and PL/SQL Questions Ask SQL and PL/SQL questions at [AskTOM](https://asktom.oracle.com/) Try out SQL and find code snippets on our hosted database with [LIVE SQL](https://livesql.oracle.com/) ## Database and other Oracle Issues Ask Database and other Oracle issues on an [OTN Forum](https://community.oracle.com/community/database/) ## cx_Oracle Documentation The cx_Oracle documentation is [here](http://cx-oracle.readthedocs.io/en/latest/) ## Got a cx_Oracle question? Ask at [GitHub](https://github.com/oracle/python-cx_Oracle/issues) When opening a new issue, fill in the template that will be shown. Include enough information for people to understand your problem. python-cx_Oracle-8.3.0/.github/pull_request_template.md000066400000000000000000000011621414105416400232410ustar00rootroot00000000000000Thanks for contributing! Before submitting PRs for cx_Oracle you must have your signed *Oracle Contributor Agreement* accepted. See https://www.oracle.com/technetwork/community/oca-486395.html If the problem solved is small, you may find it easier to open an Issue describing the problem and its cause so we can create the fix. The bottom of your commit message must have the following line using your name and e-mail address as it appears in the OCA Signatories list. ``` Signed-off-by: Your Name ``` This can be automatically added to pull requests by committing with: ``` git commit --signoff ```` python-cx_Oracle-8.3.0/.github/stale.yml000066400000000000000000000015341414105416400201360ustar00rootroot00000000000000# https://probot.github.io/apps/stale/ # Number of days of inactivity before an issue becomes stale daysUntilStale: 30 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - enhancement - bug - announcement - OCA accepted # Label to use when marking an issue as stale staleLabel: inactive # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as inactive because it has not been updated recently. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: > This issue has been automatically closed because it has not been updated for a month. python-cx_Oracle-8.3.0/.gitignore000066400000000000000000000001001414105416400167170ustar00rootroot00000000000000*.pyc .tox/ build/ dist/ doc/build cx_Oracle.egg-info/ MANIFEST python-cx_Oracle-8.3.0/.gitmodules000066400000000000000000000000631414105416400171140ustar00rootroot00000000000000[submodule "odpi"] path = odpi url = ../odpi.git python-cx_Oracle-8.3.0/.readthedocs.yaml000066400000000000000000000002011414105416400201600ustar00rootroot00000000000000version: 2 sphinx: configuration: doc/src/conf.py python: version: 3.8 install: - requirements: doc/requirements.txt python-cx_Oracle-8.3.0/CONTRIBUTING.md000066400000000000000000000035701414105416400171760ustar00rootroot00000000000000# Contributing We welcome your contributions! There are multiple ways to contribute. ## Issues For bugs or enhancement requests, please file a GitHub issue unless it's security related. When filing a bug remember that the better written the bug is, the more likely it is to be fixed. If you think you've found a security vulnerability, do not raise a GitHub issue and follow the instructions on our [Security Policy](./.github/SECURITY.md). ## Contributing Code We welcome your code contributions. To get started, you will need to sign the [Oracle Contributor Agreement](https://oca.opensource.oracle.com) (OCA). For pull requests to be accepted, the bottom of your commit message must have the following line using the name and e-mail address you used for the OCA. ```text Signed-off-by: Your Name ``` This can be automatically added to pull requests by committing with: ```text git commit --signoff ``` Only pull requests from committers that can be verified as having signed the OCA can be accepted. ### Pull request process 1. Fork this repository 1. Create a branch in your fork to implement the changes. We recommend using the issue number as part of your branch name, e.g. `1234-fixes` 1. Ensure that any documentation is updated with the changes that are required by your fix. 1. Ensure that any samples are updated if the base image has been changed. 1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly what your changes are meant to do and provide simple steps on how to validate your changes. Ensure that you reference the issue you created as well. 1. We will review your PR before it is merged. ## Code of Conduct Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd like more specific guidelines see the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) python-cx_Oracle-8.3.0/LICENSE.txt000066400000000000000000000034151414105416400165660ustar00rootroot00000000000000LICENSE AGREEMENT FOR CX_ORACLE Copyright 2016, 2018, Oracle and/or its affiliates. All rights reserved. Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the disclaimer that follows. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the names of the copyright holders nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *AS IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Computronix is a registered trademark of Computronix (Canada) Ltd. python-cx_Oracle-8.3.0/MANIFEST.in000066400000000000000000000003661414105416400165030ustar00rootroot00000000000000include MANIFEST.in include *.txt recursive-include odpi *.c recursive-include odpi *.h prune odpi/test prune odpi/samples recursive-include src *.c recursive-include src *.h recursive-include samples *.py *.sql recursive-include test *.py *.sql python-cx_Oracle-8.3.0/README.md000066400000000000000000000040451414105416400162220ustar00rootroot00000000000000# cx_Oracle version 8.3 cx_Oracle is a Python extension module that enables access to Oracle Database. It conforms to the [Python database API 2.0 specification][1] with a considerable number of additions and a couple of exclusions. See the [homepage](https://oracle.github.io/python-cx_Oracle/index.html) for a feature list. cx_Oracle 8.3 has been tested with Python versions 3.6 through 3.10. You can use cx_Oracle with Oracle 11.2, 12c, 18c, 19c and 21c client libraries. Oracle's standard client-server version interoperability allows connection to both older and newer databases. For example Oracle 19c client libraries can connect to Oracle Database 11.2. Older versions of cx_Oracle may work with older versions of Python. ## Installation See [cx_Oracle Installation][15]. ## Documentation See the [cx_Oracle Documentation][2] and [Release Notes][14]. ## Samples See the [/samples][12] directory and the [tutorial][6]. You can also look at the scripts in [cx_OracleTools][7] and the modules in [cx_PyOracleLib][8]. ## Help Issues and questions can be raised with the cx_Oracle community on [GitHub][9] or on the [mailing list][5]. ## Tests See [/test][11]. ## Contributing See [CONTRIBUTING](https://github.com/oracle/python-cx_Oracle/blob/main/CONTRIBUTING.md) ## License cx_Oracle is licensed under a BSD license which you can find [here][3]. [1]: https://www.python.org/dev/peps/pep-0249 [2]: http://cx-oracle.readthedocs.io [3]: https://github.com/oracle/python-cx_Oracle/blob/main/LICENSE.txt [5]: http://lists.sourceforge.net/lists/listinfo/cx-oracle-users [6]: https://github.com/oracle/python-cx_Oracle/tree/main/samples/tutorial [7]: http://cx-oracletools.sourceforge.net [8]: http://cx-pyoraclelib.sourceforge.net [9]: https://github.com/oracle/python-cx_Oracle/issues [11]: https://github.com/oracle/python-cx_Oracle/tree/main/test [12]: https://github.com/oracle/python-cx_Oracle/tree/main/samples [14]: https://cx-oracle.readthedocs.io/en/latest/release_notes.html [15]: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html python-cx_Oracle-8.3.0/README.txt000066400000000000000000000002431414105416400164350ustar00rootroot00000000000000Please see the cx_Oracle home page for links to documentation, source, build and installation instructions: https://oracle.github.io/python-cx_Oracle/index.html python-cx_Oracle-8.3.0/doc/000077500000000000000000000000001414105416400155055ustar00rootroot00000000000000python-cx_Oracle-8.3.0/doc/Makefile000066400000000000000000000006771414105416400171570ustar00rootroot00000000000000# Makefile to generate cx_Oracle documentation using Sphinx SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = src BUILDDIR = build .PHONY: html html: @$(SPHINXBUILD) -M html $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS) .PHONY: epub epub: @$(SPHINXBUILD) -M epub $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS) .PHONY: pdf pdf: @$(SPHINXBUILD) -M latexpdf $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS) .PHONY: clean clean: rm -rf $(BUILDDIR)/* python-cx_Oracle-8.3.0/doc/README.md000066400000000000000000000012371414105416400167670ustar00rootroot00000000000000The generated cx_Oracle documentation is at http://cx-oracle.readthedocs.io/ This directory contains the documentation source. It is written using reST (re-Structured Text) format source files which are processed using Sphinx and turned into HTML, PDF or ePub documents. If you wish to build these yourself, you need to install Sphinx. Sphinx is available on many Linux distributions as a pre-built package. You can also install Sphinx on all platforms using the Python package manager "pip". For more information on Sphinx, please visit this page: http://www.sphinx-doc.org Once Sphinx is installed, the supplied Makefile can be used to build the different targets. python-cx_Oracle-8.3.0/doc/requirements.txt000066400000000000000000000000461414105416400207710ustar00rootroot00000000000000sphinx==4.1.2 sphinx_rtd_theme==0.5.1 python-cx_Oracle-8.3.0/doc/src/000077500000000000000000000000001414105416400162745ustar00rootroot00000000000000python-cx_Oracle-8.3.0/doc/src/api_manual/000077500000000000000000000000001414105416400204025ustar00rootroot00000000000000python-cx_Oracle-8.3.0/doc/src/api_manual/aq.rst000066400000000000000000000275651414105416400215540ustar00rootroot00000000000000.. _aq: ********************* Advanced Queuing (AQ) ********************* See :ref:`aqusermanual` for more information about using AQ in cx_Oracle. .. note:: All of these objects are extensions to the DB API. .. _queue: ------ Queues ------ These objects are created using the :meth:`Connection.queue()` method and are used to enqueue and dequeue messages. .. attribute:: Queue.connection This read-only attribute returns a reference to the connection object on which the queue was created. .. method:: Queue.deqmany(maxMessages) Dequeues up to the specified number of messages from the queue and returns a list of these messages. Each element of the returned list is a :ref:`message property` object. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the name of the method was changed from `deqMany()`. The old name will continue to work for a period of time. .. method:: Queue.deqone() Dequeues at most one message from the queue. If a message is dequeued, it will be a :ref:`message property` object; otherwise, it will be the value None. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the name of the method was changed from `deqOne()`. The old name will continue to work for a period of time. .. attribute:: Queue.deqoptions This read-only attribute returns a reference to the :ref:`options ` that will be used when dequeuing messages from the queue. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the name of the attribute was changed from `deqOptions`. The old name will continue to work for a period of time. .. method:: Queue.enqmany(messages) Enqueues multiple messages into the queue. The messages parameter must be a sequence containing :ref:`message property ` objects which have all had their payload attribute set to a value that the queue supports. Warning: calling this function in parallel on different connections acquired from the same pool may fail due to Oracle bug 29928074. Ensure that this function is not run in parallel, use standalone connections or connections from different pools, or make multiple calls to :meth:`Queue.enqone()` instead. The function :meth:`Queue.deqmany()` call is not affected. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the name of the method was changed from `enqMany()`. The old name will continue to work for a period of time. .. method:: Queue.enqone(message) Enqueues a single message into the queue. The message must be a :ref:`message property` object which has had its payload attribute set to a value that the queue supports. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the name of the method was changed from `enqOne()`. The old name will continue to work for a period of time. .. attribute:: Queue.enqoptions This read-only attribute returns a reference to the :ref:`options ` that will be used when enqueuing messages into the queue. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the name of the attribute was changed from `enqOptions`. The old name will continue to work for a period of time. .. attribute:: Queue.name This read-only attribute returns the name of the queue. .. attribute:: Queue.payload_type This read-only attribute returns the object type for payloads that can be enqueued and dequeued. If using a raw queue, this returns the value None. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the name of the attribute was changed from `payloadType`. The old name will continue to work for a period of time. .. _deqoptions: --------------- Dequeue Options --------------- .. note:: These objects are used to configure how messages are dequeued from queues. An instance of this object is found in the attribute :attr:`Queue.deqOptions`. .. attribute:: DeqOptions.condition This attribute specifies a boolean expression similar to the where clause of a SQL query. The boolean expression can include conditions on message properties, user data properties and PL/SQL or SQL functions. The default is to have no condition specified. .. attribute:: DeqOptions.consumername This attribute specifies the name of the consumer. Only messages matching the consumer name will be accessed. If the queue is not set up for multiple consumers this attribute should not be set. The default is to have no consumer name specified. .. attribute:: DeqOptions.correlation This attribute specifies the correlation identifier of the message to be dequeued. Special pattern-matching characters, such as the percent sign (%) and the underscore (_), can be used. If multiple messages satisfy the pattern, the order of dequeuing is indeterminate. The default is to have no correlation specified. .. attribute:: DeqOptions.deliverymode This write-only attribute specifies what types of messages should be dequeued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT` (default), :data:`~cx_Oracle.MSG_BUFFERED` or :data:`~cx_Oracle.MSG_PERSISTENT_OR_BUFFERED`. .. attribute:: DeqOptions.mode This attribute specifies the locking behaviour associated with the dequeue operation. It should be one of the values :data:`~cx_Oracle.DEQ_BROWSE`, :data:`~cx_Oracle.DEQ_LOCKED`, :data:`~cx_Oracle.DEQ_REMOVE` (default), or :data:`~cx_Oracle.DEQ_REMOVE_NODATA`. .. attribute:: DeqOptions.msgid This attribute specifies the identifier of the message to be dequeued. The default is to have no message identifier specified. .. attribute:: DeqOptions.navigation This attribute specifies the position of the message that is retrieved. It should be one of the values :data:`~cx_Oracle.DEQ_FIRST_MSG`, :data:`~cx_Oracle.DEQ_NEXT_MSG` (default), or :data:`~cx_Oracle.DEQ_NEXT_TRANSACTION`. .. attribute:: DeqOptions.transformation This attribute specifies the name of the transformation that must be applied after the message is dequeued from the database but before it is returned to the calling application. The transformation must be created using dbms_transform. The default is to have no transformation specified. .. attribute:: DeqOptions.visibility This attribute specifies the transactional behavior of the dequeue request. It should be one of the values :data:`~cx_Oracle.DEQ_ON_COMMIT` (default) or :data:`~cx_Oracle.DEQ_IMMEDIATE`. This attribute is ignored when using the :data:`~cx_Oracle.DEQ_BROWSE` mode. Note the value of :attr:`~Connection.autocommit` is always ignored. .. attribute:: DeqOptions.wait This attribute specifies the time to wait, in seconds, for a message matching the search criteria to become available for dequeuing. One of the values :data:`~cx_Oracle.DEQ_NO_WAIT` or :data:`~cx_Oracle.DEQ_WAIT_FOREVER` can also be used. The default is :data:`~cx_Oracle.DEQ_WAIT_FOREVER`. .. _enqoptions: --------------- Enqueue Options --------------- .. note:: These objects are used to configure how messages are enqueued into queues. An instance of this object is found in the attribute :attr:`Queue.enqOptions`. .. attribute:: EnqOptions.deliverymode This write-only attribute specifies what type of messages should be enqueued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT` (default) or :data:`~cx_Oracle.MSG_BUFFERED`. .. attribute:: EnqOptions.transformation This attribute specifies the name of the transformation that must be applied before the message is enqueued into the database. The transformation must be created using dbms_transform. The default is to have no transformation specified. .. attribute:: EnqOptions.visibility This attribute specifies the transactional behavior of the enqueue request. It should be one of the values :data:`~cx_Oracle.ENQ_ON_COMMIT` (default) or :data:`~cx_Oracle.ENQ_IMMEDIATE`. Note the value of :attr:`~Connection.autocommit` is ignored. .. _msgproperties: ------------------ Message Properties ------------------ .. note:: These objects are used to identify the properties of messages that are enqueued and dequeued in queues. They are created by the method :meth:`Connection.msgproperties()`. They are used by the methods :meth:`Queue.enqone()` and :meth:`Queue.enqmany()` and returned by the methods :meth:`Queue.deqone()` and :meth:`Queue.deqmany()`. .. attribute:: MessageProperties.attempts This read-only attribute specifies the number of attempts that have been made to dequeue the message. .. attribute:: MessageProperties.correlation This attribute specifies the correlation used when the message was enqueued. .. attribute:: MessageProperties.delay This attribute specifies the number of seconds to delay an enqueued message. Any integer is acceptable but the constant :data:`~cx_Oracle.MSG_NO_DELAY` can also be used indicating that the message is available for immediate dequeuing. .. attribute:: MessageProperties.deliverymode This read-only attribute specifies the type of message that was dequeued. It will be one of the values :data:`~cx_Oracle.MSG_PERSISTENT` or :data:`~cx_Oracle.MSG_BUFFERED`. .. attribute:: MessageProperties.enqtime This read-only attribute specifies the time that the message was enqueued. .. attribute:: MessageProperties.exceptionq This attribute specifies the name of the queue to which the message is moved if it cannot be processed successfully. Messages are moved if the number of unsuccessful dequeue attempts has exceeded the maximum number of retries or if the message has expired. All messages in the exception queue are in the :data:`~cx_Oracle.MSG_EXPIRED` state. The default value is the name of the exception queue associated with the queue table. .. attribute:: MessageProperties.expiration This attribute specifies, in seconds, how long the message is available for dequeuing. This attribute is an offset from the delay attribute. Expiration processing requires the queue monitor to be running. Any integer is accepted but the constant :data:`~cx_Oracle.MSG_NO_EXPIRATION` can also be used indicating that the message never expires. .. attribute:: MessageProperties.msgid This read-only attribute specifies the id of the message in the last queue that enqueued or dequeued the message. If the message has never been dequeued or enqueued, the value will be `None`. .. attribute:: MessageProperties.payload This attribute identifies the payload that will be enqueued or the payload that was dequeued when using a :ref:`queue `. When enqueuing, the value is checked to ensure that it conforms to the type expected by that queue. For RAW queues, the value can be a bytes object or a string. If the value is a string it will first be converted to bytes by encoding in the encoding identified by the attribute :attr:`Connection.encoding`. .. attribute:: MessageProperties.priority This attribute specifies the priority of the message. A smaller number indicates a higher priority. The priority can be any integer, including negative numbers. The default value is zero. .. attribute:: MessageProperties.state This read-only attribute specifies the state of the message at the time of the dequeue. It will be one of the values :data:`~cx_Oracle.MSG_WAITING`, :data:`~cx_Oracle.MSG_READY`, :data:`~cx_Oracle.MSG_PROCESSED` or :data:`~cx_Oracle.MSG_EXPIRED`. python-cx_Oracle-8.3.0/doc/src/api_manual/connection.rst000066400000000000000000000566051414105416400233070ustar00rootroot00000000000000.. _connobj: ***************** Connection Object ***************** .. note:: Any outstanding changes will be rolled back when the connection object is destroyed or closed. .. method:: Connection.__enter__() The entry point for the connection as a context manager. It returns itself. .. note:: This method is an extension to the DB API definition. .. method:: Connection.__exit__() The exit point for the connection as a context manager. This will close the connection and roll back any uncommitted transaction. .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.action This write-only attribute sets the action column in the v$session table. It is a string attribute and cannot be set to None -- use the empty string instead. .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.autocommit This read-write attribute determines whether autocommit mode is on or off. When autocommit mode is on, all statements are committed as soon as they have completed executing. .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.begin([formatId, transactionId, branchId]) Explicitly begin a new transaction. Without parameters, this explicitly begins a local transaction; otherwise, this explicitly begins a distributed (global) transaction with the given parameters. See the Oracle documentation for more details. Note that in order to make use of global (distributed) transactions, the :attr:`~Connection.internal_name` and :attr:`~Connection.external_name` attributes must be set. .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.call_timeout This read-write attribute specifies the amount of time (in milliseconds) that a single round-trip to the database may take before a timeout will occur. A value of 0 means that no timeout will take place. If a timeout occurs, the error *DPI-1067* will be returned if the connection is still usable. Alternatively the error *DPI-1080* will be returned if the connection has become invalid and can no longer be used. .. versionadded:: 7.0 .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the attribute `callTimeout` was renamed to `call_timeout`. The old name will continue to work for a period of time. The error *DPI-1080* was also introduced in this release. .. note:: This attribute is an extension to the DB API definition and is only available in Oracle Client 18c and higher. .. method:: Connection.cancel() Break a long-running transaction. .. note:: This method is an extension to the DB API definition. .. method:: Connection.changepassword(oldpassword, newpassword) Change the password of the logon. .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.client_identifier This write-only attribute sets the client_identifier column in the v$session table. .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.clientinfo This write-only attribute sets the client_info column in the v$session table. .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.close() Close the connection now, rather than whenever __del__ is called. The connection will be unusable from this point forward; an Error exception will be raised if any operation is attempted with the connection. All open cursors and LOBs created by the connection will be closed and will also no longer be usable. Internally, references to the connection are held by cursor objects, LOB objects, subscription objects, etc. Once all of these references are released, the connection itself will be closed automatically. Either control references to these related objects carefully or explicitly close connections in order to ensure sufficient resources are available. .. method:: Connection.commit() Commit any pending transactions to the database. .. method:: Connection.createlob(lobType) Create and return a new temporary :ref:`LOB object ` of the specified type. The lobType parameter should be one of :data:`cx_Oracle.CLOB`, :data:`cx_Oracle.BLOB` or :data:`cx_Oracle.NCLOB`. .. versionadded:: 6.2 .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.current_schema This read-write attribute sets the current schema attribute for the session. Setting this value is the same as executing the SQL statement "ALTER SESSION SET CURRENT_SCHEMA". The attribute is set (and verified) on the next call that does a round trip to the server. The value is placed before unqualified database objects in SQL statements you then execute. .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.cursor() Return a new :ref:`cursor object ` using the connection. .. attribute:: Connection.dbop This write-only attribute sets the database operation that is to be monitored. This can be viewed in the DBOP_NAME column of the V$SQL_MONITOR table. .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.deq(name, options, msgproperties, payload) Returns a message id after successfully dequeuing a message. The options object can be created using :meth:`~Connection.deqoptions()` and the msgproperties object can be created using :meth:`~Connection.msgproperties()`. The payload must be an object created using :meth:`ObjectType.newobject()`. .. versionadded:: 5.3 .. deprecated:: 7.2 Use the methods :meth:`Queue.deqone()` or :meth:`Queue.deqmany()` instead. .. note:: This method is an extension to the DB API definition. .. method:: Connection.deqoptions() Returns an object specifying the options to use when dequeuing messages. See :ref:`deqoptions` for more information. .. versionadded:: 5.3 .. deprecated:: 7.2 Use the attribute :attr:`Queue.deqoptions` instead. .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.dsn This read-only attribute returns the TNS entry of the database to which a connection has been established. .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.edition This read-only attribute gets the session edition and is only available in Oracle Database 11.2 (both client and server must be at this level or higher for this to work). .. versionadded:: 5.3 .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.encoding This read-only attribute returns the IANA character set name of the character set in use by the Oracle client for regular strings. .. deprecated:: 8.2 .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.enq(name, options, msgproperties, payload) Returns a message id after successfully enqueuing a message. The options object can be created using :meth:`~Connection.enqoptions()` and the msgproperties object can be created using :meth:`~Connection.msgproperties()`. The payload must be an object created using :meth:`ObjectType.newobject()`. .. versionadded:: 5.3 .. deprecated:: 7.2 Use the methods :meth:`Queue.enqone()` or :meth:`Queue.enqmany()` instead. .. note:: This method is an extension to the DB API definition. .. method:: Connection.enqoptions() Returns an object specifying the options to use when enqueuing messages. See :ref:`enqoptions` for more information. .. versionadded:: 5.3 .. deprecated:: 7.2 Use the attribute :attr:`Queue.enqoptions` instead. .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.external_name This read-write attribute specifies the external name that is used by the connection when logging distributed transactions. .. versionadded:: 5.3 .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.getSodaDatabase() Return a :ref:`SodaDatabase ` object for Simple Oracle Document Access (SODA). All SODA operations are performed either on the returned SodaDatabase object or from objects created by the returned SodaDatabase object. See `here `__ for additional information on SODA. .. versionadded:: 7.0 .. note:: This method is an extension to the DB API definition. .. method:: Connection.gettype(name) Return a :ref:`type object ` given its name. This can then be used to create objects which can be bound to cursors created by this connection. .. versionadded:: 5.3 .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.handle This read-only attribute returns the OCI service context handle for the connection. It is primarily provided to facilitate testing the creation of a connection using the OCI service context handle. .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.inputtypehandler This read-write attribute specifies a method called for each value that is bound to a statement executed on any cursor associated with this connection. The method signature is handler(cursor, value, arraysize) and the return value is expected to be a variable object or None in which case a default variable object will be created. If this attribute is None, the default behavior will take place for all values bound to statements. .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.internal_name This read-write attribute specifies the internal name that is used by the connection when logging distributed transactions. .. versionadded:: 5.3 .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.ltxid This read-only attribute returns the logical transaction id for the connection. It is used within Oracle Transaction Guard as a means of ensuring that transactions are not duplicated. See the Oracle documentation and the provided sample for more information. .. versionadded:: 5.3 .. note: This attribute is an extension to the DB API definition. It is only available when Oracle Database 12.1 or higher is in use on both the server and the client. .. attribute:: Connection.maxBytesPerCharacter This read-only attribute returns the maximum number of bytes each character can use for the client character set. .. deprecated:: 8.2 .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.module This write-only attribute sets the module column in the v$session table. The maximum length for this string is 48 and if you exceed this length you will get ORA-24960. .. note: This attribute is an extension to the DB API definition. .. method:: Connection.msgproperties(payload, correlation, delay, exceptionq, \ expiration, priority) Returns an object specifying the properties of messages used in advanced queuing. See :ref:`msgproperties` for more information. Each of the parameters are optional. If specified, they act as a shortcut for setting each of the equivalently named properties. .. versionadded:: 5.3 .. versionchanged:: 7.2 Added parameters .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.nencoding This read-only attribute returns the IANA character set name of the national character set in use by the Oracle client. .. deprecated:: 8.2 .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.outputtypehandler This read-write attribute specifies a method called for each column that is going to be fetched from any cursor associated with this connection. The method signature is handler(cursor, name, defaultType, length, precision, scale) and the return value is expected to be a variable object or None in which case a default variable object will be created. If this attribute is None, the default behavior will take place for all columns fetched from cursors. See :ref:`outputtypehandlers`. .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.ping() Ping the server which can be used to test if the connection is still active. .. note:: This method is an extension to the DB API definition. .. method:: Connection.prepare() Prepare the distributed (global) transaction for commit. Return a boolean indicating if a transaction was actually prepared in order to avoid the error ORA-24756 (transaction does not exist). .. note:: This method is an extension to the DB API definition. .. method:: Connection.queue(name, payload_type=None) Creates a :ref:`queue ` which is used to enqueue and dequeue messages in Advanced Queueing. The name parameter is expected to be a string identifying the queue in which messages are to be enqueued or dequeued. The payload_type parameter, if specified, is expected to be an :ref:`object type ` that identifies the type of payload the queue expects. If not specified, RAW data is enqueued and dequeued. .. versionadded:: 7.2 .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the parameter `payloadType` was renamed to `payload_type`. The old name will continue to work as a keyword parameter for a period of time. .. note:: This method is an extension to the DB API definition. .. method:: Connection.rollback() Rollback any pending transactions. .. method:: Connection.shutdown([mode]) Shutdown the database. In order to do this the connection must be connected as :data:`~cx_Oracle.SYSDBA` or :data:`~cx_Oracle.SYSOPER`. Two calls must be made unless the mode specified is :data:`~cx_Oracle.DBSHUTDOWN_ABORT`. An example is shown below: :: import cx_Oracle connection = cx_Oracle.connect(mode = cx_Oracle.SYSDBA) connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_IMMEDIATE) cursor = connection.cursor() cursor.execute("alter database close normal") cursor.execute("alter database dismount") connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL) .. note:: This method is an extension to the DB API definition. .. method:: Connection.startup(force=False, restrict=False, pfile=None) Startup the database. This is equivalent to the SQL\*Plus command "startup nomount". The connection must be connected as :data:`~cx_Oracle.SYSDBA` or :data:`~cx_Oracle.SYSOPER` with the :data:`~cx_Oracle.PRELIM_AUTH` option specified for this to work. The pfile parameter, if specified, is expected to be a string identifying the location of the parameter file (PFILE) which will be used instead of the stored parameter file (SPFILE). An example is shown below: :: import cx_Oracle connection = cx_Oracle.connect( mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH) connection.startup() connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA) cursor = connection.cursor() cursor.execute("alter database mount") cursor.execute("alter database open") .. note:: This method is an extension to the DB API definition. .. attribute:: Connection.stmtcachesize This read-write attribute specifies the size of the statement cache. This value can make a significant difference in performance if you have a small number of statements that you execute repeatedly. The default value is 20. See :ref:`Statement Caching ` for more information. .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.subscribe(namespace=cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE, protocol=cx_Oracle.SUBSCR_PROTO_OCI, callback=None, timeout=0, operations=OPCODE_ALLOPS, port=0, qos=0, ip_address=None, grouping_class=0, grouping_value=0, grouping_type=cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY, name=None, client_initiated=False) Return a new :ref:`subscription object ` that receives notifications for events that take place in the database that match the given parameters. The namespace parameter specifies the namespace the subscription uses. It can be one of :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` or :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`. The protocol parameter specifies the protocol to use when notifications are sent. Currently the only valid value is :data:`cx_Oracle.SUBSCR_PROTO_OCI`. The callback is expected to be a callable that accepts a single parameter. A :ref:`message object ` is passed to this callback whenever a notification is received. The timeout value specifies that the subscription expires after the given time in seconds. The default value of 0 indicates that the subscription never expires. The operations parameter enables filtering of the messages that are sent (insert, update, delete). The default value will send notifications for all operations. This parameter is only used when the namespace is set to :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE`. The port parameter specifies the listening port for callback notifications from the database server. If not specified, an unused port will be selected by the Oracle Client libraries. The qos parameter specifies quality of service options. It should be one or more of the following flags, OR'ed together: :data:`cx_Oracle.SUBSCR_QOS_RELIABLE`, :data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY`, :data:`cx_Oracle.SUBSCR_QOS_ROWIDS`, :data:`cx_Oracle.SUBSCR_QOS_QUERY`, :data:`cx_Oracle.SUBSCR_QOS_BEST_EFFORT`. The ip_address parameter specifies the IP address (IPv4 or IPv6) in standard string notation to bind for callback notifications from the database server. If not specified, the client IP address will be determined by the Oracle Client libraries. The grouping_class parameter specifies what type of grouping of notifications should take place. Currently, if set, this value can only be set to the value :data:`cx_Oracle.SUBSCR_GROUPING_CLASS_TIME`, which will group notifications by the number of seconds specified in the grouping_value parameter. The grouping_type parameter should be one of the values :data:`cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY` (the default) or :data:`cx_Oracle.SUBSCR_GROUPING_TYPE_LAST`. The name parameter is used to identify the subscription and is specific to the selected namespace. If the namespace parameter is :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` then the name is optional and can be any value. If the namespace parameter is :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`, however, the name must be in the format '' for single consumer queues and ':' for multiple consumer queues, and identifies the queue that will be monitored for messages. The queue name may include the schema, if needed. The client_initiated parameter is used to determine if client initiated connections or server initiated connections (the default) will be established. Client initiated connections are only available in Oracle Client 19.4 and Oracle Database 19.4 and higher. .. versionadded:: 6.4 The parameters ipAddress, groupingClass, groupingValue, groupingType and name were added. .. versionadded:: 7.3 The parameter clientInitiated was added. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the parameter `ipAddress` was renamed to `ip_address`, the parameter `groupingClass` was renamed to `grouping_class`, the parameter `groupingValue` was renamed to `grouping_value`, the parameter `groupingType` was renamed to `grouping_type` and the parameter `clientInitiated` was renamed to `client_initiated`. The old names will continue to work as keyword parameters for a period of time. .. note:: This method is an extension to the DB API definition. .. note:: The subscription can be deregistered in the database by calling the function :meth:`~Connection.unsubscribe()`. If this method is not called and the connection that was used to create the subscription is explicitly closed using the function :meth:`~Connection.close()`, the subscription will not be deregistered in the database. .. attribute:: Connection.tag This read-write attribute initially contains the actual tag of the session that was acquired from a pool by :meth:`SessionPool.acquire()`. If the connection was not acquired from a pool or no tagging parameters were specified (tag and matchanytag) when the connection was acquired from the pool, this value will be None. If the value is changed, it must be a string containing name=value pairs like "k1=v1;k2=v2". If this value is not None when the connection is released back to the pool it will be used to retag the session. This value can be overridden in the call to :meth:`SessionPool.release()`. .. note:: This attribute is an extension to the DB API definition. .. versionadded:: 7.1 .. attribute:: Connection.tnsentry This read-only attribute returns the TNS entry of the database to which a connection has been established. .. deprecated:: 8.2 Use the attribute :attr:`~Connection.dsn` instead. .. note:: This attribute is an extension to the DB API definition. .. method:: Connection.unsubscribe(subscr) Unsubscribe from events in the database that were originally subscribed to using :meth:`~Connection.subscribe()`. The connection used to unsubscribe should be the same one used to create the subscription, or should access the same database and be connected as the same user name. .. versionadded:: 6.4 .. attribute:: Connection.username This read-only attribute returns the name of the user which established the connection to the database. .. note:: This attribute is an extension to the DB API definition. .. attribute:: Connection.version This read-only attribute returns the version of the database to which a connection has been established. .. note:: This attribute is an extension to the DB API definition. .. note:: If you connect to Oracle Database 18 or higher with client libraries 12.2 or lower that you will only receive the base version (such as 18.0.0.0.0) instead of the full version (18.3.0.0.0). python-cx_Oracle-8.3.0/doc/src/api_manual/cursor.rst000066400000000000000000000627511414105416400224640ustar00rootroot00000000000000.. _cursorobj: ************* Cursor Object ************* .. method:: Cursor.__enter__() The entry point for the cursor as a context manager. It returns itself. .. note:: This method is an extension to the DB API definition. .. method:: Cursor.__exit__() The exit point for the cursor as a context manager. It closes the cursor. .. note:: This method is an extension to the DB API definition. .. attribute:: Cursor.arraysize This read-write attribute can be used to tune the number of rows internally fetched and buffered by internal calls to the database when fetching rows from SELECT statements and REF CURSORS. The value can drastically affect the performance of a query since it directly affects the number of network round trips between Python and the database. For methods like :meth:`~Cursor.fetchone()` and :meth:`~Cursor.fetchall()` it does not change how many rows are returned to the application. For :meth:`~Cursor.fetchmany()` it is the default number of rows to fetch. Due to the performance benefits, the default ``Cursor.arraysize`` is 100 instead of the 1 that the DB API recommends. This value means that 100 rows are fetched by each internal call to the database. See :ref:`Tuning Fetch Performance ` for more information. .. attribute:: Cursor.bindarraysize This read-write attribute specifies the number of rows to bind at a time and is used when creating variables via :meth:`~Cursor.setinputsizes()` or :meth:`~Cursor.var()`. It defaults to 1 meaning to bind a single row at a time. .. note:: The DB API definition does not define this attribute. .. method:: Cursor.arrayvar(typ, value, [size]) Create an array variable associated with the cursor of the given type and size and return a :ref:`variable object `. The value is either an integer specifying the number of elements to allocate or it is a list and the number of elements allocated is drawn from the size of the list. If the value is a list, the variable is also set with the contents of the list. If the size is not specified and the type is a string or binary, 4000 bytes is allocated. This is needed for passing arrays to PL/SQL (in cases where the list might be empty and the type cannot be determined automatically) or returning arrays from PL/SQL. Array variables can only be used for PL/SQL associative arrays with contiguous keys. For PL/SQL associative arrays with sparsely populated keys or for varrays and nested tables, the approach shown in this `example `__ needs to be used. .. note:: The DB API definition does not define this method. .. method:: Cursor.bindnames() Return the list of bind variable names bound to the statement. Note that a statement must have been prepared first. .. note:: The DB API definition does not define this method. .. attribute:: Cursor.bindvars This read-only attribute provides the bind variables used for the last execute. The value will be either a list or a dictionary depending on whether binding was done by position or name. Care should be taken when referencing this attribute. In particular, elements should not be removed or replaced. .. note:: The DB API definition does not define this attribute. .. method:: Cursor.callfunc(name, returnType, parameters=[], \ keyword_parameters={}) Call a function with the given name. The return type is specified in the same notation as is required by :meth:`~Cursor.setinputsizes()`. The sequence of parameters must contain one entry for each parameter that the function expects. Any keyword parameters will be included after the positional parameters. The result of the call is the return value of the function. See :ref:`plsqlfunc` for an example. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the parameter `keywordParameters` was renamed to `keyword_parameters`. The old name will continue to work as a keyword parameter for a period of time. .. note:: The DB API definition does not define this method. .. note:: If you intend to call :meth:`Cursor.setinputsizes()` on the cursor prior to making this call, then note that the first item in the parameter list refers to the return value of the function. .. method:: Cursor.callproc(name, parameters=[], keyword_parameters={}) Call a procedure with the given name. The sequence of parameters must contain one entry for each parameter that the procedure expects. The result of the call is a modified copy of the input sequence. Input parameters are left untouched; output and input/output parameters are replaced with possibly new values. Keyword parameters will be included after the positional parameters and are not returned as part of the output sequence. See :ref:`plsqlproc` for an example. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the parameter `keywordParameters` was renamed to `keyword_parameters`. The old name will continue to work as a keyword parameter for a period of time. .. note:: The DB API definition does not allow for keyword parameters. .. method:: Cursor.close() Close the cursor now, rather than whenever __del__ is called. The cursor will be unusable from this point forward; an Error exception will be raised if any operation is attempted with the cursor. .. attribute:: Cursor.connection This read-only attribute returns a reference to the connection object on which the cursor was created. .. note:: This attribute is an extension to the DB API definition but it is mentioned in PEP 249 as an optional extension. .. data:: Cursor.description This read-only attribute is a sequence of 7-item sequences. Each of these sequences contains information describing one result column: (name, type, display_size, internal_size, precision, scale, null_ok). This attribute will be None for operations that do not return rows or if the cursor has not had an operation invoked via the :meth:`~Cursor.execute()` method yet. The type will be one of the :ref:`database type constants ` defined at the module level. .. method:: Cursor.execute(statement, parameters=[], ** keyword_parameters) Execute a statement against the database. See :ref:`sqlexecution`. Parameters may be passed as a dictionary or sequence or as keyword parameters. If the parameters are a dictionary, the values will be bound by name and if the parameters are a sequence the values will be bound by position. Note that if the values are bound by position, the order of the variables is from left to right as they are encountered in the statement and SQL statements are processed differently than PL/SQL statements. For this reason, it is generally recommended to bind parameters by name instead of by position. Parameters passed as a dictionary are name and value pairs. The name maps to the bind variable name used by the statement and the value maps to the Python value you wish bound to that bind variable. A reference to the statement will be retained by the cursor. If None or the same string object is passed in again, the cursor will execute that statement again without performing a prepare or rebinding and redefining. This is most effective for algorithms where the same statement is used, but different parameters are bound to it (many times). Note that parameters that are not passed in during subsequent executions will retain the value passed in during the last execution that contained them. For maximum efficiency when reusing an statement, it is best to use the :meth:`~Cursor.setinputsizes()` method to specify the parameter types and sizes ahead of time; in particular, None is assumed to be a string of length 1 so any values that are later bound as numbers or dates will raise a TypeError exception. If the statement is a query, the cursor is returned as a convenience to the caller (so it can be used directly as an iterator over the rows in the cursor); otherwise, ``None`` is returned. .. note:: The DB API definition does not define the return value of this method. .. method:: Cursor.executemany(statement, parameters, batcherrors=False, \ arraydmlrowcounts=False) Prepare a statement for execution against a database and then execute it against all parameter mappings or sequences found in the sequence parameters. See :ref:`batchstmnt`. The statement is managed in the same way as the :meth:`~Cursor.execute()` method manages it. If the size of the buffers allocated for any of the parameters exceeds 2 GB, you will receive the error "DPI-1015: array size of is too large", where varies with the size of each element being allocated in the buffer. If you receive this error, decrease the number of elements in the sequence parameters. If there are no parameters, or parameters have previously been bound, the number of iterations can be specified as an integer instead of needing to provide a list of empty mappings or sequences. When true, the batcherrors parameter enables batch error support within Oracle and ensures that the call succeeds even if an exception takes place in one or more of the sequence of parameters. The errors can then be retrieved using :meth:`~Cursor.getbatcherrors()`. When true, the arraydmlrowcounts parameter enables DML row counts to be retrieved from Oracle after the method has completed. The row counts can then be retrieved using :meth:`~Cursor.getarraydmlrowcounts()`. Both the batcherrors parameter and the arraydmlrowcounts parameter can only be true when executing an insert, update, delete or merge statement; in all other cases an error will be raised. For maximum efficiency, it is best to use the :meth:`~Cursor.setinputsizes()` method to specify the parameter types and sizes ahead of time; in particular, None is assumed to be a string of length 1 so any values that are later bound as numbers or dates will raise a TypeError exception. .. method:: Cursor.executemanyprepared(num_iters) Execute the previously prepared and bound statement the given number of times. The variables that are bound must have already been set to their desired value before this call is made. This method was designed for the case where optimal performance is required as it comes at the expense of compatibility with the DB API. .. note:: The DB API definition does not define this method. .. deprecated:: 6.4 Use :meth:`~Cursor.executemany()` instead with None for the statement argument and an integer for the parameters argument. .. method:: Cursor.fetchall() Fetch all (remaining) rows of a query result, returning them as a list of tuples. An empty list is returned if no more rows are available. Note that the cursor's arraysize attribute can affect the performance of this operation, as internally reads from the database are done in batches corresponding to the arraysize. An exception is raised if the previous call to :meth:`~Cursor.execute()` did not produce any result set or no call was issued yet. See :ref:`fetching` for an example. .. method:: Cursor.fetchmany(num_rows=cursor.arraysize) Fetch the next set of rows of a query result, returning a list of tuples. An empty list is returned if no more rows are available. Note that the cursor's arraysize attribute can affect the performance of this operation. The number of rows to fetch is specified by the parameter. If it is not given, the cursor's arraysize attribute determines the number of rows to be fetched. If the number of rows available to be fetched is fewer than the amount requested, fewer rows will be returned. An exception is raised if the previous call to :meth:`~Cursor.execute()` did not produce any result set or no call was issued yet. See :ref:`fetching` for an example. .. method:: Cursor.fetchone() Fetch the next row of a query result set, returning a single tuple or None when no more data is available. An exception is raised if the previous call to :meth:`~Cursor.execute()` did not produce any result set or no call was issued yet. See :ref:`fetching` for an example. .. method:: Cursor.fetchraw(num_rows=cursor.arraysize) Fetch the next set of rows of a query result into the internal buffers of the defined variables for the cursor. The number of rows actually fetched is returned. An exception is raised if the previous call to :meth:`~Cursor.execute()` did not produce any result set or no call was issued yet. .. deprecated:: 8.2 Use :meth:`Cursor.fetchmany()` instead. .. note:: The DB API definition does not define this method. .. attribute:: Cursor.fetchvars This read-only attribute specifies the list of variables created for the last query that was executed on the cursor. Care should be taken when referencing this attribute. In particular, elements should not be removed or replaced. .. note:: The DB API definition does not define this attribute. .. method:: Cursor.getarraydmlrowcounts() Retrieve the DML row counts after a call to :meth:`~Cursor.executemany()` with arraydmlrowcounts enabled. This will return a list of integers corresponding to the number of rows affected by the DML statement for each element of the array passed to :meth:`~Cursor.executemany()`. .. note:: The DB API definition does not define this method and it is only available for Oracle 12.1 and higher. .. method:: Cursor.getbatcherrors() Retrieve the exceptions that took place after a call to :meth:`~Cursor.executemany()` with batcherrors enabled. This will return a list of Error objects, one error for each iteration that failed. The offset can be determined by looking at the offset attribute of the error object. .. note:: The DB API definition does not define this method. .. method:: Cursor.getimplicitresults() Return a list of cursors which correspond to implicit results made available from a PL/SQL block or procedure without the use of OUT ref cursor parameters. The PL/SQL block or procedure opens the cursors and marks them for return to the client using the procedure dbms_sql.return_result. Cursors returned in this fashion should not be closed. They will be closed automatically by the parent cursor when it is closed. Closing the parent cursor will invalidate the cursors returned by this method. .. versionadded:: 5.3 .. note:: The DB API definition does not define this method and it is only available for Oracle Database 12.1 (both client and server must be at this level or higher). It is most like the DB API method nextset(), but unlike that method (which requires that the next result set overwrite the current result set), this method returns cursors which can be fetched independently of each other. .. attribute:: Cursor.inputtypehandler This read-write attribute specifies a method called for each value that is bound to a statement executed on the cursor and overrides the attribute with the same name on the connection if specified. The method signature is handler(cursor, value, arraysize) and the return value is expected to be a variable object or None in which case a default variable object will be created. If this attribute is None, the value of the attribute with the same name on the connection is used. .. note:: This attribute is an extension to the DB API definition. .. method:: Cursor.__iter__() Returns the cursor itself to be used as an iterator. .. note:: This method is an extension to the DB API definition but it is mentioned in PEP 249 as an optional extension. .. data:: Cursor.lastrowid This read-only attribute returns the rowid of the last row modified by the cursor. If no row was modified by the last operation performed on the cursor, the value None is returned. .. versionadded:: 7.3 .. attribute:: Cursor.outputtypehandler This read-write attribute specifies a method called for each column that is to be fetched from this cursor. The method signature is handler(cursor, name, defaultType, length, precision, scale) and the return value is expected to be a variable object or None in which case a default variable object will be created. If this attribute is None, the value of the attribute with the same name on the connection is used instead. See :ref:`outputtypehandlers`. .. note:: This attribute is an extension to the DB API definition. .. method:: Cursor.parse(statement) This can be used to parse a statement without actually executing it (this step is done automatically by Oracle when a statement is executed). .. note:: The DB API definition does not define this method. .. note:: You can parse any DML or DDL statement. DDL statements are executed immediately and an implied commit takes place. .. attribute:: Cursor.prefetchrows This read-write attribute can be used to tune the number of rows that the Oracle Client library fetches when a SELECT statement is executed. This value can reduce the number of round-trips to the database that are required to fetch rows but at the cost of additional memory. Setting this value to 0 can be useful when the timing of fetches must be explicitly controlled. See :ref:`Tuning Fetch Performance ` for more information. .. note:: The DB API definition does not define this method. .. method:: Cursor.prepare(statement, [tag]) This can be used before a call to :meth:`~Cursor.execute()` to define the statement that will be executed. When this is done, the prepare phase will not be performed when the call to :meth:`~Cursor.execute()` is made with None or the same string object as the statement. If specified the statement will be returned to the statement cache with the given tag. See the Oracle documentation for more information about the statement cache. See :ref:`Statement Caching ` for more information. .. note:: The DB API definition does not define this method. .. attribute:: Cursor.rowcount This read-only attribute specifies the number of rows that have currently been fetched from the cursor (for select statements), that have been affected by the operation (for insert, update, delete and merge statements), or the number of successful executions of the statement (for PL/SQL statements). .. attribute:: Cursor.rowfactory This read-write attribute specifies a method to call for each row that is retrieved from the database. Ordinarily a tuple is returned for each row but if this attribute is set, the method is called with the tuple that would normally be returned, and the result of the method is returned instead. See :ref:`rowfactories`. .. note:: The DB API definition does not define this attribute. .. method:: Cursor.scroll(value=0, mode="relative") Scroll the cursor in the result set to a new position according to the mode. If mode is "relative" (the default value), the value is taken as an offset to the current position in the result set. If set to "absolute", value states an absolute target position. If set to "first", the cursor is positioned at the first row and if set to "last", the cursor is set to the last row in the result set. An error is raised if the mode is "relative" or "absolute" and the scroll operation would position the cursor outside of the result set. .. versionadded:: 5.3 .. note:: This method is an extension to the DB API definition but it is mentioned in PEP 249 as an optional extension. .. attribute:: Cursor.scrollable This read-write boolean attribute specifies whether the cursor can be scrolled or not. By default, cursors are not scrollable, as the server resources and response times are greater than nonscrollable cursors. This attribute is checked and the corresponding mode set in Oracle when calling the method :meth:`~Cursor.execute()`. .. versionadded:: 5.3 .. note:: The DB API definition does not define this attribute. .. method:: Cursor.setinputsizes(\*args, \*\*keywordArgs) This can be used before a call to :meth:`~Cursor.execute()`, :meth:`~Cursor.callfunc()` or :meth:`~Cursor.callproc()` to predefine memory areas for the operation's parameters. Each parameter should be a type object corresponding to the input that will be used or it should be an integer specifying the maximum length of a string parameter. Use keyword parameters when binding by name and positional parameters when binding by position. The singleton None can be used as a parameter when using positional parameters to indicate that no space should be reserved for that position. .. note:: If you plan to use :meth:`~Cursor.callfunc()` then be aware that the first parameter in the list refers to the return value of the function. .. method:: Cursor.setoutputsize(size, [column]) This method does nothing and is retained solely for compatibility with the DB API. The module automatically allocates as much space as needed to fetch LONG and LONG RAW columns (or CLOB as string and BLOB as bytes). .. attribute:: Cursor.statement This read-only attribute provides the string object that was previously prepared with :meth:`~Cursor.prepare()` or executed with :meth:`~Cursor.execute()`. .. note:: The DB API definition does not define this attribute. .. method:: Cursor.var(typ, [size, arraysize, inconverter, outconverter, \ typename, encoding_errors, bypass_decode]) Create a variable with the specified characteristics. This method was designed for use with PL/SQL in/out variables where the length or type cannot be determined automatically from the Python object passed in or for use in input and output type handlers defined on cursors or connections. The typ parameter specifies the type of data that should be stored in the variable. This should be one of the :ref:`database type constants `, :ref:`DB API constants `, an object type returned from the method :meth:`Connection.gettype()` or one of the following Python types: .. list-table:: :header-rows: 1 * - Python Type - Database Type * - bool - :attr:`cx_Oracle.DB_TYPE_BOOLEAN` * - bytes - :attr:`cx_Oracle.DB_TYPE_RAW` * - datetime.date - :attr:`cx_Oracle.DB_TYPE_DATE` * - datetime.datetime - :attr:`cx_Oracle.DB_TYPE_DATE` * - datetime.timedelta - :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS` * - decimal.Decimal - :attr:`cx_Oracle.DB_TYPE_NUMBER` * - float - :attr:`cx_Oracle.DB_TYPE_NUMBER` * - int - :attr:`cx_Oracle.DB_TYPE_NUMBER` * - str - :attr:`cx_Oracle.DB_TYPE_VARCHAR` The size parameter specifies the length of string and raw variables and is ignored in all other cases. If not specified for string and raw variables, the value 4000 is used. The arraysize parameter specifies the number of elements the variable will have. If not specified the bind array size (usually 1) is used. When a variable is created in an output type handler this parameter should be set to the cursor's array size. The inconverter and outconverter parameters specify methods used for converting values to/from the database. More information can be found in the section on :ref:`variable objects`. The typename parameter specifies the name of a SQL object type and must be specified when using type :data:`cx_Oracle.OBJECT` unless the type object was passed directly as the first parameter. The encoding_errors parameter specifies what should happen when decoding byte strings fetched from the database into strings. It should be one of the values noted in the builtin `decode `__ function. The bypass_decode parameter, if specified, should be passed as a boolean value. Passing a `True` value causes values of database types :data:`~cx_Oracle.DB_TYPE_VARCHAR`, :data:`~cx_Oracle.DB_TYPE_CHAR`, :data:`~cx_Oracle.DB_TYPE_NVARCHAR`, :data:`~cx_Oracle.DB_TYPE_NCHAR` and :data:`~cx_Oracle.DB_TYPE_LONG` to be returned as `bytes` instead of `str`, meaning that cx_Oracle doesn't do any decoding. See :ref:`Fetching raw data ` for more information. .. versionadded:: 8.2 The parameter `bypass_decode` was added. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the parameter `encodingErrors` was renamed to `encoding_errors`. The old name will continue to work as a keyword parameter for a period of time. .. note:: The DB API definition does not define this method. python-cx_Oracle-8.3.0/doc/src/api_manual/deprecations.rst000066400000000000000000000156351414105416400236260ustar00rootroot00000000000000.. _deprecations: ************ Deprecations ************ The following tables contains all of the deprecations in the cx_Oracle API, when they were first deprecated and a comment on what should be used instead, if applicable. The most recent deprecations are listed first. .. list-table:: Deprecated in 8.2 :header-rows: 1 :widths: 25 75 :width: 100% :name: _deprecations_8_2 * - Name - Comments * - `encoding` parameter to :meth:`cx_Oracle.connect()` - No longer needed as the use of encodings other than UTF-8 is deprecated. Encoding is handled internally between cx_Oracle and Oracle Database. * - `nencoding` parameter to :meth:`cx_Oracle.connect()` - No longer needed as the use of encodings other than UTF-8 is deprecated. * - `encoding` parameter to :meth:`cx_Oracle.SessionPool()` - No longer needed as the use of encodings other than UTF-8 is deprecated. * - `nencoding` parameter to :meth:`cx_Oracle.SessionPool()` - No longer needed as the use of encodings other than UTF-8 is deprecated. * - Connection.maxBytesPerCharacter - No longer needed as the use of encodings other than UTF-8 is deprecated. The constant value 4 can be used instead. * - Positional parameters to :meth:`cx_Oracle.connect()` - Replace with keyword parameters in order to comply with the Python database API. * - Positional parameters to :meth:`cx_Oracle.SessionPool()` - Replace with keyword parameters in order to comply with the Python database API. * - `threaded` parameter to :meth:`cx_Oracle.SessionPool()` - The value of this parameter is ignored. Threading is now always used. * - `waitTimeout` parameter to :meth:`cx_Oracle.SessionPool()` - Replace with parameter name `wait_timeout` * - `maxLifetimeSession` parameter to :meth:`cx_Oracle.SessionPool()` - Replace with parameter name `max_lifetime_session` * - `sessionCallback` parameter to :meth:`cx_Oracle.SessionPool()` - Replace with parameter name `session_callback` * - `maxSessionsPerShard` parameter to :meth:`cx_Oracle.SessionPool()` - Replace with parameter name `max_sessions_per_shard` * - `SessionPool.tnsentry` - Replace with :data:`SessionPool.dsn` * - `payloadType` parameter to :meth:`Connection.queue()` - Replace with parameter name `payload_type` if using keyword parameters. * - `ipAddress` parameter to :meth:`Connection.subscribe()` - Replace with parameter name `ip_address` * - `groupingClass` parameter to :meth:`Connection.subscribe()` - Replace with parameter name `grouping_class` * - `groupingValue` parameter to :meth:`Connection.subscribe()` - Replace with parameter name `grouping_value` * - `groupingType` parameter to :meth:`Connection.subscribe()` - Replace with parameter name `grouping_type` * - `clientInitiated` parameter to :meth:`Connection.subscribe()` - Replace with parameter name `client_initiated` * - `Connection.callTimeout` - Replace with :data:`Connection.call_timeout` * - `Connection.tnsentry` - Replace with :data:`Connection.dsn` * - `keywordParameters` parameter to :meth:`Cursor.callfunc()` - Replace with parameter name `keyword_parameters` * - `keywordParameters` parameter to :meth:`Cursor.callproc()` - Replace with parameter name `keyword_parameters` * - `encodingErrors` parameter to :meth:`Cursor.var()` - Replace with parameter name `encoding_errors` * - `Cursor.fetchraw()` - Replace with :meth:`Cursor.fetchmany()` * - `newSize` parameter to :meth:`LOB.trim()` - Replace with parameter name `new_size` * - `Queue.deqMany` - Replace with :meth:`Queue.deqmany()` * - `Queue.deqOne` - Replace with :meth:`Queue.deqone()` * - `Queue.enqMany` - Replace with :meth:`Queue.enqmany()` * - `Queue.enqOne` - Replace with :meth:`Queue.enqone()` * - `Queue.deqOptions` - Replace with :data:`Queue.deqoptions` * - `Queue.enqOptions` - Replace with :meth:`Queue.enqoptions` * - `Queue.payloadType` - Replace with :meth:`Queue.payload_type` * - `Subscription.ipAddress` - Replace with :attr:`Subscription.ip_address` * - `Message.consumerName` - Replace with :attr:`Message.consumer_name` * - `Message.queueName` - Replace with :attr:`Message.queue_name` * - `Variable.actualElements` - Replace with :attr:`Variable.actual_elements` * - `Variable.bufferSize` - Replace with :attr:`Variable.buffer_size` * - `Variable.numElements` - Replace with :attr:`Variable.num_elements` .. list-table:: Deprecated in 8.0 :header-rows: 1 :widths: 25 75 :width: 100% :name: _deprecations_8_0 * - Name - Comments * - cx_Oracle.BFILE - Replace with :data:`cx_Oracle.DB_TYPE_BFILE` * - cx_Oracle.BLOB - Replace with :data:`cx_Oracle.DB_TYPE_BLOB` * - cx_Oracle.BOOLEAN - Replace with :data:`cx_Oracle.DB_TYPE_BOOLEAN` * - cx_Oracle.CLOB - Replace with :data:`cx_Oracle.DB_TYPE_CLOB` * - cx_Oracle.CURSOR - Replace with :data:`cx_Oracle.DB_TYPE_CURSOR` * - cx_Oracle.FIXED_CHAR - Replace with :data:`cx_Oracle.DB_TYPE_CHAR` * - cx_Oracle.FIXED_NCHAR - Replace with :data:`cx_Oracle.DB_TYPE_NCHAR` * - cx_Oracle.INTERVAL - Replace with :data:`cx_Oracle.DB_TYPE_INTERVAL_DS` * - cx_Oracle.LONG_BINARY - Replace with :data:`cx_Oracle.DB_TYPE_LONG_RAW` * - cx_Oracle.LONG_STRING - Replace with :data:`cx_Oracle.DB_TYPE_LONG` * - cx_Oracle.NATIVE_FLOAT - Replace with :data:`cx_Oracle.DB_TYPE_BINARY_DOUBLE` * - cx_Oracle.NATIVE_INT - Replace with :data:`cx_Oracle.DB_TYPE_BINARY_INTEGER` * - cx_Oracle.NCHAR - Replace with :data:`cx_Oracle.DB_TYPE_NVARCHAR` * - cx_Oracle.NCLOB - Replace with :data:`cx_Oracle.DB_TYPE_NCLOB` * - cx_Oracle.OBJECT - Replace with :data:`cx_Oracle.DB_TYPE_OBJECT` * - cx_Oracle.TIMESTAMP - Replace with :data:`cx_Oracle.DB_TYPE_TIMESTAMP` .. list-table:: Deprecated in 7.2 :header-rows: 1 :widths: 25 75 :width: 100% :name: _deprecations_7_2 * - Name - Comments * - Connection.deq() - Replace with :meth:`Queue.deqone()` or :meth:`Queue.deqmany()`. * - Connection.deqoptions() - Replace with attribute :attr:`Queue.deqoptions`. * - Connection.enq() - Replace with :meth:`Queue.enqone()` or :meth:`Queue.enqmany()`. * - Connection.enqoptions() - Replace with attribute :attr:`Queue.enqoptions`. .. list-table:: Deprecated in 6.4 :header-rows: 1 :widths: 25 75 :width: 100% :name: _deprecations_6_4 * - Name - Comments * - Cursor.executemanyprepared() - Replace with :meth:`~Cursor.executemany()` with None for the statement argument and an integer for the parameters argument. python-cx_Oracle-8.3.0/doc/src/api_manual/lob.rst000066400000000000000000000056701414105416400217200ustar00rootroot00000000000000.. _lobobj: *********** LOB Objects *********** See :ref:`lobdata` for more information about using LOBs. .. note:: This object is an extension the DB API. It is returned whenever Oracle :data:`CLOB`, :data:`BLOB` and :data:`BFILE` columns are fetched. .. method:: LOB.close() Close the LOB. Call this when writing is completed so that the indexes associated with the LOB can be updated -- but only if :meth:`~LOB.open()` was called first. .. method:: LOB.fileexists() Return a boolean indicating if the file referenced by the BFILE type LOB exists. .. method:: LOB.getchunksize() Return the chunk size for the internal LOB. Reading and writing to the LOB in chunks of multiples of this size will improve performance. .. method:: LOB.getfilename() Return a two-tuple consisting of the directory alias and file name for a BFILE type LOB. .. method:: LOB.isopen() Return a boolean indicating if the LOB has been opened using the method :meth:`~LOB.open()`. .. method:: LOB.open() Open the LOB for writing. This will improve performance when writing to a LOB in chunks and there are functional or extensible indexes associated with the LOB. If this method is not called, each write will perform an open internally followed by a close after the write has been completed. .. method:: LOB.read([offset=1, [amount]]) Return a portion (or all) of the data in the LOB object. Note that the amount and offset are in bytes for BLOB and BFILE type LOBs and in UCS-2 code points for CLOB and NCLOB type LOBs. UCS-2 code points are equivalent to characters for all but supplemental characters. If supplemental characters are in the LOB, the offset and amount will have to be chosen carefully to avoid splitting a character. .. method:: LOB.setfilename(dirAlias, name) Set the directory alias and name of the BFILE type LOB. .. method:: LOB.size() Returns the size of the data in the LOB object. For BLOB and BFILE type LOBs this is the number of bytes. For CLOB and NCLOB type LOBs this is the number of UCS-2 code points. UCS-2 code points are equivalent to characters for all but supplemental characters. .. method:: LOB.trim(new_size=0) Trim the LOB to the new size. .. attribute:: LOB.type This read-only attribute returns the type of the LOB as one of the :ref:`database type constants `. .. versionadded:: 8.0 .. method:: LOB.write(data, offset=1) Write the data to the LOB object at the given offset. The offset is in bytes for BLOB type LOBs and in UCS-2 code points for CLOB and NCLOB type LOBs. UCS-2 code points are equivalent to characters for all but supplemental characters. If supplemental characters are in the LOB, the offset will have to be chosen carefully to avoid splitting a character. Note that if you want to make the LOB value smaller, you must use the :meth:`~LOB.trim()` function. python-cx_Oracle-8.3.0/doc/src/api_manual/module.rst000066400000000000000000001441701414105416400224300ustar00rootroot00000000000000.. module:: cx_Oracle .. _module: **************** Module Interface **************** .. data:: __future__ Special object which contains attributes which control the behavior of cx_Oracle, allowing for opting in for new features. No attributes are currently supported so all attributes will silently ignore being set and will always appear to have the value None. .. note:: This method is an extension to the DB API definition. .. versionadded:: 6.2 .. function:: Binary(string) Construct an object holding a binary (long) string value. .. function:: clientversion() Return the version of the client library being used as a 5-tuple. The five values are the major version, minor version, update number, patch number and port update number. .. note:: This method is an extension to the DB API definition. .. function:: connect(user=None, password=None, dsn=None, \ mode=cx_Oracle.DEFAULT_AUTH, handle=0, pool=None, threaded=False, \ events=False, cclass=None, purity=cx_Oracle.ATTR_PURITY_DEFAULT, \ newpassword=None, encoding=None, nencoding=None, edition=None, \ appcontext=[], tag=None, matchanytag=None, shardingkey=[], \ supershardingkey=[], stmtcachesize=20) Connection(user=None, password=None, dsn=None, \ mode=cx_Oracle.DEFAULT_AUTH, handle=0, pool=None, threaded=False, \ events=False, cclass=None, purity=cx_Oracle.ATTR_PURITY_DEFAULT, \ newpassword=None, encoding=None, nencoding=None, edition=None, \ appcontext=[], tag=None, matchanytag=False, shardingkey=[], \ supershardingkey=[], stmtcachesize=20) Constructor for creating a connection to the database. Return a :ref:`connection object `. All parameters are optional and can be specified as keyword parameters. See :ref:`connhandling` information about connections. The dsn (data source name) is the TNS entry (from the Oracle names server or tnsnames.ora file) or is a string like the one returned from :meth:`~cx_Oracle.makedsn()`. If the user parameter is passed and the password and dsn parameters are not passed, the user parameter is assumed to be a connect string in the format ``user/password@dsn``, the same format accepted by Oracle applications such as SQL\*Plus. See :ref:`connstr` for more information. If the mode is specified, it must be one of the :ref:`connection authorization modes` which are defined at the module level. If the handle is specified, it must be of type OCISvcCtx\* and is only of use when embedding Python in an application (like PowerBuilder) which has already made the connection. The connection thus created should *never* be used after the source handle has been closed or destroyed. The pool parameter is expected to be a :ref:`session pool object ` and the use of this parameter is the equivalent of calling :meth:`SessionPool.acquire()`. Parameters not accepted by that method are ignored. The threaded parameter is expected to be a boolean expression which indicates whether or not Oracle should wrap accesses to connections with a mutex. The events parameter is expected to be a boolean expression which indicates whether or not to initialize Oracle in events mode. This is required for continuous query notification and high availability event notifications. The cclass parameter is expected to be a string and defines the connection class for database resident connection pooling (DRCP). The purity parameter is expected to be one of :data:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or :data:`~cx_Oracle.ATTR_PURITY_DEFAULT`. The newpassword parameter is expected to be a string if specified and sets the password for the logon during the connection process. See the :ref:`globalization ` section for details on the encoding and nencoding parameters. Note the default encoding and nencoding values changed to "UTF-8" in cx_Oracle 8, and any character set in NLS_LANG is ignored. In a future release of cx_Oracle, only UTF-8 will be supported. The edition parameter is expected to be a string if specified and sets the edition to use for the session. It is only relevant if both the client and the database are at least Oracle Database 11.2. If this parameter is used with the cclass parameter the exception "DPI-1058: edition not supported with connection class" will be raised. The appcontext parameter is expected to be a list of 3-tuples, if specified, and sets the application context for the connection. Application context is available in the database by using the sys_context() PL/SQL method and can be used within a logon trigger as well as any other PL/SQL procedures. Each entry in the list is expected to contain three strings: the namespace, the name and the value. The tag parameter, if specified, is expected to be a string and will limit the sessions that can be returned from a session pool unless the matchanytag parameter is set to True. In that case sessions with the specified tag will be preferred over others, but if no such sessions are available a session with a different tag may be returned instead. In any case, untagged sessions will always be returned if no sessions with the specified tag are available. Sessions are tagged when they are :meth:`released ` back to the pool. The shardingkey and supershardingkey parameters, if specified, are expected to be a sequence of values which will be used to identify the database shard to connect to. The key values can be strings, numbers, bytes or dates. The stmtcachesize parameter, if specified, is expected to be an integer which specifies the initial value of :data:`~Connection.stmtcachesize`. .. versionchanged:: 8.2 The parameter `stmtcachesize` was added. .. function:: Cursor(connection) Constructor for creating a cursor. Return a new :ref:`cursor object ` using the connection. .. note:: This method is an extension to the DB API definition. .. function:: Date(year, month, day) Construct an object holding a date value. .. function:: DateFromTicks(ticks) Construct an object holding a date value from the given ticks value (number of seconds since the epoch; see the documentation of the standard Python time module for details). .. function:: init_oracle_client(lib_dir=None, config_dir=None, \ error_url=None, driver_name=None) Initialize the Oracle client library now, rather than when :func:`cx_Oracle.clientversion()`, :func:`cx_Oracle.connect()` or :func:`cx_Oracle.SessionPool()` is called for the first time. If initialization has already taken place, an exception is raised. If the parameter `lib_dir` is not `None` or the empty string, the specified directory is the only one searched for the Oracle Client libraries; otherwise, the standard way of locating the Oracle Client library is used. If the parameter `config_dir` is not `None` or the empty string, the specified directory is used to find Oracle Client library configuration files. This is equivalent to setting the environment variable `TNS_ADMIN` and overrides any value already set in `TNS_ADMIN`. If this parameter is not set, the standard way of locating Oracle Client library configuration files is used. If the parameter `error_url` is not `None` or the empty string, the specified value is included in the message of the exception raised when the Oracle Client library cannot be loaded; otherwise, the :ref:`installation` URL is included. If the parameter `driver_name` is not `None` or the empty string, the specified value can be found in database views that give information about connections. For example, it is in the ``CLIENT_DRIVER`` column of ``V$SESSION_CONNECT_INFO``. The standard is to set this value to ``" : version>"``, where is the name of the driver and is its version. There should be a single space character before and after the colon. If this value is not specified, then the default value of "cx_Oracle : " is used. See :ref:`initialization` for more discussion. .. note:: This method is an extension to the DB API definition. .. function:: makedsn(host, port, sid=None, service_name=None, region=None, \ sharding_key=None, super_sharding_key=None) Return a string suitable for use as the dsn parameter for :meth:`~cx_Oracle.connect()`. This string is identical to the strings that are defined by the Oracle names server or defined in the tnsnames.ora file. .. note:: This method is an extension to the DB API definition. .. function:: SessionPool(user=None, password=None, dsn=None, min=1, max=2, \ increment=1, connectiontype=cx_Oracle.Connection, threaded=True, \ getmode=cx_Oracle.SPOOL_ATTRVAL_NOWAIT, events=False, \ homogeneous=True, externalauth=False, encoding=None, nencoding=None, \ edition=None, timeout=0, wait_timeout=0, max_lifetime_session=0, \ session_callback=None, max_sessions_per_shard=0, \ soda_metadata_cache=False, stmtcachesize=20, ping_interval=60) Create and return a :ref:`session pool object `. Session pooling (also known as connection pooling) creates a pool of available connections to the database, allowing applications to acquire a connection very quickly. It is of primary use in a server where connections are requested in rapid succession and used for a short period of time, for example in a web server. See :ref:`connpool` for more information. Connection pooling in cx_Oracle is handled by Oracle's `Session pooling `__ technology. This allows cx_Oracle applications to support features like `Application Continuity `__. The user, password and dsn parameters are the same as for :meth:`cx_Oracle.connect()` The min, max and increment parameters control pool growth behavior. A fixed pool size where min equals max is recommended to help prevent connection storms and to help overall system stability. The min parameter is the number of connections opened when the pool is created. The increment is the number of connections that are opened whenever a connection request exceeds the number of currently open connections. The max parameter is the maximum number of connections that can be open in the connection pool. Note that when using :ref:`external authentication `, :ref:`heterogeneous pools `, or :ref:`drcp`, then the pool growth behavior is different. In these cases the number of connections created at pool startup is always zero, and the increment is always one. If the connectiontype parameter is specified, all calls to :meth:`~SessionPool.acquire()` will create connection objects of that type, rather than the base type defined at the module level. The getmode parameter indicates whether or not future :func:`SessionPool.acquire()` calls will wait for available connections. It can be one of the :ref:`Session Pool Get Modes ` values. The events parameter is expected to be a boolean expression which indicates whether or not to initialize Oracle in events mode. This is required for continuous query notification and high availability event notifications. The homogeneous parameter is expected to be a boolean expression which indicates whether or not to create a homogeneous pool. A homogeneous pool requires that all connections in the pool use the same credentials. As such proxy authentication and external authentication is not possible with a homogeneous pool. See :ref:`Heterogeneous and Homogeneous Connection Pools `. The externalauth parameter is expected to be a boolean expression which indicates whether or not external authentication should be used. External authentication implies that something other than the database is authenticating the user to the database. This includes the use of operating system authentication and Oracle wallets. See :ref:`Connecting Using External Authentication `. The encoding and nencoding parameters set the encodings used for string values transferred between cx_Oracle and Oracle Database, see :ref:`Character Sets and Globalization `. Note the default encoding and nencoding values changed to "UTF-8" in cx_Oracle 8, and any character set in NLS_LANG is ignored. In a future release of cx_Oracle, only UTF-8 will be supported. The edition parameter is expected to be a string, if specified, and sets the edition to use for the sessions in the pool. It is only relevant if both the client and the server are at least Oracle Database 11.2. See :ref:`Edition-Based Redefinition (EBR) `. The timeout parameter is expected to be an integer, if specified, and sets the length of time (in seconds) after which idle sessions in the pool are terminated. Note that termination only occurs when the pool is accessed. The default value of 0 means that no idle sessions are terminated. The initial pool timeout must be non-zero if you subsequently want to change the timeout with :meth:`SessionPool.reconfigure()`. The wait_timeout parameter is expected to be an integer, if specified, and sets the length of time (in milliseconds) that the caller should wait for a session to become available in the pool before returning with an error. This value is only used if the getmode parameter is set to the value :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`. The max_lifetime_session parameter is expected to be an integer, if specified, and sets the maximum length of time (in seconds) a pooled session may exist. Sessions that are in use will not be closed. They become candidates for termination only when they are released back to the pool and have existed for longer than max_lifetime_session seconds. Note that termination only occurs when the pool is accessed. The default value is 0 which means that there is no maximum length of time that a pooled session may exist. The session_callback parameter is expected to be either a string or a callable. If the session_callback parameter is a callable, it will be called when a newly created connection is returned from the pool, or when a tag is requested and that tag does not match the connection's actual tag. The callable will be invoked with the connection and the requested tag as its only parameters. If the parameter is a string, it should be the name of a PL/SQL procedure that will be called when :func:`SessionPool.acquire()` requests a tag and that tag does not match the connection's actual tag. See :ref:`Session CallBacks for Setting Pooled Connection State `. Support for the PL/SQL procedure requires Oracle Client libraries 12.2 or later. See the `OCI documentation `__ for more information. The max_sessions_per_shard parameter is expected to be an integer, if specified. Setting this greater than zero specifies the maximum number of sessions in the pool that can be used for any given shard in a sharded database. This lets connections in the pool be balanced across the shards. A value of zero will not set any maximum number of sessions for each shard. This value is ignored if the Oracle client library version is less than 18.3. The soda_metadata_cache parameter is expected to be a boolean expresion which indicates whether or not to enable the SODA metatata cache. This significantly improves the performance of methods :meth:`SodaDatabase.createCollection()` and :meth:`SodaDatabase.openCollection()` but can become out of date as it does not track changes made externally. It is also only available in Oracle Client 19.11 and higher. See :ref:`Using the SODA Metadata Cache `. The stmtcachesize parameter, if specified, is expected to be an integer which specifies the initial value of :data:`~SessionPool.stmtcachesize`. The ping_interval parameter, if specified, is expected to be an integer which specifies the initial value of :data:`~SessionPool.ping_interval`. .. note:: This method is an extension to the DB API definition. .. versionchanged:: 8.2 The parameters `soda_metadata_cache`, `stmtcachesize` and `ping_interval` were added. For consistency and compliance with the PEP 8 naming style, the parameter `waitTimeout` was renamed to `wait_timeout`, the parameter `maxLifetimeSession` was renamed to `max_lifetime_session`, the parameter `sessionCallback` was renamed to `session_callback` and the parameter `maxSessionsPerShard` was renamed to `max_sessions_per_shard`. The old names will continue to work as keyword parameters for a period of time. The `threaded` parameter value is ignored and threading is always enabled; in previous versions the default was False. .. function:: Time(hour, minute, second) Construct an object holding a time value. .. note:: The time only data type is not supported by Oracle. Calling this function will raise a NotSupportedError exception. .. function:: TimeFromTicks(ticks) Construct an object holding a time value from the given ticks value (number of seconds since the epoch; see the documentation of the standard Python time module for details). .. note:: The time only data type is not supported by Oracle. Calling this function will raise a NotSupportedError exception. .. function:: Timestamp(year, month, day, hour, minute, second) Construct an object holding a time stamp value. .. function:: TimestampFromTicks(ticks) Construct an object holding a time stamp value from the given ticks value (number of seconds since the epoch; see the documentation of the standard Python time module for details). .. _constants: Constants ========= General ------- .. data:: apilevel String constant stating the supported DB API level. Currently '2.0'. .. data:: buildtime String constant stating the time when the binary was built. .. note:: This constant is an extension to the DB API definition. .. data:: paramstyle String constant stating the type of parameter marker formatting expected by the interface. Currently 'named' as in 'where name = :name'. .. data:: threadsafety Integer constant stating the level of thread safety that the interface supports. Currently 2, which means that threads may share the module and connections, but not cursors. Sharing means that a thread may use a resource without wrapping it using a mutex semaphore to implement resource locking. Note that in order to make use of multiple threads in a program which intends to connect and disconnect in different threads, the threaded parameter to :meth:`connect()` must be `True`. .. data:: version .. data:: __version__ String constant stating the version of the module. Currently '|release|'. .. note:: This attribute is an extension to the DB API definition. Advanced Queuing: Delivery Modes -------------------------------- These constants are extensions to the DB API definition. They are possible values for the :attr:`~DeqOptions.deliverymode` attribute of the :ref:`dequeue options object ` passed as the options parameter to the :meth:`Connection.deq()` method as well as the :attr:`~EnqOptions.deliverymode` attribute of the :ref:`enqueue options object ` passed as the options parameter to the :meth:`Connection.enq()` method. They are also possible values for the :attr:`~MessageProperties.deliverymode` attribute of the :ref:`message properties object ` passed as the msgproperties parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()` methods. .. data:: MSG_BUFFERED This constant is used to specify that enqueue/dequeue operations should enqueue or dequeue buffered messages. .. data:: MSG_PERSISTENT This constant is used to specify that enqueue/dequeue operations should enqueue or dequeue persistent messages. This is the default value. .. data:: MSG_PERSISTENT_OR_BUFFERED This constant is used to specify that dequeue operations should dequeue either persistent or buffered messages. Advanced Queuing: Dequeue Modes ------------------------------- These constants are extensions to the DB API definition. They are possible values for the :attr:`~DeqOptions.mode` attribute of the :ref:`dequeue options object `. This object is the options parameter for the :meth:`Connection.deq()` method. .. data:: DEQ_BROWSE This constant is used to specify that dequeue should read the message without acquiring any lock on the message (equivalent to a select statement). .. data:: DEQ_LOCKED This constant is used to specify that dequeue should read and obtain a write lock on the message for the duration of the transaction (equivalent to a select for update statement). .. data:: DEQ_REMOVE This constant is used to specify that dequeue should read the message and update or delete it. This is the default value. .. data:: DEQ_REMOVE_NODATA This constant is used to specify that dequeue should confirm receipt of the message but not deliver the actual message content. Advanced Queuing: Dequeue Navigation Modes ------------------------------------------ These constants are extensions to the DB API definition. They are possible values for the :attr:`~DeqOptions.navigation` attribute of the :ref:`dequeue options object `. This object is the options parameter for the :meth:`Connection.deq()` method. .. data:: DEQ_FIRST_MSG This constant is used to specify that dequeue should retrieve the first available message that matches the search criteria. This resets the position to the beginning of the queue. .. data:: DEQ_NEXT_MSG This constant is used to specify that dequeue should retrieve the next available message that matches the search criteria. If the previous message belongs to a message group, AQ retrieves the next available message that matches the search criteria and belongs to the message group. This is the default. .. data:: DEQ_NEXT_TRANSACTION This constant is used to specify that dequeue should skip the remainder of the transaction group and retrieve the first message of the next transaction group. This option can only be used if message grouping is enabled for the current queue. Advanced Queuing: Dequeue Visibility Modes ------------------------------------------ These constants are extensions to the DB API definition. They are possible values for the :attr:`~DeqOptions.visibility` attribute of the :ref:`dequeue options object `. This object is the options parameter for the :meth:`Connection.deq()` method. .. data:: DEQ_IMMEDIATE This constant is used to specify that dequeue should perform its work as part of an independent transaction. .. data:: DEQ_ON_COMMIT This constant is used to specify that dequeue should be part of the current transaction. This is the default value. Advanced Queuing: Dequeue Wait Modes ------------------------------------ These constants are extensions to the DB API definition. They are possible values for the :attr:`~DeqOptions.wait` attribute of the :ref:`dequeue options object `. This object is the options parameter for the :meth:`Connection.deq()` method. .. data:: DEQ_NO_WAIT This constant is used to specify that dequeue not wait for messages to be available for dequeuing. .. data:: DEQ_WAIT_FOREVER This constant is used to specify that dequeue should wait forever for messages to be available for dequeuing. This is the default value. Advanced Queuing: Enqueue Visibility Modes ------------------------------------------ These constants are extensions to the DB API definition. They are possible values for the :attr:`~EnqOptions.visibility` attribute of the :ref:`enqueue options object `. This object is the options parameter for the :meth:`Connection.enq()` method. .. data:: ENQ_IMMEDIATE This constant is used to specify that enqueue should perform its work as part of an independent transaction. .. data:: ENQ_ON_COMMIT This constant is used to specify that enqueue should be part of the current transaction. This is the default value. Advanced Queuing: Message States -------------------------------- These constants are extensions to the DB API definition. They are possible values for the :attr:`~MessageProperties.state` attribute of the :ref:`message properties object `. This object is the msgproperties parameter for the :meth:`Connection.deq()` and :meth:`Connection.enq()` methods. .. data:: MSG_EXPIRED This constant is used to specify that the message has been moved to the exception queue. .. data:: MSG_PROCESSED This constant is used to specify that the message has been processed and has been retained. .. data:: MSG_READY This constant is used to specify that the message is ready to be processed. .. data:: MSG_WAITING This constant is used to specify that the message delay has not yet been reached. Advanced Queuing: Other ----------------------- These constants are extensions to the DB API definition. They are special constants used in advanced queuing. .. data:: MSG_NO_DELAY This constant is a possible value for the :attr:`~MessageProperties.delay` attribute of the :ref:`message properties object ` passed as the msgproperties parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()` methods. It specifies that no delay should be imposed and the message should be immediately available for dequeuing. This is also the default value. .. data:: MSG_NO_EXPIRATION This constant is a possible value for the :attr:`~MessageProperties.expiration` attribute of the :ref:`message properties object ` passed as the msgproperties parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()` methods. It specifies that the message never expires. This is also the default value. .. _connection-authorization-modes: Connection Authorization Modes ------------------------------ These constants are extensions to the DB API definition. They are possible values for the mode parameter of the :meth:`connect()` method. .. data:: DEFAULT_AUTH This constant is used to specify that default authentication is to take place. This is the default value if no mode is passed at all. .. versionadded:: 7.2 .. data:: PRELIM_AUTH This constant is used to specify that preliminary authentication is to be used. This is needed for performing database startup and shutdown. .. data:: SYSASM This constant is used to specify that SYSASM access is to be acquired. .. data:: SYSBKP This constant is used to specify that SYSBACKUP access is to be acquired. .. data:: SYSDBA This constant is used to specify that SYSDBA access is to be acquired. .. data:: SYSDGD This constant is used to specify that SYSDG access is to be acquired. .. data:: SYSKMT This constant is used to specify that SYSKM access is to be acquired. .. data:: SYSOPER This constant is used to specify that SYSOPER access is to be acquired. .. data:: SYSRAC This constant is used to specify that SYSRAC access is to be acquired. Database Shutdown Modes ----------------------- These constants are extensions to the DB API definition. They are possible values for the mode parameter of the :meth:`Connection.shutdown()` method. .. data:: DBSHUTDOWN_ABORT This constant is used to specify that the caller should not wait for current processing to complete or for users to disconnect from the database. This should only be used in unusual circumstances since database recovery may be necessary upon next startup. .. data:: DBSHUTDOWN_FINAL This constant is used to specify that the instance can be truly halted. This should only be done after the database has been shutdown with one of the other modes (except abort) and the database has been closed and dismounted using the appropriate SQL commands. .. data:: DBSHUTDOWN_IMMEDIATE This constant is used to specify that all uncommitted transactions should be rolled back and any connected users should be disconnected. .. data:: DBSHUTDOWN_TRANSACTIONAL This constant is used to specify that further connections to the database should be prohibited and no new transactions should be allowed. It then waits for all active transactions to complete. .. data:: DBSHUTDOWN_TRANSACTIONAL_LOCAL This constant is used to specify that further connections to the database should be prohibited and no new transactions should be allowed. It then waits for only local active transactions to complete. Event Types ----------- These constants are extensions to the DB API definition. They are possible values for the :attr:`Message.type` attribute of the messages that are sent for subscriptions created by the :meth:`Connection.subscribe()` method. .. data:: EVENT_AQ This constant is used to specify that one or more messages are available for dequeuing on the queue specified when the subscription was created. .. data:: EVENT_DEREG This constant is used to specify that the subscription has been deregistered and no further notifications will be sent. .. data:: EVENT_NONE This constant is used to specify no information is available about the event. .. data:: EVENT_OBJCHANGE This constant is used to specify that a database change has taken place on a table registered with the :meth:`Subscription.registerquery()` method. .. data:: EVENT_QUERYCHANGE This constant is used to specify that the result set of a query registered with the :meth:`Subscription.registerquery()` method has been changed. .. data:: EVENT_SHUTDOWN This constant is used to specify that the instance is in the process of being shut down. .. data:: EVENT_SHUTDOWN_ANY This constant is used to specify that any instance (when running RAC) is in the process of being shut down. .. data:: EVENT_STARTUP This constant is used to specify that the instance is in the process of being started up. .. _cqn-operation-codes: Operation Codes --------------- These constants are extensions to the DB API definition. They are possible values for the operations parameter for the :meth:`Connection.subscribe()` method. One or more of these values can be OR'ed together. These values are also used by the :attr:`MessageTable.operation` or :attr:`MessageQuery.operation` attributes of the messages that are sent. .. data:: OPCODE_ALLOPS This constant is used to specify that messages should be sent for all operations. .. data:: OPCODE_ALLROWS This constant is used to specify that the table or query has been completely invalidated. .. data:: OPCODE_ALTER This constant is used to specify that messages should be sent when a registered table has been altered in some fashion by DDL, or that the message identifies a table that has been altered. .. data:: OPCODE_DELETE This constant is used to specify that messages should be sent when data is deleted, or that the message identifies a row that has been deleted. .. data:: OPCODE_DROP This constant is used to specify that messages should be sent when a registered table has been dropped, or that the message identifies a table that has been dropped. .. data:: OPCODE_INSERT This constant is used to specify that messages should be sent when data is inserted, or that the message identifies a row that has been inserted. .. data:: OPCODE_UPDATE This constant is used to specify that messages should be sent when data is updated, or that the message identifies a row that has been updated. .. _sesspoolmodes: Session Pool Get Modes ---------------------- These constants are extensions to the DB API definition. They are possible values for the getmode parameter of the :meth:`SessionPool()` method. .. data:: SPOOL_ATTRVAL_FORCEGET This constant is used to specify that a new connection will be returned if there are no free sessions available in the pool. .. data:: SPOOL_ATTRVAL_NOWAIT This constant is used to specify that an exception should be raised if there are no free sessions available in the pool. This is the default value. .. data:: SPOOL_ATTRVAL_WAIT This constant is used to specify that the caller should wait until a session is available if there are no free sessions available in the pool. .. data:: SPOOL_ATTRVAL_TIMEDWAIT This constant is used to specify that the caller should wait for a period of time (defined by the wait_timeout parameter) for a session to become available before returning with an error. Session Pool Purity ------------------- These constants are extensions to the DB API definition. They are possible values for the purity parameter of the :meth:`connect()` method, which is used in database resident connection pooling (DRCP). .. data:: ATTR_PURITY_DEFAULT This constant is used to specify that the purity of the session is the default value identified by Oracle (see Oracle's documentation for more information). This is the default value. .. data:: ATTR_PURITY_NEW This constant is used to specify that the session acquired from the pool should be new and not have any prior session state. .. data:: ATTR_PURITY_SELF This constant is used to specify that the session acquired from the pool need not be new and may have prior session state. Subscription Grouping Classes ----------------------------- These constants are extensions to the DB API definition. They are possible values for the groupingClass parameter of the :meth:`Connection.subscribe()` method. .. data:: SUBSCR_GROUPING_CLASS_TIME This constant is used to specify that events are to be grouped by the period of time in which they are received. Subscription Grouping Types --------------------------- These constants are extensions to the DB API definition. They are possible values for the groupingType parameter of the :meth:`Connection.subscribe()` method. .. data:: SUBSCR_GROUPING_TYPE_SUMMARY This constant is used to specify that when events are grouped a summary of the events should be sent instead of the individual events. This is the default value. .. data:: SUBSCR_GROUPING_TYPE_LAST This constant is used to specify that when events are grouped the last event that makes up the group should be sent instead of the individual events. .. _subscr-namespaces: Subscription Namespaces ----------------------- These constants are extensions to the DB API definition. They are possible values for the namespace parameter of the :meth:`Connection.subscribe()` method. .. data:: SUBSCR_NAMESPACE_AQ This constant is used to specify that notifications should be sent when a queue has messages available to dequeue. .. data:: SUBSCR_NAMESPACE_DBCHANGE This constant is used to specify that database change notification or query change notification messages are to be sent. This is the default value. .. _subscr-protocols: Subscription Protocols ---------------------- These constants are extensions to the DB API definition. They are possible values for the protocol parameter of the :meth:`Connection.subscribe()` method. .. data:: SUBSCR_PROTO_HTTP This constant is used to specify that notifications will be sent to an HTTP URL when a message is generated. This value is currently not supported. .. data:: SUBSCR_PROTO_MAIL This constant is used to specify that notifications will be sent to an e-mail address when a message is generated. This value is currently not supported. .. data:: SUBSCR_PROTO_OCI This constant is used to specify that notifications will be sent to the callback routine identified when the subscription was created. It is the default value and the only value currently supported. .. data:: SUBSCR_PROTO_SERVER This constant is used to specify that notifications will be sent to a PL/SQL procedure when a message is generated. This value is currently not supported. .. _subscr-qos: Subscription Quality of Service ------------------------------- These constants are extensions to the DB API definition. They are possible values for the qos parameter of the :meth:`Connection.subscribe()` method. One or more of these values can be OR'ed together. .. data:: SUBSCR_QOS_BEST_EFFORT This constant is used to specify that best effort filtering for query result set changes is acceptable. False positive notifications may be received. This behaviour may be suitable for caching applications. .. data:: SUBSCR_QOS_DEREG_NFY This constant is used to specify that the subscription should be automatically unregistered after the first notification is received. .. data:: SUBSCR_QOS_QUERY This constant is used to specify that notifications should be sent if the result set of the registered query changes. By default no false positive notifications will be generated. .. data:: SUBSCR_QOS_RELIABLE This constant is used to specify that notifications should not be lost in the event of database failure. .. data:: SUBSCR_QOS_ROWIDS This constant is used to specify that the rowids of the inserted, updated or deleted rows should be included in the message objects that are sent. .. _types: DB API Types ------------ .. data:: BINARY This type object is used to describe columns in a database that contain binary data. The database types :data:`DB_TYPE_RAW` and :data:`DB_TYPE_LONG_RAW` will compare equal to this value. If a variable is created with this type, the database type :data:`DB_TYPE_RAW` will be used. .. data:: DATETIME This type object is used to describe columns in a database that are dates. The database types :data:`DB_TYPE_DATE`, :data:`DB_TYPE_TIMESTAMP`, :data:`DB_TYPE_TIMESTAMP_LTZ` and :data:`DB_TYPE_TIMESTAMP_TZ` will all compare equal to this value. If a variable is created with this type, the database type :data:`DB_TYPE_DATE` will be used. .. data:: NUMBER This type object is used to describe columns in a database that are numbers. The database types :data:`DB_TYPE_BINARY_DOUBLE`, :data:`DB_TYPE_BINARY_FLOAT`, :data:`DB_TYPE_BINARY_INTEGER` and :data:`DB_TYPE_NUMBER` will all compare equal to this value. If a variable is created with this type, the database type :data:`DB_TYPE_NUMBER` will be used. .. data:: ROWID This type object is used to describe the pseudo column "rowid". The database type :data:`DB_TYPE_ROWID` will compare equal to this value. If a variable is created with this type, the database type :data:`DB_TYPE_VARCHAR` will be used. .. data:: STRING This type object is used to describe columns in a database that are strings. The database types :data:`DB_TYPE_CHAR`, :data:`DB_TYPE_LONG`, :data:`DB_TYPE_NCHAR`, :data:`DB_TYPE_NVARCHAR` and :data:`DB_TYPE_VARCHAR` will all compare equal to this value. If a variable is created with this type, the database type :data:`DB_TYPE_VARCHAR` will be used. .. _dbtypes: Database Types -------------- All of these types are extensions to the DB API definition. They are found in query and object metadata. They can also be used to specify the database type when binding data. .. data:: DB_TYPE_BFILE Describes columns, attributes or array elements in a database that are of type BFILE. It will compare equal to the DB API type :data:`BINARY`. .. data:: DB_TYPE_BINARY_DOUBLE Describes columns, attributes or array elements in a database that are of type BINARY_DOUBLE. It will compare equal to the DB API type :data:`NUMBER`. .. data:: DB_TYPE_BINARY_FLOAT Describes columns, attributes or array elements in a database that are of type BINARY_FLOAT. It will compare equal to the DB API type :data:`NUMBER`. .. data:: DB_TYPE_BINARY_INTEGER Describes attributes or array elements in a database that are of type BINARY_INTEGER. It will compare equal to the DB API type :data:`NUMBER`. .. data:: DB_TYPE_BLOB Describes columns, attributes or array elements in a database that are of type BLOB. It will compare equal to the DB API type :data:`BINARY`. .. data:: DB_TYPE_BOOLEAN Describes attributes or array elements in a database that are of type BOOLEAN. It is only available in Oracle 12.1 and higher and only within PL/SQL. .. data:: DB_TYPE_CHAR Describes columns, attributes or array elements in a database that are of type CHAR. It will compare equal to the DB API type :data:`STRING`. Note that these are fixed length string values and behave differently from VARCHAR2. .. data:: DB_TYPE_CLOB Describes columns, attributes or array elements in a database that are of type CLOB. It will compare equal to the DB API type :data:`STRING`. .. data:: DB_TYPE_CURSOR Describes columns in a database that are of type CURSOR. In PL/SQL these are knoown as REF CURSOR. .. data:: DB_TYPE_DATE Describes columns, attributes or array elements in a database that are of type DATE. It will compare equal to the DB API type :data:`DATETIME`. .. data:: DB_TYPE_INTERVAL_DS Describes columns, attributes or array elements in a database that are of type INTERVAL DAY TO SECOND. .. data:: DB_TYPE_INTERVAL_YM Describes columns, attributes or array elements in a database that are of type INTERVAL YEAR TO MONTH. This database type is not currently supported by cx_Oracle. .. data:: DB_TYPE_JSON Describes columns in a database that are of type JSON (with Oracle Database 21 or later). .. versionadded:: 8.1 .. data:: DB_TYPE_LONG Describes columns, attributes or array elements in a database that are of type LONG. It will compare equal to the DB API type :data:`STRING`. .. data:: DB_TYPE_LONG_RAW Describes columns, attributes or array elements in a database that are of type LONG RAW. It will compare equal to the DB API type :data:`BINARY`. .. data:: DB_TYPE_NCHAR Describes columns, attributes or array elements in a database that are of type NCHAR. It will compare equal to the DB API type :data:`STRING`. Note that these are fixed length string values and behave differently from NVARCHAR2. .. data:: DB_TYPE_NCLOB Describes columns, attributes or array elements in a database that are of type NCLOB. It will compare equal to the DB API type :data:`STRING`. .. data:: DB_TYPE_NUMBER Describes columns, attributes or array elements in a database that are of type NUMBER. It will compare equal to the DB API type :data:`NUMBER`. .. data:: DB_TYPE_NVARCHAR Describes columns, attributes or array elements in a database that are of type NVARCHAR2. It will compare equal to the DB API type :data:`STRING`. .. data:: DB_TYPE_OBJECT Describes columns, attributes or array elements in a database that are an instance of a named SQL or PL/SQL type. .. data:: DB_TYPE_RAW Describes columns, attributes or array elements in a database that are of type RAW. It will compare equal to the DB API type :data:`BINARY`. .. data:: DB_TYPE_ROWID Describes columns, attributes or array elements in a database that are of type ROWID or UROWID. It will compare equal to the DB API type :data:`ROWID`. .. data:: DB_TYPE_TIMESTAMP Describes columns, attributes or array elements in a database that are of type TIMESTAMP. It will compare equal to the DB API type :data:`DATETIME`. .. data:: DB_TYPE_TIMESTAMP_LTZ Describes columns, attributes or array elements in a database that are of type TIMESTAMP WITH LOCAL TIME ZONE. It will compare equal to the DB API type :data:`DATETIME`. .. data:: DB_TYPE_TIMESTAMP_TZ Describes columns, attributes or array elements in a database that are of type TIMESTAMP WITH TIME ZONE. It will compare equal to the DB API type :data:`DATETIME`. .. data:: DB_TYPE_VARCHAR Describes columns, attributes or array elements in a database that are of type VARCHAR2. It will compare equal to the DB API type :data:`STRING`. .. _dbtypesynonyms: Database Type Synonyms ---------------------- All of the following constants are deprecated and will be removed in a future version of cx_Oracle. .. data:: BFILE A synonym for :data:`DB_TYPE_BFILE`. .. deprecated:: 8.0 .. data:: BLOB A synonym for :data:`DB_TYPE_BLOB`. .. deprecated:: 8.0 .. data:: BOOLEAN A synonym for :data:`DB_TYPE_BOOLEAN`. .. deprecated:: 8.0 .. data:: CLOB A synonym for :data:`DB_TYPE_CLOB`. .. deprecated:: 8.0 .. data:: CURSOR A synonym for :data:`DB_TYPE_CURSOR`. .. deprecated:: 8.0 .. data:: FIXED_CHAR A synonym for :data:`DB_TYPE_CHAR`. .. deprecated:: 8.0 .. data:: FIXED_NCHAR A synonym for :data:`DB_TYPE_NCHAR`. .. deprecated:: 8.0 .. data:: INTERVAL A synonym for :data:`DB_TYPE_INTERVAL_DS`. .. deprecated:: 8.0 .. data:: LONG_BINARY A synonym for :data:`DB_TYPE_LONG_RAW`. .. deprecated:: 8.0 .. data:: LONG_STRING A synonym for :data:`DB_TYPE_LONG`. .. deprecated:: 8.0 .. data:: NATIVE_FLOAT A synonym for :data:`DB_TYPE_BINARY_DOUBLE`. .. deprecated:: 8.0 .. data:: NATIVE_INT A synonym for :data:`DB_TYPE_BINARY_INTEGER`. .. deprecated:: 8.0 .. data:: NCHAR A synonym for :data:`DB_TYPE_NVARCHAR`. .. deprecated:: 8.0 .. data:: NCLOB A synonym for :data:`DB_TYPE_NCLOB`. .. deprecated:: 8.0 .. data:: OBJECT A synonym for :data:`DB_TYPE_OBJECT`. .. deprecated:: 8.0 .. data:: TIMESTAMP A synonym for :data:`DB_TYPE_TIMESTAMP`. .. deprecated:: 8.0 Other Types ----------- All of these types are extensions to the DB API definition. .. data:: ApiType This type object is the Python type of the database API type constants :data:`BINARY`, :data:`DATETIME`, :data:`NUMBER`, :data:`ROWID` and :data:`STRING`. .. data:: DbType This type object is the Python type of the :ref:`database type constants `. .. data:: LOB This type object is the Python type of :data:`DB_TYPE_BLOB`, :data:`DB_TYPE_BFILE`, :data:`DB_TYPE_CLOB` and :data:`DB_TYPE_NCLOB` data that is returned from cursors. .. _exceptions: Exceptions ========== .. exception:: Warning Exception raised for important warnings and defined by the DB API but not actually used by cx_Oracle. .. exception:: Error Exception that is the base class of all other exceptions defined by cx_Oracle and is a subclass of the Python StandardError exception (defined in the module exceptions). .. exception:: InterfaceError Exception raised for errors that are related to the database interface rather than the database itself. It is a subclass of Error. .. exception:: DatabaseError Exception raised for errors that are related to the database. It is a subclass of Error. .. exception:: DataError Exception raised for errors that are due to problems with the processed data. It is a subclass of DatabaseError. .. exception:: OperationalError Exception raised for errors that are related to the operation of the database but are not necessarily under the control of the programmer. It is a subclass of DatabaseError. .. exception:: IntegrityError Exception raised when the relational integrity of the database is affected. It is a subclass of DatabaseError. .. exception:: InternalError Exception raised when the database encounters an internal error. It is a subclass of DatabaseError. .. exception:: ProgrammingError Exception raised for programming errors. It is a subclass of DatabaseError. .. exception:: NotSupportedError Exception raised when a method or database API was used which is not supported by the database. It is a subclass of DatabaseError. .. _exchandling: Exception handling ================== .. note:: PEP 249 (Python Database API Specification v2.0) says the following about exception values: [...] The values of these exceptions are not defined. They should give the user a fairly good idea of what went wrong, though. [...] With cx_Oracle every exception object has exactly one argument in the ``args`` tuple. This argument is a ``cx_Oracle._Error`` object which has the following five read-only attributes. .. attribute:: _Error.code Integer attribute representing the Oracle error number (ORA-XXXXX). .. attribute:: _Error.offset Integer attribute representing the error offset when applicable. .. attribute:: _Error.message String attribute representing the Oracle message of the error. This message is localized by the environment of the Oracle connection. .. attribute:: _Error.context String attribute representing the context in which the exception was raised. .. attribute:: _Error.isrecoverable Boolean attribute representing whether the error is recoverable or not. This is False in all cases unless both Oracle Database 12.1 (or later) and Oracle Client 12.1 (or later) are being used. .. versionadded:: 5.3 This allows you to use the exceptions for example in the following way: :: import cx_Oracle connection = cx_Oracle.connect("cx_Oracle/dev@localhost/orclpdb1") cursor = connection.cursor() try: cursor.execute("select 1 / 0 from dual") except cx_Oracle.DatabaseError as exc: error, = exc.args print("Oracle-Error-Code:", error.code) print("Oracle-Error-Message:", error.message) python-cx_Oracle-8.3.0/doc/src/api_manual/object_type.rst000066400000000000000000000120431414105416400234430ustar00rootroot00000000000000.. _objecttype: ******************* Object Type Objects ******************* .. note:: This object is an extension to the DB API. It is returned by the :meth:`Connection.gettype()` call and is available as the :data:`Variable.type` for variables containing Oracle objects. .. method:: ObjectType([sequence]) The object type may be called directly and serves as an alternative way of calling :meth:`~ObjectType.newobject()`. .. attribute:: ObjectType.attributes This read-only attribute returns a list of the :ref:`attributes ` that make up the object type. .. attribute:: ObjectType.iscollection This read-only attribute returns a boolean indicating if the object type refers to a collection or not. .. attribute:: ObjectType.name This read-only attribute returns the name of the type. .. attribute:: ObjectType.element_type This read-only attribute returns the type of elements found in collections of this type, if :attr:`~ObjectType.iscollection` is ``True``; otherwise, it returns ``None``. If the collection contains objects, this will be another object type; otherwise, it will be one of the :ref:`database type constants `. .. versionadded:: 8.0 .. method:: ObjectType.newobject([sequence]) Return a new Oracle object of the given type. This object can then be modified by setting its attributes and then bound to a cursor for interaction with Oracle. If the object type refers to a collection, a sequence may be passed and the collection will be initialized with the items in that sequence. .. attribute:: ObjectType.schema This read-only attribute returns the name of the schema that owns the type. Object Objects -------------- .. note:: This object is an extension to the DB API. It is returned by the :meth:`ObjectType.newobject()` call and can be bound to variables of type :data:`~cx_Oracle.OBJECT`. Attributes can be retrieved and set directly. .. method:: Object.append(element) Append an element to the collection object. If no elements exist in the collection, this creates an element at index 0; otherwise, it creates an element immediately following the highest index available in the collection. .. method:: Object.asdict() Return a dictionary where the collection's indexes are the keys and the elements are its values. .. versionadded:: 7.0 .. method:: Object.aslist() Return a list of each of the collection's elements in index order. .. method:: Object.copy() Create a copy of the object and return it. .. method:: Object.delete(index) Delete the element at the specified index of the collection. If the element does not exist or is otherwise invalid, an error is raised. Note that the indices of the remaining elements in the collection are not changed. In other words, the delete operation creates holes in the collection. .. method:: Object.exists(index) Return True or False indicating if an element exists in the collection at the specified index. .. method:: Object.extend(sequence) Append all of the elements in the sequence to the collection. This is the equivalent of performing :meth:`~Object.append()` for each element found in the sequence. .. method:: Object.first() Return the index of the first element in the collection. If the collection is empty, None is returned. .. method:: Object.getelement(index) Return the element at the specified index of the collection. If no element exists at that index, an exception is raised. .. method:: Object.last() Return the index of the last element in the collection. If the collection is empty, None is returned. .. method:: Object.next(index) Return the index of the next element in the collection following the specified index. If there are no elements in the collection following the specified index, None is returned. .. method:: Object.prev(index) Return the index of the element in the collection preceding the specified index. If there are no elements in the collection preceding the specified index, None is returned. .. method:: Object.setelement(index, value) Set the value in the collection at the specified index to the given value. .. method:: Object.size() Return the number of elements in the collection. .. method:: Object.trim(num) Remove the specified number of elements from the end of the collection. .. _objectattr: Object Attribute Objects ------------------------ .. note:: This object is an extension to the DB API. The elements of :attr:`ObjectType.attributes` are instances of this type. .. attribute:: ObjectAttribute.name This read-only attribute returns the name of the attribute. .. attribute:: ObjectAttribute.type This read-only attribute returns the type of the attribute. This will be an :ref:`Oracle Object Type ` if the variable binds Oracle objects; otherwise, it will be one of the :ref:`database type constants `. .. versionadded:: 8.0 python-cx_Oracle-8.3.0/doc/src/api_manual/session_pool.rst000066400000000000000000000275711414105416400236640ustar00rootroot00000000000000.. _sesspool: ****************** SessionPool Object ****************** .. note:: This object is an extension to the DB API. Connection pooling in cx_Oracle is handled by SessionPool objects. See :ref:`connpool` for information on connection pooling. .. method:: SessionPool.acquire(user=None, password=None, cclass=None, \ purity=cx_Oracle.ATTR_PURITY_DEFAULT, tag=None, matchanytag=False, \ shardingkey=[], supershardingkey=[]) Acquire a connection from the session pool and return a :ref:`connection object `. If the pool is homogeneous, the user and password parameters cannot be specified. If they are, an exception will be raised. The cclass parameter, if specified, should be a string corresponding to the connection class for database resident connection pooling (DRCP). The purity parameter is expected to be one of :data:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or :data:`~cx_Oracle.ATTR_PURITY_DEFAULT`. The tag parameter, if specified, is expected to be a string with name=value pairs like "k1=v1;k2=v2" and will limit the sessions that can be returned from a session pool unless the matchanytag parameter is set to True. In that case sessions with the specified tag will be preferred over others, but if no such sessions are available a session with a different tag may be returned instead. In any case, untagged sessions will always be returned if no sessions with the specified tag are available. Sessions are tagged when they are :meth:`released ` back to the pool. The shardingkey and supershardingkey parameters, if specified, are expected to be a sequence of values which will be used to identify the database shard to connect to. The key values can be strings, numbers, bytes or dates. .. attribute:: SessionPool.busy This read-only attribute returns the number of sessions currently acquired. .. method:: SessionPool.close(force=False) Close the session pool now, rather than when the last reference to it is released, which makes it unusable for further work. If any connections have been acquired and not released back to the pool this method will fail unless the force parameter is set to True. .. method:: SessionPool.drop(connection) Drop the connection from the pool which is useful if the connection is no longer usable (such as when the session is killed). .. attribute:: SessionPool.dsn This read-only attribute returns the TNS entry of the database to which a connection has been established. .. attribute:: SessionPool.getmode This read-write attribute determines how connections are returned from the pool. If :data:`~cx_Oracle.SPOOL_ATTRVAL_FORCEGET` is specified, a new connection will be returned even if there are no free sessions in the pool. :data:`~cx_Oracle.SPOOL_ATTRVAL_NOWAIT` will raise an exception if there are no free sessions are available in the pool. If :data:`~cx_Oracle.SPOOL_ATTRVAL_WAIT` is specified and there are no free sessions in the pool, the caller will wait until a free session is available. :data:`~cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT` uses the value of :data:`~SessionPool.wait_timeout` to determine how long the caller should wait for a session to become available before returning an error. .. attribute:: SessionPool.homogeneous This read-write boolean attribute indicates whether the pool is considered homogeneous or not. If the pool is not homogeneous different authentication can be used for each connection acquired from the pool. .. attribute:: SessionPool.increment This read-only attribute returns the number of sessions that will be established when additional sessions need to be created. .. attribute:: SessionPool.max This read-only attribute returns the maximum number of sessions that the session pool can control. .. attribute:: SessionPool.max_lifetime_session This read-write attribute returns the maximum length of time (in seconds) that a pooled session may exist. Sessions that are in use will not be closed. They become candidates for termination only when they are released back to the pool and have existed for longer than max_lifetime_session seconds. Note that termination only occurs when the pool is accessed. A value of 0 means that there is no maximum length of time that a pooled session may exist. This attribute is only available in Oracle Database 12.1. .. versionadded:: 5.3 .. attribute:: SessionPool.max_sessions_per_shard This read-write attribute returns the number of sessions that can be created per shard in the pool. Setting this attribute greater than zero specifies the maximum number of sessions in the pool that can be used for any given shard in a sharded database. This lets connections in the pool be balanced across the shards. A value of zero will not set any maximum number of sessions for each shard. This attribute is only available in Oracle Client 18.3 and higher. .. versionadded:: 8.2 .. attribute:: SessionPool.min This read-only attribute returns the number of sessions with which the session pool was created and the minimum number of sessions that will be controlled by the session pool. .. attribute:: SessionPool.name This read-only attribute returns the name assigned to the session pool by Oracle. .. attribute:: SessionPool.opened This read-only attribute returns the number of sessions currently opened by the session pool. .. attribute:: SessionPool.ping_interval This read-write integer attribute specifies the pool ping interval in seconds. When a connection is acquired from the pool, a check is first made to see how long it has been since the connection was put into the pool. If this idle time exceeds ``ping_interval``, then a :ref:`round-trip ` ping to the database is performed. If the connection is unusable, it is discarded and a different connection is selected to be returned by :meth:`SessionPool.acquire()`. Setting ``ping_interval`` to a negative value disables pinging. Setting it to 0 forces a ping for every ``aquire()`` and is not recommended. Prior to cx_Oracle 8.2, the ping interval was fixed at 60 seconds. .. versionadded:: 8.2 .. method:: SessionPool.reconfigure([min, max, increment, getmode, timeout, \ wait_timeout, max_lifetime_session, max_sessions_per_shard, \ soda_metadata_cache, stmtcachesize, ping_interval]) Reconfigures various parameters of a connection pool. The pool size can be altered with ``reconfigure()`` by passing values for :data:`~SessionPool.min`, :data:`~SessionPool.max` or :data:`~SessionPool.increment`. The :data:`~SessionPool.getmode`, :data:`~SessionPool.timeout`, :data:`~SessionPool.wait_timeout`, :data:`~SessionPool.max_lifetime_session`, :data:`~SessionPool.max_sessions_per_shard`, :data:`~SessionPool.soda_metadata_cache`, :data:`~SessionPool.stmtcachesize` and :data:`~SessionPool.ping_interval` attributes can be set directly or with ``reconfigure()``. All parameters are optional. Unspecified parameters will leave those pool attributes unchanged. The parameters are processed in two stages. After any size change has been processed, reconfiguration on the other parameters is done sequentially. If an error such as an invalid value occurs when changing one attribute, then an exception will be generated but any already changed attributes will retain their new values. During reconfiguration of a pool's size, the behavior of :meth:`SessionPool.acquire()` depends on the ``getmode`` in effect when ``acquire()`` is called: * With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_FORCEGET`, an ``acquire()`` call will wait until the pool has been reconfigured. * With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`, an ``acquire()`` call will try to acquire a connection in the time specified by :data:`~SessionPool.wait_timeout` and return an error if the time taken exceeds that value. * With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_WAIT`, an ``acquire()`` call will wait until after the pool has been reconfigured and a connection is available. * With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_NOWAIT`, if the number of busy connections is less than the pool size, ``acquire()`` will return a new connection after pool reconfiguration is complete. Closing connections with :meth:`SessionPool.release()` or :meth:`Connection.close()` will wait until any pool size reconfiguration is complete. Closing the connection pool with :meth:`SessionPool.close()` will wait until reconfiguration is complete. See :ref:`Connection Pool Reconfiguration `. .. versionadded:: 8.2 .. method:: SessionPool.release(connection, tag=None) Release the connection back to the pool now, rather than whenever __del__ is called. The connection will be unusable from this point forward; an Error exception will be raised if any operation is attempted with the connection. Any cursors or LOBs created by the connection will also be marked unusable and an Error exception will be raised if any operation is attempted with them. Internally, references to the connection are held by cursor objects, LOB objects, etc. Once all of these references are released, the connection itself will be released back to the pool automatically. Either control references to these related objects carefully or explicitly release connections back to the pool in order to ensure sufficient resources are available. If the tag is not None, it is expected to be a string with name=value pairs like "k1=v1;k2=v2" and will override the value in the property :attr:`Connection.tag`. If either :attr:`Connection.tag` or the tag parameter are not None, the connection will be retagged when it is released back to the pool. .. attribute:: SessionPool.soda_metadata_cache This read-write boolean attribute returns whether the SODA metadata cache is enabled or not. Enabling the cache significantly improves the performance of methods :meth:`SodaDatabase.createCollection()` (when not specifying a value for the metadata parameter) and :meth:`SodaDatabase.openCollection()`. Note that the cache can become out of date if changes to the metadata of cached collections are made externally. .. versionadded:: 8.2 .. attribute:: SessionPool.stmtcachesize This read-write attribute specifies the size of the statement cache that will be used for connections obtained from the pool. See :ref:`Statement Caching ` for more information. .. versionadded:: 6.0 .. attribute:: SessionPool.timeout This read-write attribute specifies the time (in seconds) after which idle sessions will be terminated in order to maintain an optimum number of open sessions. Note that termination only occurs when the pool is accessed. A value of 0 means that no idle sessions are terminated. .. attribute:: SessionPool.tnsentry This read-only attribute returns the TNS entry of the database to which a connection has been established. .. deprecated:: 8.2 Use the attribute :attr:`~SessionPool.dsn` instead. .. attribute:: SessionPool.username This read-only attribute returns the name of the user which established the connection to the database. .. attribute:: SessionPool.wait_timeout This read-write attribute specifies the time (in milliseconds) that the caller should wait for a session to become available in the pool before returning with an error. This value is only used if the getmode parameter to :meth:`cx_Oracle.SessionPool()` was the value :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`. .. versionadded:: 6.4 python-cx_Oracle-8.3.0/doc/src/api_manual/soda.rst000066400000000000000000000575331414105416400220770ustar00rootroot00000000000000.. _soda: **** SODA **** `Oracle Database Simple Oracle Document Access (SODA) `__ allows documents to be inserted, queried, and retrieved from Oracle Database using a set of NoSQL-style cx_Oracle methods. By default, documents are JSON strings. See the :ref:`user manual ` for examples. .. _sodarequirements: ----------------- SODA Requirements ----------------- To use SODA, the role SODA_APP must be granted to the user. To create collections, users need the CREATE TABLE privilege. These can be granted by a DBA: .. code-block:: sql SQL> grant soda_app, create table to myuser; Advanced users who are using Oracle sequences for keys will also need the CREATE SEQUENCE privilege. SODA requires Oracle Client 18.3 or higher and Oracle Database 18.1 and higher. .. note:: If you are using Oracle Database 21c (or later) and create new collections you need to do one of the following: - Use Oracle Client libraries 21c (or later). - Or, explicitly use collection metadata when creating collections and set the data storage type to BLOB, for example:: { "keyColumn": { "name":"ID" }, "contentColumn": { "name": "JSON_DOCUMENT", "sqlType": "BLOB" }, "versionColumn": { "name": "VERSION", "method": "UUID" }, "lastModifiedColumn": { "name": "LAST_MODIFIED" }, "creationTimeColumn": { "name": "CREATED_ON" } } - Or, set the database initialization parameter `compatible `__ to 19 or lower. Otherwise you may get errors such as "ORA-40842: unsupported value JSON in the metadata for the field sqlType" or "ORA-40659: Data type does not match the specification in the collection metadata". .. _sodadb: -------------------- SODA Database Object -------------------- .. note:: This object is an extension the DB API. It is returned by the method :meth:`Connection.getSodaDatabase()`. .. method:: SodaDatabase.createCollection(name, metadata=None, mapMode=False) Creates a SODA collection with the given name and returns a new :ref:`SODA collection object `. If you try to create a collection, and a collection with the same name and metadata already exists, then that existing collection is opened without error. If metadata is specified, it is expected to be a string containing valid JSON or a dictionary that will be transformed into a JSON string. This JSON permits you to specify the configuration of the collection including storage options; specifying the presence or absence of columns for creation timestamp, last modified timestamp and version; whether the collection can store only JSON documents; and methods of key and version generation. The default metadata creates a collection that only supports JSON documents and uses system generated keys. See this `collection metadata reference `__ for more information. If the mapMode parameter is set to True, the new collection is mapped to an existing table instead of creating a table. If a collection is created in this way, dropping the collection will not drop the existing table either. .. versionadded:: 7.0 .. method:: SodaDatabase.createDocument(content, key=None, mediaType="application/json") Creates a :ref:`SODA document ` usable for SODA write operations. You only need to use this method if your collection requires client-assigned keys or has non-JSON content; otherwise, you can pass your content directly to SODA write operations. SodaDocument attributes 'createdOn', 'lastModified' and 'version' will be None. The content parameter can be a dictionary or list which will be transformed into a JSON string and then UTF-8 encoded. It can also be a string which will be UTF-8 encoded or it can be a bytes object which will be stored unchanged. If a bytes object is provided and the content is expected to be JSON, note that SODA only supports UTF-8, UTF-16LE and UTF-16BE encodings. The key parameter should only be supplied if the collection in which the document is to be placed requires client-assigned keys. The mediaType parameter should only be supplied if the collection in which the document is to be placed supports non-JSON documents and the content for this document is non-JSON. Using a standard MIME type for this value is recommended but any string will be accepted. .. versionadded:: 7.0 .. method:: SodaDatabase.getCollectionNames(startName=None, limit=0) Returns a list of the names of collections in the database that match the criteria, in alphabetical order. If the startName parameter is specified, the list of names returned will start with this value and also contain any names that fall after this value in alphabetical order. If the limit parameter is specified and is non-zero, the number of collection names returned will be limited to this value. .. versionadded:: 7.0 .. method:: SodaDatabase.openCollection(name) Opens an existing collection with the given name and returns a new :ref:`SODA collection object `. If a collection with that name does not exist, None is returned. .. versionadded:: 7.0 .. _sodacoll: ---------------------- SODA Collection Object ---------------------- .. note:: This object is an extension the DB API. It is used to represent SODA collections and is created by methods :meth:`SodaDatabase.createCollection()` and :meth:`SodaDatabase.openCollection()`. .. method:: SodaCollection.createIndex(spec) Creates an index on a SODA collection. The spec is expected to be a dictionary or a JSON-encoded string. See this `overview `__ for information on indexes in SODA. Note that a commit should be performed before attempting to create an index. .. versionadded:: 7.0 .. method:: SodaCollection.drop() Drops the collection from the database, if it exists. Note that if the collection was created with mapMode set to True the underlying table will not be dropped. A boolean value is returned indicating if the collection was actually dropped. .. versionadded:: 7.0 .. method:: SodaCollection.dropIndex(name, force=False) Drops the index with the specified name, if it exists. The force parameter, if set to True, can be used to force the dropping of an index that the underlying Oracle Database domain index doesn't normally permit. This is only applicable to spatial and JSON search indexes. See `here `__ for more information. A boolean value is returned indicating if the index was actually dropped. .. versionadded:: 7.0 .. method:: SodaCollection.find() This method is used to begin an operation that will act upon documents in the collection. It creates and returns a :ref:`SodaOperation object ` which is used to specify the criteria and the operation that will be performed on the documents that match that criteria. .. versionadded:: 7.0 .. method:: SodaCollection.getDataGuide() Returns a :ref:`SODA document object ` containing property names, data types and lengths inferred from the JSON documents in the collection. It can be useful for exploring the schema of a collection. Note that this method is only supported for JSON-only collections where a JSON search index has been created with the 'dataguide' option enabled. If there are no documents in the collection, None is returned. .. versionadded:: 7.0 .. method:: SodaCollection.insertMany(docs) Inserts a list of documents into the collection at one time. Each of the input documents can be a dictionary or list or an existing :ref:`SODA document object `. .. note:: This method requires Oracle Client 18.5 and higher and is available only as a preview. .. versionadded:: 7.2 .. method:: SodaCollection.insertManyAndGet(docs, hint=None) Similarly to :meth:`~SodaCollection.insertMany()` this method inserts a list of documents into the collection at one time. The only difference is that it returns a list of :ref:`SODA Document objects `. Note that for performance reasons the returned documents do not contain the content. The hint parameter, if specified, supplies a hint to the database when processing the SODA operation. This is expected to be a string in the same format as a SQL hint but without any comment characters, for example ``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring) or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL Tuning Guide documentation `MONITOR and NO_MONITOR Hints `__ and `Monitoring Database Operations `__ for more information. .. note:: This method requires Oracle Client 18.5 and higher. .. versionadded:: 7.2 .. versionchanged:: 8.2 The parameter `hint` was added. Use of the hint parameter requires Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11). .. method:: SodaCollection.insertOne(doc) Inserts a given document into the collection. The input document can be a dictionary or list or an existing :ref:`SODA document object `. .. versionadded:: 7.0 .. method:: SodaCollection.insertOneAndGet(doc, hint=None) Similarly to :meth:`~SodaCollection.insertOne()` this method inserts a given document into the collection. The only difference is that it returns a :ref:`SODA Document object `. Note that for performance reasons the returned document does not contain the content. The hint parameter, if specified, supplies a hint to the database when processing the SODA operation. This is expected to be a string in the same format as a SQL hint but without any comment characters, for example ``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring) or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL Tuning Guide documentation `MONITOR and NO_MONITOR Hints `__ and `Monitoring Database Operations `__ for more information. .. versionadded:: 7.0 .. versionchanged:: 8.2 The parameter `hint` was added. Use of the hint parameter requires Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11). .. attribute:: SodaCollection.metadata This read-only attribute returns a dictionary containing the metadata that was used to create the collection. See this `collection metadata reference `__ for more information. .. versionadded:: 7.0 .. attribute:: SodaCollection.name This read-only attribute returns the name of the collection. .. versionadded:: 7.0 .. method:: SodaCollection.save(doc) Saves a document into the collection. This method is equivalent to :meth:`~SodaCollection.insertOne()` except that if client-assigned keys are used, and the document with the specified key already exists in the collection, it will be replaced with the input document. This method requires Oracle Client 19.9 or higher in addition to the usual SODA requirements. .. versionadded:: 8.0 .. method:: SodaCollection.saveAndGet(doc, hint=None) Saves a document into the collection. This method is equivalent to :meth:`~SodaCollection.insertOneAndGet()` except that if client-assigned keys are used, and the document with the specified key already exists in the collection, it will be replaced with the input document. The hint parameter, if specified, supplies a hint to the database when processing the SODA operation. This is expected to be a string in the same format as a SQL hint but without any comment characters, for example ``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring) or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL Tuning Guide documentation `MONITOR and NO_MONITOR Hints `__ and `Monitoring Database Operations `__ for more information. This method requires Oracle Client 19.9 or higher in addition to the usual SODA requirements. .. versionadded:: 8.0 .. versionchanged:: 8.2 The parameter `hint` was added. Use of the hint parameter requires Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11). .. method:: SodaCollection.truncate() Removes all of the documents in the collection, similarly to what is done for rows in a table by the TRUNCATE TABLE statement. .. versionadded:: 8.0 .. _sodadoc: -------------------- SODA Document Object -------------------- .. note:: This object is an extension the DB API. It is returned by the methods :meth:`SodaDatabase.createDocument()`, :meth:`SodaOperation.getDocuments()` and :meth:`SodaOperation.getOne()` as well as by iterating over :ref:`SODA document cursors `. .. attribute:: SodaDoc.createdOn This read-only attribute returns the creation time of the document in `ISO 8601 `__ format. Documents created by :meth:`SodaDatabase.createDocument()` or fetched from collections where this attribute is not stored will return None. .. versionadded:: 7.0 .. method:: SodaDoc.getContent() Returns the content of the document as a dictionary or list. This method assumes that the content is application/json and will raise an exception if this is not the case. If there is no content, however, None will be returned. .. versionadded:: 7.0 .. method:: SodaDoc.getContentAsBytes() Returns the content of the document as a bytes object. If there is no content, however, None will be returned. .. versionadded:: 7.0 .. method:: SodaDoc.getContentAsString() Returns the content of the document as a string. If the document encoding is not known, UTF-8 will be used. If there is no content, however, None will be returned. .. versionadded:: 7.0 .. attribute:: SodaDoc.key This read-only attribute returns the unique key assigned to this document. Documents created by :meth:`SodaDatabase.createDocument()` may not have a value assigned to them and return None. .. versionadded:: 7.0 .. attribute:: SodaDoc.lastModified This read-only attribute returns the last modified time of the document in `ISO 8601 `__ format. Documents created by :meth:`SodaDatabase.createDocument()` or fetched from collections where this attribute is not stored will return None. .. versionadded:: 7.0 .. attribute:: SodaDoc.mediaType This read-only attribute returns the media type assigned to the document. By convention this is expected to be a MIME type but no checks are performed on this value. If a value is not specified when calling :meth:`SodaDatabase.createDocument()` or the document is fetched from a collection where this component is not stored, the string "application/json" is returned. .. versionadded:: 7.0 .. attribute:: SodaDoc.version This read-only attribute returns the version assigned to this document. Documents created by :meth:`SodaDatabase.createDocument()` or fetched from collections where this attribute is not stored will return None. .. versionadded:: 7.0 .. _sodadoccur: --------------------------- SODA Document Cursor Object --------------------------- .. note:: This object is an extension the DB API. It is returned by the method :meth:`SodaOperation.getCursor()` and implements the iterator protocol. Each iteration will return a :ref:`SODA document object `. .. method:: SodaDocCursor.close() Close the cursor now, rather than whenever __del__ is called. The cursor will be unusable from this point forward; an Error exception will be raised if any operation is attempted with the cursor. .. versionadded:: 7.0 .. _sodaop: --------------------- SODA Operation Object --------------------- .. note:: This object is an extension to the DB API. It represents an operation that will be performed on all or some of the documents in a SODA collection. It is created by the method :meth:`SodaCollection.find()`. .. method:: SodaOperation.count() Returns a count of the number of documents in the collection that match the criteria. If :meth:`~SodaOperation.skip()` or :meth:`~SodaOperation.limit()` were called on this object, an exception is raised. .. versionadded:: 7.0 .. method:: SodaOperation.fetchArraySize(value) This is a tuning method to specify the number of documents that are internally fetched in batches by calls to :meth:`~SodaOperation.getCursor()` and :meth:`~SodaOperation.getDocuments()`. It does not affect how many documents are returned to the application. A value of 0 will use the default value (100). This method is only available in Oracle Client 19.5 and higher. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. .. versionadded:: 8.0 .. method:: SodaOperation.filter(value) Sets a filter specification for complex document queries and ordering of JSON documents. Filter specifications must be provided as a dictionary or JSON-encoded string and can include comparisons, regular expressions, logical and spatial operators, among others. See the `overview of SODA filter specifications `__ for more information. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. .. versionadded:: 7.0 .. method:: SodaOperation.getCursor() Returns a :ref:`SODA Document Cursor object ` that can be used to iterate over the documents that match the criteria. .. versionadded:: 7.0 .. method:: SodaOperation.getDocuments() Returns a list of :ref:`SODA Document objects ` that match the criteria. .. versionadded:: 7.0 .. method:: SodaOperation.getOne() Returns a single :ref:`SODA Document object ` that matches the criteria. Note that if multiple documents match the criteria only the first one is returned. .. versionadded:: 7.0 .. method:: SodaOperation.hint(value) Specifies a hint that will be provided to the SODA operation when it is performed. This is expected to be a string in the same format as a SQL hint but without any comment characters, for example ``hint("MONITOR")``. Pass only the hint ``"MONITOR"`` (turn on monitoring) or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL Tuning Guide documentation `MONITOR and NO_MONITOR Hints `__ and `Monitoring Database Operations `__ for more information. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. Use of this method requires Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11). .. versionadded:: 8.2 .. method:: SodaOperation.key(value) Specifies that the document with the specified key should be returned. This causes any previous calls made to this method and :meth:`~SodaOperation.keys()` to be ignored. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. .. versionadded:: 7.0 .. method:: SodaOperation.keys(seq) Specifies that documents that match the keys found in the supplied sequence should be returned. This causes any previous calls made to this method and :meth:`~SodaOperation.key()` to be ignored. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. .. versionadded:: 7.0 .. method:: SodaOperation.limit(value) Specifies that only the specified number of documents should be returned. This method is only usable for read operations such as :meth:`~SodaOperation.getCursor()` and :meth:`~SodaOperation.getDocuments()`. For write operations, any value set using this method is ignored. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. .. versionadded:: 7.0 .. method:: SodaOperation.remove() Removes all of the documents in the collection that match the criteria. The number of documents that have been removed is returned. .. versionadded:: 7.0 .. method:: SodaOperation.replaceOne(doc) Replaces a single document in the collection with the specified document. The input document can be a dictionary or list or an existing :ref:`SODA document object `. A boolean indicating if a document was replaced or not is returned. Currently the method :meth:`~SodaOperation.key()` must be called before this method can be called. .. versionadded:: 7.0 .. method:: SodaOperation.replaceOneAndGet(doc) Similarly to :meth:`~SodaOperation.replaceOne()`, this method replaces a single document in the collection with the specified document. The only difference is that it returns a :ref:`SODA document object `. Note that for performance reasons the returned document does not contain the content. .. versionadded:: 7.0 .. method:: SodaOperation.skip(value) Specifies the number of documents that match the other criteria that will be skipped. This method is only usable for read operations such as :meth:`~SodaOperation.getCursor()` and :meth:`~SodaOperation.getDocuments()`. For write operations, any value set using this method is ignored. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. .. versionadded:: 7.0 .. method:: SodaOperation.version(value) Specifies that documents with the specified version should be returned. Typically this is used with :meth:`~SodaOperation.key()` to implement optimistic locking, so that the write operation called later does not affect a document that someone else has modified. As a convenience, the SodaOperation object is returned so that further criteria can be specified by chaining methods together. .. versionadded:: 7.0 python-cx_Oracle-8.3.0/doc/src/api_manual/subscription.rst000066400000000000000000000200061414105416400236560ustar00rootroot00000000000000.. _subscrobj: ******************* Subscription Object ******************* .. note:: This object is an extension the DB API. .. attribute:: Subscription.callback This read-only attribute returns the callback that was registered when the subscription was created. .. attribute:: Subscription.connection This read-only attribute returns the connection that was used to register the subscription when it was created. .. attribute:: Subscription.id This read-only attribute returns the value of ``REGID`` found in the database view ``USER_CHANGE_NOTIFICATION_REGS`` or the value of ``REG_ID`` found in the database view ``USER_SUBSCR_REGISTRATIONS``. For AQ subscriptions, the value is 0. .. attribute:: Subscription.ip_address This read-only attribute returns the IP address used for callback notifications from the database server. If not set during construction, this value is None. .. versionadded:: 6.4 .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the attribute `ipAddress` was renamed to `ip_address`. The old name will continue to work for a period of time. .. attribute:: Subscription.name This read-only attribute returns the name used to register the subscription when it was created. .. versionadded:: 6.4 .. attribute:: Subscription.namespace This read-only attribute returns the namespace used to register the subscription when it was created. .. attribute:: Subscription.operations This read-only attribute returns the operations that will send notifications for each table or query that is registered using this subscription. .. attribute:: Subscription.port This read-only attribute returns the port used for callback notifications from the database server. If not set during construction, this value is zero. .. attribute:: Subscription.protocol This read-only attribute returns the protocol used to register the subscription when it was created. .. attribute:: Subscription.qos This read-only attribute returns the quality of service flags used to register the subscription when it was created. .. method:: Subscription.registerquery(statement, [args]) Register the query for subsequent notification when tables referenced by the query are changed. This behaves similarly to cursor.execute() but only queries are permitted and the args parameter must be a sequence or dictionary. If the qos parameter included the flag cx_Oracle.SUBSCR_QOS_QUERY when the subscription was created, then the ID for the registered query is returned; otherwise, None is returned. .. attribute:: Subscription.timeout This read-only attribute returns the timeout (in seconds) that was specified when the subscription was created. A value of 0 indicates that there is no timeout. .. _msgobjects: Message Objects --------------- .. note:: This object is created internally when notification is received and passed to the callback procedure specified when a subscription is created. .. attribute:: Message.consumer_name This read-only attribute returns the name of the consumer which generated the notification. It will be populated if the subscription was created with the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` and the queue is a multiple consumer queue. .. versionadded:: 6.4 .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the attribute `consumerName` was renamed to `consumer_name`. The old name will continue to work for a period of time. .. attribute:: Message.dbname This read-only attribute returns the name of the database that generated the notification. .. attribute:: Message.queries This read-only attribute returns a list of message query objects that give information about query result sets changed for this notification. This attribute will be None if the qos parameter did not include the flag :data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created. .. attribute:: Message.queue_name This read-only attribute returns the name of the queue which generated the notification. It will only be populated if the subscription was created with the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`. .. versionadded:: 6.4 .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the attribute `queueName` was renamed to `queue_name`. The old name will continue to work for a period of time. .. attribute:: Message.registered This read-only attribute returns whether the subscription which generated this notification is still registered with the database. The subscription is automatically deregistered with the database when the subscription timeout value is reached or when the first notification is sent (when the quality of service flag :data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY` is used). .. versionadded:: 6.4 .. attribute:: Message.subscription This read-only attribute returns the subscription object for which this notification was generated. .. attribute:: Message.tables This read-only attribute returns a list of message table objects that give information about the tables changed for this notification. This attribute will be None if the qos parameter included the flag :data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created. .. attribute:: Message.txid This read-only attribute returns the id of the transaction that generated the notification. .. attribute:: Message.type This read-only attribute returns the type of message that has been sent. See the constants section on event types for additional information. Message Table Objects --------------------- .. note:: This object is created internally for each table changed when notification is received and is found in the tables attribute of message objects, and the tables attribute of message query objects. .. attribute:: MessageTable.name This read-only attribute returns the name of the table that was changed. .. attribute:: MessageTable.operation This read-only attribute returns the operation that took place on the table that was changed. .. attribute:: MessageTable.rows This read-only attribute returns a list of message row objects that give information about the rows changed on the table. This value is only filled in if the qos parameter to the :meth:`Connection.subscribe()` method included the flag :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS`. Message Row Objects ------------------- .. note:: This object is created internally for each row changed on a table when notification is received and is found in the rows attribute of message table objects. .. attribute:: MessageRow.operation This read-only attribute returns the operation that took place on the row that was changed. .. attribute:: MessageRow.rowid This read-only attribute returns the rowid of the row that was changed. Message Query Objects --------------------- .. note:: This object is created internally for each query result set changed when notification is received and is found in the queries attribute of message objects. .. attribute:: MessageQuery.id This read-only attribute returns the query id of the query for which the result set changed. The value will match the value returned by Subscription.registerquery when the related query was registered. .. attribute:: MessageQuery.operation This read-only attribute returns the operation that took place on the query result set that was changed. Valid values for this attribute are :data:`~cx_Oracle.EVENT_DEREG` and :data:`~cx_Oracle.EVENT_QUERYCHANGE`. .. attribute:: MessageQuery.tables This read-only attribute returns a list of message table objects that give information about the table changes that caused the query result set to change for this notification. python-cx_Oracle-8.3.0/doc/src/api_manual/variable.rst000066400000000000000000000073561414105416400227340ustar00rootroot00000000000000.. _varobj: **************** Variable Objects **************** .. note:: The DB API definition does not define this object. .. attribute:: Variable.actual_elements This read-only attribute returns the actual number of elements in the variable. This corresponds to the number of elements in a PL/SQL index-by table for variables that are created using the method :func:`Cursor.arrayvar()`. For all other variables this value will be identical to the attribute :attr:`~Variable.numElements`. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the attribute `actualElements` was renamed to `actual_elements`. The old name will continue to work for a period of time. .. attribute:: Variable.buffer_size This read-only attribute returns the size of the buffer allocated for each element in bytes. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the attribute `bufferSize` was renamed to `buffer_size`. The old name will continue to work for a period of time. .. method:: Variable.getvalue([pos=0]) Return the value at the given position in the variable. For variables created using the method :func:`Cursor.arrayvar()` the value returned will be a list of each of the values in the PL/SQL index-by table. For variables bound to DML returning statements, the value returned will also be a list corresponding to the returned data for the given execution of the statement (as identified by the pos parameter). .. attribute:: Variable.inconverter This read-write attribute specifies the method used to convert data from Python to the Oracle database. The method signature is converter(value) and the expected return value is the value to bind to the database. If this attribute is None, the value is bound directly without any conversion. .. attribute:: Variable.num_elements This read-only attribute returns the number of elements allocated in an array, or the number of scalar items that can be fetched into the variable or bound to the variable. .. versionchanged:: 8.2 For consistency and compliance with the PEP 8 naming style, the attribute `numElements` was renamed to `num_elements`. The old name will continue to work for a period of time. .. attribute:: Variable.outconverter This read-write attribute specifies the method used to convert data from the Oracle database to Python. The method signature is converter(value) and the expected return value is the value to return to Python. If this attribute is None, the value is returned directly without any conversion. .. method:: Variable.setvalue(pos, value) Set the value at the given position in the variable. .. attribute:: Variable.size This read-only attribute returns the size of the variable. For strings this value is the size in characters. For all others, this is same value as the attribute bufferSize. .. attribute:: Variable.type This read-only attribute returns the type of the variable. This will be an :ref:`Oracle Object Type ` if the variable binds Oracle objects; otherwise, it will be one of the :ref:`database type constants `. .. versionchanged:: 8.0 Database type constants are now used when the variable is not used for binding Oracle objects. .. attribute:: Variable.values This read-only attribute returns a copy of the value of all actual positions in the variable as a list. This is the equivalent of calling :meth:`~Variable.getvalue()` for each valid position and the length will correspond to the value of the :attr:`~Variable.actualElements` attribute. python-cx_Oracle-8.3.0/doc/src/conf.py000066400000000000000000000102001414105416400175640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # cx_Oracle documentation build configuration file # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys # If your extensions are in another directory, add it here. #sys.path.append('some/directory') # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. #extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] # The suffix of source filenames. source_suffix = '.rst' # The root toctree document. root_doc = master_doc = 'index' # General substitutions. project = 'cx_Oracle' copyright = u'2016, 2020, Oracle and/or its affiliates. All rights reserved. Portions Copyright © 2007-2015, Anthony Tuininga. All rights reserved. Portions Copyright © 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. All rights reserved' author = 'Oracle' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '8.3' # The full version, including alpha/beta/rc tags. release = '8.3.0' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['.static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Content template for the index page. #html_index = '' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. html_copy_source = False # Output file base name for HTML help builder. htmlhelp_basename = 'cx_Oracledoc' numfig = True # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). #latex_documents = [] # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True python-cx_Oracle-8.3.0/doc/src/images/000077500000000000000000000000001414105416400175415ustar00rootroot00000000000000python-cx_Oracle-8.3.0/doc/src/images/cx_Oracle_arch.png000066400000000000000000002576101414105416400231560ustar00rootroot00000000000000‰PNG  IHDRˆ€¯ÃúUƒiCCPICC profile(‘}‘=HÃ@Å_SE‘ŠƒÄ:dh,ˆŠ8jŠP!Ô ­:˜\úM’GÁµààÇbÕÁÅYWWAüqssRt‘ÿ—ZÄxpÜw÷wï¡QašÕ5hºm¦“ 1›[{^!  †ˆÌ,cN’Rð_÷ðõ.γüÏý9úռŀ€H<Ë Ó&Þ žÞ´ ÎûÄaV’Uâsâ1“.HüÈuÅã7ÎE—ž63éyâ0±Xì`¥ƒYÉÔˆ§ˆ£ª¦S¾õXå¼ÅY«ÔXëžü…¡¼¾²Ìuš#HbK BA eT`#N«NŠ…4í'|üî_"—B®29P…ÙõƒÿÁïn­Âä„—JÝ/ŽózvfÝq¾§yŸ+½í¯6€™OÒëm-z l×mMÙ.w€¡'C6eW Ò àýŒ¾) Þ}k^o­}œ>ê*u£EÊ^÷ywogoÿžiõ÷U r›¥Õ8bKGDÿÿÿ ½§“ pHYs%%IR$ðtIMEå(Z²ùJ IDATxÚìÝy|\eÝÿÿ÷93“=é¾ïÐÒÒV(-–B±ì Š"â‚ðS\n¼·Û¨ÜÞêw½En(›”–­ m¶t¡[š6é’4i“&i³Îd¶sÎï,ͤIš¤™äÌÌëùx¤Éœ™Ìœ¹æsÎt®w®ë2Çq€”aÒ©…€ Ť"€C@bˆR @Š! H1D)†€ Ť"€C@bˆR @Š! H1D)†€ Ť"€C@bˆR @Š! H1D)†€ Ť"€C@bˆR @Š! H1D)†€ Ť"€㥠0êAIRiU­êÁæ/ð”ë[¿Ü%/+£ÓËyÙÍß'Žª!Yš8j( Çqšý©>l ‚ʪjUV]K£@ ÉËÊhþÊÎМÉc%‰àÀeˆpÆÚB{J*8E^Ëè¢9“Ç*/;ã”IXDè“ÖPhcAq!cƒªÊËåol¿¡AþƆ“×54´Ýà>Ù9¹±—ssc¶O=g¦$iôØq=º¿ÖÀ躳h\€A@@€^É/©PYu­ö”Tt{»â¢BùTY~T•åGi8H!Ù9¹ÊÎÍUvN®¦ž3³ÛШýÈ"¦¡8D8­ú@Pù%ÚXPÜåm*+ÊUÕÚëi`DX0pˆÐ¥²ªZå®èr´Pk(´{Û ÐcÙ9¹=n|—aA@üàÝCþÆ úÅé¢ÙSÆÄÚ”UÕjCA±ÊªkO¹®²¢\ï¬]#c ˆ‹ìœ\-¼ìŠNƒ¢‰#‡êâs§ô"H’6wºÆÁ` uµN=wÝ‚Y4À" HqeUµZº~û)Û[§‘# –ìœ\Í¡¦Î8'f{^V†;Us¦Œ¥‘úˆ€ EÕ‚zuËÞS¦“cÄÀmºQ4qäP]wá,åeeÐH½D@‚:5äolPþ¶­:T´¸Ò´35gþeçä¶mc4@ߤ˜¥ë¶Ÿ2j¨¸¨P›Ö­¡q®—“«içÌÔœ ÄlŸ=e,kô@Š(«ªÕ†‚â˜p¨²¢\ùÛ¶¨²ü( H(M;——•¡ëÌÒÄQCi €Ó HM)WYQ®5Ë_¤q +;'Wsç_¨©3ÎiÛÆ”s=C@ä6kcAq̶5+–1j4¦Í˜©…K.Ù¶èÜ©ºøÜ©4@ˆ’XÇõ†˜R¬:›rŽ kDIª³pˆ)åɬ³)ç&Žª[—Ì£q:0i€äC8HEþÆíÞ¶EÅE…mÛʪkµtÝv F$Â!¤¹ó/Ôœ ´]ÎËÊÐï_Dô H"á5+–±Þ eu ‰˜nà$¦˜H„CÄÚ½m‹òßÝÚv™éæN" H„Ct®³hCA1 R@‚ÛPP|ÊšC„CœÔ1$ÚXPLHR@+«ªÕÆv\•åZ³üE€voÛ¢â¢Â¶Ë Š•_RAÀ”E@ ÊªjµtýÉu‡è^g!QYU- R’á8ŽC3$žöëÐ3Ù9¹ZxÙ=vœ$)/+Cw¾ è³HY™ìP¨Ç·7ÓÓå›8‘†Ã # H@íÃ!IZ³bëÐCÙ9¹úÀmŸl»<{ÊX]·` )À‡¥pXN4*;”l[N0(ǶÚ¿_v8,ëøqEÊÊ$ÓT¤¼\ö‰’$Ƕ•vô¨<†Ñ|g-]ë–ã¨7ì†tò>ZXŽ£ð„ 2Z¶›Ã‡Ë7~¼dÛòM˜ Ïðá2ÓÓ•>c† Ó”‘‘!™¦ÌŒ ^¯”–&3-½B@`:N-G8@ïM›1S —\ÞvùÚ³4gÊX’„uâ„‚û÷+Z]-«¢B¡’Ù€ì@@VCƒ+Z[+E£ò…B2%Ù-¿ëHjí6·[.·²[»ÓãЭn¶ \Æ0 µ^ÓºŸ‘ôtÉë•wèP)-MžÜ\™YY2³²”>uª[ž#(tÊK$Žú@ð”u‡‡è½CEû4zÜxMqޤæõˆ&ª¼¬ \α[¢Ç‘ÝР`a¡B……jz÷]…öï—Žoyt2èé.Î õzâ3æÂîp¿Vw×Ô$I 74œrUcËwCíÂ¥#”>}º2çÏWúŒʘ9Sfn®ÔJ¦Ia¥ F$Ö ÿt\hâÈ¡ºuÉ<\&ZU¥He¥¢G*R^®Ðþý —•ɪªRN}½"Ž#ËquÙŽÓ6(.´;ÝTó%¯aÈcò†‡ ‘gäH¥Mœ¨ôéÓå7NÞ ä=ZÞQ£(¼@@ òK*ôÚÖ½m—™Z€3×q="¦š€Á­®VãªUj*(PÓ¶m²#)‘ ÉtµïÒÐÎí$ëJo¿ ’a² CfzºäóÉôù”9¾2çÌQî•WÊ3r$…™„ˆÄ£¯lT} (IÊw«voÛB£ÐæÎ¿Ps.X IÊËÊÐï_D£Àp‚AYõõ²jkÕ”Ÿ/ÿºu lß®\ÛVȶÛF¹cgS¯Ýk2 C!Ç‘ß0”·`†,Y¢œ÷¼G¾¡CåÉË“™ÁÔ¬‰Œ€ ¼ºu¯ö”T´]~êÑ?Ñ(ô“ìœ\]qãMÊÎÉ•$Íž2V×-˜EÃ@Xõõ ¼ý¶üÛ¶)R\¬pE…œãÇå3Ͷ)â\×a¢]趤Ë’£æÑFfËôtQÛ–wäH¥«ŒiÓ4dÁå-^,o^ž`ˆ\®>Ô£¯ll»ÌÔrô¿i3fjá’Ë%5"ºnÁ,M5”†€3àØ¶ Ç‘ ªößÿVýK/É9|X–$;‘º¥S° ÝvÕÛ=[ÍÉ”ä1 y§LѨ~P£n¾YžŒ 9†!Ã49\Œ€Àå–®Û®²êZIReE¹Ö,‘F ®¸ñƒ=vœ$iâÈ¡ºuÉ<ú \\¬PQ‘ü[¶(¸}»²**´mEGV¢uG§`÷¹å8j´í^ä2 Ci†¡ ÓTpÜ8åΛ§! *kÆ eL›ÆáBD.Æè!NvN®>pÛ'Û.ßú¾yŒ"€²Õ>÷œê^zIVMì@@fKȰЩ©yZ¹3e´6¡iÊ›™)ß°a}óÍuË-2³²8`\‚€ÀÅÚ*.*Ô¦ukhâè¢%WhêŒs$±t+U¤²RÁ={Tûì³2öì‘å8 ·Ž¤‘ü ÌÌLð^"Zp‘ Åm?WV”Ëߨ@£0HZ§™“¤ú@P¯nÝK£Hh«WëàG?ªÊßüFáÔhYêKW½“‚áJ¢k ‡û•ë¯Úq$ù-KMû÷ëÐ/©í7߬š•+y¡{‰€ÀEb¦—côƒ®ýû1£ˆ$ªð¡C*ûêWUûÃʪ©‘$¥tÄ“b—­æ5‡’ñY·>§HMJï»Oûî¾[M‡qÐ÷S̸H} Øöó¡""[ûiæêAÕ‚ÊËÊ a$Œòûï—ÙÁ ¢ýŒ0µ\bqË´rQ;M¶­ð»ï*ÿóŸ×°K.ÑÙ?ù '€Ó`€Kä·=Ô: \í§™“¤zFàzŽe©iëV-^¬ÆÕ«e755?”ª7¨Ú±GV  ã+WjóÂ…ªß¼YŽeqRè€K”UŸœ¶¦ªÝ_+€ÁÕ~š¹üÃ4W‹;¦òûîÓñ¯]Žúo*9¡Äb9ŽSdäP§©æ€¬øž{tà{ßS¸‚÷ïθDûu * ˆp cC§ï×à6 /¿¬ƒý¨×­S GM%KR£ã¤ÔÈ¡®4Y–ޝY£í7߬ãË—s’耀À%Ú¯?D@€{øbÞ¯ ‰¸ÝبÊßýN'~ò“~C‡‹%¥ôÈ¡îê ôG?Rɯ~%«Ýûzª# p Åm?³þîÒq"p“Ðþý:ü…/(°t©B¶Ý¯áPb±$ù ‡ºÜŸmëÄÓO+ÿÎ;ÕTTÄÉCD®Ð~ôëà>¬CÀ׬Qõw(\R¢p?„C‰ÅVóÈ!¦•ë^ضÕT\¬ýŸùŒN¬^òç"¨÷Ÿ ˆü wÀuÚ¯CÔþ}Kõÿ¨c÷ß/?®5ÔŠp(±0­\ï,Kÿë¿Túûß§ôyÄË©`ð±þîÖq"LGî½Wþãrß„C‰¥uä;š>±ÚÞr}üqöï×Ìßü&%Ï%Œ pöMíÿB¸CÌ""ƒÄnjRé—¾$ëwârÿNŠ…+‰Î’Ôà’p(‘5mØ ‚»î’¤Üs' deUµm?³6îÕþ}ºýû7 ”Ò/Y¡]»ŒC(áP \–ã¸gäP"×ã(dÛjܹSù_øBÊSˆûC^õɦëêE`Y :tË-ŠÉf”OçR)’Ôè¦ç›mo;Ž‚……ÚqÓM²êëS¦–ˆ\„éåp¯âÂ}m?—U3‚À±mýÖ·d;·pˆu‡‡%Éï¢iåºv:ì»í8 ;¦½_ÿºœ™º€`Ŭ?Ä"-ËRÉw(¸káP×O eê¡5r\ÓôNÒÕí8òïܩݟùŒdYI_S^N³ƒ«Þß. bt¦L¯oùåäd)Žè±'ŸÓ†­;h µŸnÿþ ñRû䓊Å- J–äš5‡¾vN³ïޤà¾}*ÿç?5Mêº" €8šýÅi“'jİ!²G‹KU[ß Œôt;ªù1Fìpû ª®©‰ù,Z\z¤G1™é:Î,½çÜs …´·èöî?اiò¼^Κ¡<³¬Ûû;wÆYúëo~Üvyý¦­úÖÿBwö6}üC×wú;_ÿâgOÙvÿƒ¿Óëë6tù8Y™úîWïÒ•ï[$¯ÇsÊõË^[£zTÑèé;‰&«ïÝó½gö9Þ—$5úúÃßžÔs+Vv{_/˜§_þðÛm—oÿÊwÔà÷ëÞ»?§‹Ì“Ïç=£}]°,¶lé÷‘#L-— /¿‡¢%ÕmÞ,Y–”$SÌHXuõ±sô{<¦<-ÖŽ«Š¹î†«.;íý]åûb.W?!I>$¯Wû5$/·Ëë¦Mž Ç~ý?ºöòÅ]:7]{…þðÓï+#=½Ûǹñê%úûï~ª Þsn—÷%I9ÙYúöWîÔ¯øŽ†ëza]ÓûqáïÑø™–,º°Óp¨7û ºù?ÍK/Éôë}%†¨ã¨‘p(aêÆë÷«òßÿNšú# °¦Mžs¹¡Ñ¯ªãÍÓ½½öÆ[n;A3ÏžÚå}†¡+/½(fÛÊ–Q@‡”÷j¿º»ý•‹/ÒÔIÚ.Û¶­h'ÓÉœ7ûÝtíå]ÞÏ'n¾A÷ýKÊÊ̈ÙnÛ¶ŽUW4=åw.¹pžþß÷¾ÑãçqÏ]·+;+ëŒ÷t¯êÿW¡~ ‡CÔqpÙs%ê^ضuø7¿IšdŠ9 ë¶›c§}+:TÒöó‘ŠJmß½WóæÎjÛvÝ—jßâNïë‚÷œ«‘ǵ]®¨¬Ö¶]’¤GŸ|N/¼²Z’t÷gnÓ®99é¿õG½óî®¶Ë–e©¦®þ´û …õèÏhé²×‰D´à¼9zàÛ_Õ°v£•>ñáõìò×NY;)7'[w|â#1Ûjëêõãß>¢-;v«)RšÏ§sgœ¥oåN=uRÛíΛ}ŽÞ·hÖoÜÚãv>“}Ý Ê+ÚO÷G8”,I~¡„«G’'–ß>eÏœ™ðuÈ" é×\®+ÇŽøÙ‘¿/æòòUkc._³äb†Ñéý]ó¾‹c.¿Ún’ã8ª>Q£ê5ªkˆÖ®úxMÛuÕ'jzÕÔÕëËßýoýã™e †B²l[ïlߥoüàÁ˜Û3JÇ=å÷?{뇔›“Ývùè±*Ýñûµ~ÓV5C’¤p$¢{öéîï< ü}ûc~ÿËŸý„LÓèQ;Ÿé¾€î¶o—ÕOÛ„C‰!â8¬9”Àuc9ŽÞ}7)j‘€@B0 CãFÔEóÏÓÏî»W÷ýçc®/+?¦¿?;øªõ›l L$iÔˆáZpÞìSîÛcšºbñ˜m¯¬y3.Ï£)Ô׿ÿ ö8庂¢ƒÚ±'6ä9bXÌe¯×£[?x]̶Ǟ|NG+*;}¼†F¿~óçÇc¶M›v·ü½ûO{Ÿ’´ugl(6aܘ~ß×öÓÞ€ž±ª«Ïè÷ ‡ƒ­æpˆ‘CÉQ7áÊʤ¨KFH8–e«¢²Jÿ~uµžx~¹¢Q«ËÛnÙ‘¯Êêã=r„$);+K‹Îך·65(òztùÅï¹ï×Ö¾=hÏ­¡Ñßíõ;DU=ëTªîæL;:îû NÏS[«h×I‘p%ÑEG¶-ÃEûD8tfÌšš¤¨M"®öàïUÔjî6©©­WéÑ ©8Öm(ÔñÃïŠUëõ¹ÛnnÛöþ+·D‹æŸ3òeËŽÝ:^SëÚÏ»£:¬óÓÓ&#==ærS0” ŸÍHxi†¡¾¼+3rÈý I5"J®ºñFRÔ(W{ñÕÕ²Îp®ö«ÖÅD—\xrs²ÕÐè×5—]sۗ׬wu{ÔÖ7Ä\î8]Wìm ‡).#‡CÄqäwÙšC ^ø´A?c "Iïð‘rí*(l»ìóyuåâ‹”žæÓû.Zж½)Ôomvõs9Z;ßyÇE]3jDÌåÒ#å)ÐÑu\.¢=")aùªu1—¯»b±.¹ðeef´m[»a‹‚¡ÞMòâõ ìÄ GÅDï7÷”éãNÙG¯GÍ?/f[éÑ Š€ÄÔrîm9äP;ÔËH +×mˆ™V킹çêöÞs›WÖ¼yúüÖ>š—; ÏãHEl@”•¥«—\Üíï\{Ùb~r¤Q0RI#ˆH4„Cîué´r„Cè €”ÐèhÝÆ­m— ÃМ™ÓÛ.?Q«wÞÝyÚû9^Ssù‚¹³üy¬~kS̶¯~þš}ÎÙÞ~Öô³ô•Ï}"fÛ“/¬P ©‰¢À¼=\ìžpÈý‡R§n<=çl}ê–èáŸý@#†m»mm]½þñÌ2Š—èI@D8ä~ÇQ#áPÊÔ/I"¯ E¼óîNUŸ¨‰™n­ÕË«×÷è>ÞݽWMÁ 23š×.òù¼ºïëwÇܦ è ŠKÆíy>R®_]­ÜpMÛ¶ÌŒ ýÇŸêö÷lÛÖÿ>ö/Fà"i§éh&r7CÍáPÀ¶e¸®é ‡âÅ—$õË")öNƒ C‡hßâÝG]}ƒžyéµno“æ‹ÿßâýâÕ#ÿx:f$Qw*«ë+ÿõ?Z¾r…€‹¤w¹_˜iåR²nÒ™bú_}CcÛÏ ~Ùýüñ߯¬>eÛ+kÖ÷ê>þð·ÿÓï{BáHä”ëNÔÖ)ßþ¶Ëþ@ìh¿?Ðí}7øý1—ßÞ¶=öÏë®o|_[wä«¡ÑÊm,ËÖ¡ÃGôÂ+«tûW¾£wwtûØñÚWÐ5¯iª³ÿí¹ŸÕ2rÈ}MO8×]””a&G´b8N ©.öëçÞhûù©GÿDƒ åyLSÆ‘$ÕÔÕËêᙞ5b¸^øëÿÊã1Û>@ßüùÿбªã½¾/Ÿ×«)Ç+7'[™é:Q[§¢ƒ%²:tdge*;+S¡pDuõ §½ß¼œled¤+ ©¾“à§ëç6LÓ&OÔ°!y*=Z¡Ň Gzõœj_DuÛ_lûù빜p殽Vûüþ˜éɇÜ/B8”²uãHZ-ßÚµ _ǬAÀU,ÛVõ‰š¸ÝÿM×^ÞIÒ»» úIR$Õþâç½?ÐtÊèœîÔ7úû¶T¯QÕñ3k»ÚWpÒ0G5–%CI¥€¨ã¨‰p¨¿w>1vSÒPGF’Ô2SÌH†aèƒ×^³­³5‰Òˆ$êpNöÑCQÇQ£m‹/5kÆ4ÜãIš —€@ÊX4ÿ<=²ír(Öê77Ñ0`Py C†ÁÔr.f¨yZ9¿m»2ÌKØÚI°ýÎ0 y’¨Ö ˆ¤Œ›¯¿*æò››¶õj:5€xæñ$ö¨”$9Ô¹³éÏ4 í¬æÑCÉ„€@JÈËÍÑâ…óc¶1½tŽ#9ŽrMSY¦™˜!Q Œ 0r(åë&Ó0”m&W¤B@ %ädgÉh÷±¾è`‰6lÝAÃ×çõ&ÞZD)0r(àÒ5‡‡ÖŸïd$IÝ{9íHG+*uÍmw*;+Sõ~…Ba¸ŠÏ0”gšªséh•S¤H8äΦ'°Ý•”gšòÆÉý7Œ¤¨q")#ÐT )HC×ëó©1’íöMòp(*Q7’$¤Ñ>_RÖ=SÌ€K’&¶vF»U²‡CŽ£FËriÓ ´qiiÍ#ú’°î ˆÀE2MSÃ<wŽ"JâpÈPó´r~—NñG84°lIC=eFÒÖ=¸Ìh¯W9¦ËºoS`Í!?ÓÊQ7-² C£=ɶ›ŸCǯ$@@.4Áç“×0hˆ8cäP\w>!wÛkçóÉNòPÔËáï>ÁPHþ@@ÁPH ~Õ×7¨ÞïWC£_MÁ MCá.?#=MY™YÊÌÈPnN¶ò²³•——«Üœle¤§+;+Kéé44:UU}\/­Z#¿ß/ÃèK†ì(^§MÃ04lÈe¦§);;K£†W^n®²³³”›­¬ÌLü§ $ SÒÔ´4…4èÝÔIÜQv9DÍ´0$MòùšG×tÕט$}Dƒ, ©²ú¸ŽTSIi™Ž;¦uujlô«ÑP0”eÛ²[–eK2d˜†Œn²lGŽÛ‘äÈã1e¦<¦©ŒŒ ådg)7'[C‡ Ñø1c4uÒDM;F£GŽ 4‚$ièÐ!:wÆtýæÏJŽÙ«7¡æhÈi9ÆçììØ¶Ç‘iòx¼òzLådgkh^ž†æåiʤ :kò$M?^cGìcÈàI“ÓÒT‰È¬÷$‡¢.‡Z‚ÖŒÇ04Þç“'‰×j€hÀ GÁPXùû µqë»*)-Ó‰ºZ54$Ç‘}Ú¿hzZš–eË’­ˆ¤`8¬Úúú¶ë I2 åædiÄaš2i¢-¸@³Ï™®¬ÌL^¬åózµhþ<Ý÷µ¯ê·ù»jëêzVÛüw,¶íȶ#ŠD¤¦`HUÇOH’¶ìØ)™¦†äæhܨÑ:ιºô¢÷jÌÈŒ. )Ã04ÙçSI$2ðS^%q'yÄåáPÂŽJÐý6 C}>¥FbÜê¢ ‡u´â˜öî? íùÚ¹gdxd[¶Çn9^œï`wZÖú†€*)/×›[¶J¶¥óçÌѼÙçjÖô³5~Ü¥§¥ñB¦˜¹³fêÞ/Ý¥?þõ*¯¬J˜“¢#I–­ººÕÕ5ªðP±žYö²¦Lž ÷žwžæŸ7GãÇŽaÄH(i†¡)>ŸŠÃáëEdäРI•€Â- I}>ùR(’ˆâª¬¼B[¶ïÔŽ=*=R®ºÆFË”]N$ö€ÔYD9ŽäX–ì¨%†¶íÊ×¶»”—›«IÆëü9çê½óÎÓÄqcyaSȬ³ÏÒwïù’~÷ç¿é@ÉáNOŽŽ\vÂtÚ¿‰:²¢Íoô‹KuàÐa½´jµ¦L˜ ‹.8_Î;O£GŽà… !Í04%-MG#…Gq+…‘Cƒ†u‡pw%¥†Æz½J7 9¶4ë õQ””Ñ‹¯¾®íù{Õè÷Ënw²q»“"tÜs4´Ä­'¡º†Õíݧ=…EZöÚ*?û\Ý|ý5š2q/tŠ7z´î½û.ýæ/©ð@±»ß œîÞ›š½ÆF¿ò÷jOa‘^xíu]yÉÅúàu×(+3ƒ¸^ºahjZšŠÃáø…D„Cƒ†phw·åxšäóÉPóÚç=~IrŒõ“P8¢½û÷ëå•oh[þRL0Ôuº,ê‚mÛªohÐ[›·èÍÍ[tÁÜ9ºñêË5kút¥§ù(€DzŒDåÔÓÿ=’ÒÓÓôÕ;>«ßýåoÚ°¤­n]5z¨—»â8Žjjêôü+¯ëù—_ÑG®¿.»d‘ÆŽE‘W3$MkIÔÐßaG’‡C~Û–[LJ ¬\ÓÔ8¯·m)–TD@t†¢Ñ¨¶íÊת7ßÖžÂ"…Ã9¶Ý£¾êD ‡Úk ½¶ïÎWÁ¾BÍž9CW½o±æ¿g޼^Ê)4üúÇÒçuèp©<OßœLÓT ©))¡öÏËŠF%ÃÐ3Ë_Ѻ›uùâEºòÒK4bØPЏÚxŸOõ¶­£‘Hÿ„IÚInH ·Œ"ŠËÎ'Ü.ñz•kš)IDgäHy…þõ܋ڶ+_–íÝ1“€áP{¶m«)ÒÖ»µ#¯æŸ7[ŸúÈÍšÀE®7lÈ}ô¦ëõ›GSÉ‘rÙ–Õ‡²Iüp¨ã›˜#éXuµž~q¹Þzg‹>ÿñêü9³)àjy¦©Œ–)çΨ›$‰;ÉÃL+ÏO¨Ý5$Möùäk]g(…Ã!I29…ö^c IO¿¸\ß|à'Ú²sWÊ…CE­¨6ïØ­{øc=õâKj 4Q$.7~ÌýàÞÿÔìÓûP6É}ÒtGG*Žé§ýI?þ„ŽUUS0ÀÕÒ C3ÒÓ5Äãé[Ï k ¡ÚU5‡©g¥§ËgšRk@d}ÿJŒ ê¥wwåë¹—_ÓÞýš®êåAláPÛÝÙ¶,ÃÐÒe/k÷ÞB}øý×jþys)ËÎÊÔ=w}Nê½½ykbM‹§:nÿÆ G´jý[*:X¬[n¼N—¼wE¸HIÙQÕÕ7j̨3j  å’Æz½âñèp(Ôó_$4„CgRZš2 ãä~Ÿéþ'ÉqC@Ô ÿ|ö½þÆzš‚Í5ÐÛšI¶i¹º8¡ìÛ¯âÃeºî²%ºýÖ›)6$OwöS6dˆV¬Z+Û¶£~ãTÃÕtIY™~ûç¿êpÙ}ìCi2ðLïî*ЃýEÅ¥GÛ¶eefjÆ´É:kÊ$=u¢Îž2YgM¤!¹94€”“i𙑡#‘ˆü–¥T@+ê8ò»xÍ¡„– áˆ!)Ë45Þç“ëÖ)¢¨¨¬ÒŸRùEûåôa½)yGu¥©)¨¿¾JJJôÅÏ|RcG¢\*#=]·ßúae¤¥ëÙ¯tú— ©µ,˲õÜŠ×TVQ©ÏüV6„ÂÁÎ=…úû~¬h4ö½8ÐÔ¤{öiÇž}1ÛG¦³§NÒÙS&µ}Ÿ:y¢2ÒÓhLIo‚ϧ&G‘ˆ‚ŽÓùZ#I8zÈÐÉ5‡Ü1r(λ()Ý04ÚëU†iÆv%:DEíÆ»»÷èŸK_Piy¹œ>G´[JÐpÑ1gYÚ½¯H?{èÝ~ë‡5îl Ê¥<¦©óæjÙªÕ C1%ãÈq×›é ÃmÛÖ¦mÛU}ü„¾ô¹OjÊĉ0À~ýÈßO ‡ºS}¢FÕ'j´iÛΓ CÇÑÙS'58j &'‡‚’K¦ijZzºj¢QUYVl0‘¤åaF¥4CÒH¯WÃúºWŠ! êÆš·7è±'žVS0¢¾ôJ;íþ•4¸9HîØ¶JÊŽêWü³îøäÇtåâ‹),*.-Óoý»‚ÁP‡²q¿v]PÇíë¹èP‰~ö‡¿è[_ºKS'¥¦®^EÏü8v•­PéÑ ½ñöæ¶í>¯WS'Oh7âh²Îž2‰õ$…a^¯r<FU×%[€’#‡Z?—&,—î»Óòúçy<îñÈgw#2zèD°m[¯¬^«,}NQëLN(©5­\w;ÐÔÔŸþö/555éú+/g9XRªŸýþ:Q[SëŒêê}ÐÖ±c•úÉoÿ ï|å‹:{ÚŠþ@\ï?ªè`‰Š–ÄlÏÎÊ<±¾€æ3 õù4ÒãQe4ªzËJª(ä8jêã P…p(»%)×45Òë•×0Ú¶%Êþ6¢,ËÖó+^Ñs+^•e;gP˜„CO|–mëñ§ž“?Ð-¸^ÓCÁ ²%%úåCщÚú˜7¨”^s¨µì8ŽNÔÔêçø³¾ù•ÿOÓ§ñ6vÔHeef(ÐÐÇõš´³ P; c¶>LgM™¨éS'ë¬)“4}*ëHŽ#¯ah¼Ï§á-AQжeK E ‡â^7®ÚI¦š×åó)Ó0d'Ðþ» Q;¶ãèÿ^X¦¿ºRöœP‡:?ñ9Ž#Û²ôô‹+GôÉ[>$Ó`$Ñ`©>Q£ß>ò7Už8áÎúuQwö&î8Ž*×où«~ðͯiäðaG>ŸW¹áýóÙe®9‡VŸ¨Ñ;ïîjÛÖº¾ÑYS&éîÏ|LÓ&3 %éз‘aššœ–¦m«Æ²TÓ2¢(Ñ‚¢0áЀÖÍ îJË×PGC<¥·Œ"ê;zçÛyyåú÷+¯Q8䪣ť'>Ûvôü˯kùëk)ºAâôÛGþªŠªêeC8ÔÛ7ñòÊ*ýú‘Gå(, ξøéé’÷^àêÿð—­×ë!à¶,]^•nšëóifF†r=ž„ ˆ94xu3Ð I9¦©ééãõ¶…C‰²ÿnE@Ôbí†wôø3Ïˑі”÷ö«5Ãìëï÷Û—£Aß9ÎiÚJz|é3Z»aÅ7À"‘ˆ~ü 8$§eѾÖúôÚuY÷¤–ÛVáþCúÃßÿ¥H$BqäóyõË|K?ù¯¯éš%kÒø±2Mw}t9笩ºÿ?ïæÅà=ì$7%Mðù4-=]#½^¥µLÛåÖ.ö°ãÈO84èu·‡WóÈ 4ÃÐp¯WSÒÒ4ÁçkîK€ýOL1'iÛÎ|=úÄÓ²,ë –‚ëxŸîµ,éÏÿ|J9ÙÙZpÞ\m€¼²f6mÛ!Û±Ý[¿NbíŠeÛÚ´u»VœµVºîjŠ ˆ#Ã0t奋t奋$I¡pX‡ÑÁ’Rí/>¬ƒ%e:P|XUÇk|߆ ÉÓÏ¿ÿMed¤óBp‡>t’û C#¼^ óz²mUG£j´mW4ˆ0rÈuuÓŸlIÙ¦©ÒM³mÀ ûŸHR> *¯¬ÔãÏ<«¦¦¦>Ÿtä’¹9]4­œÑÃnjjÒߟzVãÆŒÖø1£9"ãìPi©–.[Ñ6rÈiy\õ÷÷.›V®§mãØ¶žyq…Λ=SÓ&M¢Ø€’ž–¦YÓ§iÖôi1Ûý:P\ª%¥:P|XJJu°¤L þøü‡ÊëÑÿ»ï3j/ w8ÃNrSR¦ijRZšÂŽ£ãѨšl[ÇÔþÐHËÈ!ÃÕMO8Ô«‡l©'Ÿa(Ó04ÔëUF˶DØÿD–ÒQ$Õ£O<­òcÕýPÂè«òÊ*=ú¯§ôÝ{¾$Ÿ—Amñ ‡õÈãO*ޏ·vüP †ÃúÓßþ¥¾ó ¥§¥QtÀ ÊÍÉÖ¼¹³4oî,I’mÛÚ¼}·þöÔ zwwA¿?Þ·¿|§ÎŸ=“†àýÜIžfçóÉr…Gu–¥zË’­]Ã$Ü2rˆp(1êætZë'ÏãQži*Í0ä5Œ¶éåܾÿÉ ¥{ãÿþô³Ú‘¿÷ŒZ×LÍå¢ÑC½eY–¶çè¯O.Õ>ý ŽÊ8yfÙË*:Tâ¾ÚuQ ÷G-*ÑÓ/®Ð§?z3E¸À¡Ãezyõz½¼z}ܦ»íƒïׯ»‚Æô<†¡,ÃP–ijœÏ§†–)è"¶÷®D˜V.¡ `¸Ò:Zh¸×«œ–)äZˆg`¥l@´uçn­ysCâ‡C  u¼5omÔ‚óßÃzDqp¤â˜ÞذÉ]µë²:î¯Z–¤7ÞÚ +.Y¤‰ãÇR|À ¨«oÐëë6hŪuÚSx ®õÞysuÏ]Ÿ¦Ñ¸Çvô皦r[¦ k²mùm[–iè õß4tá ‡vôP÷Ûiùj>.Ë4•ašÊh)Ôú•HuŸLR2 ªkhÐÿ½ð’"‘hŸNR®Y·%Éj>‰èÉç–iÆ´)ÊËÍåèì·2qôÊ굪««oIãYs(îç˜ú½²æ Ýõ©Û$Wz’GÔ²´që-_¹Në7mQ4jÅý1'Ž£÷kòxL^î0Häi†¡4Gy$)lÛª±mÕD£gE‡³[¾†x<æñÈg4WAk-ØIP÷É %¢'Ÿ_¦’Ò²>´N»ýsÃ.ôãÁç8ŽŠKËôgŸ×W>÷ŽÎ~Rz¤\«Ö½-Çq94€µ¼ríÛºöò%š<ðx C?úöW5mò^ îàÖNrÇQ–i*Ë4åHж„DþvÓÑI:e”‘¡“Óʹ¯nÚ¦…se¶¼¾Ù¦)aÈk2[®·SµîHJD¦&ýóÙËìcÌ㺩¹óÜ¡øMœeÈÑãKŸ×}_ÿв23iì3p ¸D[wî–ã¶7Ó8w;¶­­ïîÖþâMŸ:…bÎP8Ñ›ïlÓŠ•ë´aËvYƒ8½À—?÷q-^8Ÿ€;$H'¹¡æ&>ÃP¦ijd˾7¶„E ¶Ý2Žbäëê¦5Ä3 C9FI±Ý}uŸ0R* Ú²c—Š’ݧ©å94P'>ÇqTxð¶ìØ¥%‹r”ö‘mÛzó- EÂ.{óqÏ›`¼w%èÍM[tÖäI2MÖ(ú"ß~­XµN¯­}[ þAߟë._¬Oôƒ¼0Ü! ŠÓTŽijŒš× ;Žª£Q¥†Â-—ÛOKg¸f÷“/j¤æà Í0”fšÍß[½tÃh»CÝ'¼” ˆ‚¡V¬|#qá;ñ9Ž£+×há¼ó•‘‘NÃ÷A Ôî}E.+ ÔÛ•Ý{ •“•EQ=TY}\/¯^¯«Ö«¤ìhÜgܘQ*?VÕãÛŸ;ã,}ïkL À%’0 haõzeµûã^GRÀ²pm[¡v¿o Êî'GÛ·i†¡ ÓT–a(Ëã‘ÙEûÚÔ}RI™€hýÆÍ:TZÚëƒ×5áKÖÈ“ìþâÃZ·é]{Ùû8Rû ìh¹•vϹ)ÀZ>TrXeGÊYS 8`0¤µ·hùʵڼ}wÜŽÓ¼œl]sÙ%ºáª%ZúÒ«=ˆF ªï»Wéii¼X_ŠLmf´ûžëñ(·u›a(hY «y:ºmË–d;ެ–ïv÷uæ»ïþ¶ï¸‡¦$Ó0d:Ž<†!³eDP†iÊ')Ãã‘Ñò¼¹xu¡~—Q Фg–½,ÇîùB­‡ƒáÆ#zPŽ=§_O¤==à—¾¸BK.ZÈ(¢>Ø´m» Ó”l›îPËYdžihãÖíD@ÇãŽü}Z¾j­V­ß¨@S0.ã1M-Zp¾n¼z‰ÞwÑ…òù¼ú׳/é•Õoöè÷}^¯¼ï=rø€´Ëmw~‘âÐ݇©”Ü÷ö£‰Ç‘¯%ÜÈ‘$§- r¤¶Ÿ#Ž£¨Ô"ã´»?ømïtÕ.Ž#ŸaÈcJoioË”p¦aÈpœ“AQ'¿ëP÷))%¢77oÕ‰ºº^ÀŒêߟÓÓlÇÇ=Q[§u›6ëÚË.åhí…P$¢Ý{‹ÜqrOÑ‘CíŸþ¾B…"¥û|' éhE¥V¬Z§«×ëhEeÜgú´ÉºñªËtÝ‹5|è¶íooÙ®ßÿõ‰ßÏ÷îù‚æÎšÁ `ð¥h8Ôíݶ|7$yŒv½†¡Œv×µqEG!5I¡–>´Ö©yw;ŸŠ­»}è £×·Z¦¤tÓ”!5?-ß=-#‚º ä8R»¶q¨´Hú€Èhõ›oõùØ;³°GÃÓÓtÞ°!òÒŽu:ŽôêñW­{[—\8_9Ù¬áÒSUÕÕ:Xzxðk(Õá–Ç=PrXUUÕš8~ʼn”å4iõ[›´bå:½»» n3|è]{ùbÝxõ͘6å”ëKÊŽêþ×ãsÂ'?r£®¿Š©NàŒôc¿Œs†¿ç¨9Hò¶ ¤Ø@ÆQsˆÔú³Õnÿ£üA¶Ýá6§ãQóHux|o»mfË—Z¶Ý´A먩x·}¢×N•ôQIé,.éq!9§Œuq:9=R··ëê:§“SN¿ç†áŠg¸QÇÑ{†æéãÓ&jöÈáÑÃûr:üÜ~4RûNôtcà%éóº@„+8¤ˆÖ¼½A§vlwvõ¢ã»ÇáPwë]Mº–áíH7L«Ïœ=I™-uŽ]8r˜¡×Ž“Ï4{tò3$­ZÿQ/ìÚ[$Û¶Ô}Ô“pH½¨a¡.ÛÖî½ûôþ+/£8‘ŠKhŪõzyõzU?·Ç™}ÎÙºáª%ºö²K”—›Óím-ËÖ}þN¥G+ztß“'ŒÓ¾s̼W@ÜÑÑ—iÜzô8´=uŸÄ’: :ZQ©M[wô²ƒ¸«ôާˆî¡žž†:é OðpÈrÏÌÐmS'êòq#O}:†¡ÉÙ™½^‹höºåÆc?v Gm”=Ò¡íSk­ËëzrtU߆«ÚÁqÉ›ˆã8:|¤œÂDR«ohÔÊõ´|å:åïÛ·Ç5b¸®¿òRÝpÕM4¡Ç¿÷¿ýK›¶íìÑms²³ô‹|‹©M¸ë¥ZÝ$Ãþ'ˆ¤ˆÖoܬ`8tÚNâΦ•‹]·«7ÓÊI§NßÕÃië8rÔ<'æÂ‘Ãõé³&irNÖ)÷e†NCZìxÛÂj=9!;Ž£P(¨µÞÑ'>|GíiÔ74* µ«§¾ŽJÌ>ÓZŽ×ZS0¤º†F 9Í( ‘D-K›¶îÐòUë´~ãVEÍÛ§b IDAT¢Ñ¸/2€ÁG@A8”ju“ ûŸ`’6 Ú³¯H =<œÚ)þ‰Û>¦ôô ~8qBUÕÕª¬ª’ßï—ßïWS0(+jɶ­£aN·–‹N½m‚O+7.3C_žy–æË“㜻…l[«Oèá}ÅjŠFe};M74ú•¿¯HsfÎàÈíF0R4QÏ¡ތŒë¦†]tòv\ú&‰Dh R HX'jëôÚÚ·µbå:,ŽÛãŒ3J7\µD7üÿìyxÕ•öß[[oRK­}—÷ݼcÁ&$$™¬d›@’ÉÊ$B–ÉL’ÉG&C&aÈÀde²Í—ذÀ¶c›ÕÆ›lYû¾´ÔkUÝûýÑ-Y­µ%u«ÕÝç÷<~dUWWº÷Ü[¥ûÖ9çú(-ž]ZÑŽ®n|åžFÙ´gWÞënêl‚ ‚ ‚  $¥›ß¤‚ýIHJ DB¼~ú L“O2ð'O™%Ë2TUAVV\.–,Yðz½ðú¼èééEww7:::ÐÞÑÁALÓ€$É#"Œ†ŽŸz‘C\È’„…¹xï‚2Ú¬ãF uøüxüR3¶uAç|Jqh2KLÓÄk§NcÕ²%Q¥¨KWüþ a"vâPééHšÒŸ .ÈA‰¤B× :zûjjq䥗ar—óØmV\¿} vïªÂUkVÆdŽ÷‚¸ë;÷¡§7º§5+–â+Ÿ½: ‚ ‚ ˆÄC‰Céæ7©`’’’Q ÄÉÓçÆ],ž,­ÜxÓ‚¦iÿ®i,šÙYÙX´` ÓD @Kk .Ö_¹sç ÂBQêŠCNUÅË`k~TÆÆ‡^íéÇOO_@GxQ\šfZ¹ñl=yæ ¬V ÞI‹Mp{œ‰84EZ9‡¢ôiAÂ&‘4œ:[‡½ûkñôÁÃpzârÆ6­[ƒ›ªwຫ7Ç|^ð±ßâôù‹Q훟›ƒ{¿þEhªJOAADb!bfcÎÄ\j{²ŸHMhÀãA}cs”ÓÆôë§ ‹LŒAQ(Š‚åË–cÙÒe¸zë¼qòÎ×Õ¡¿ß Ó4JâcÀòÌL|bÙ,vfŒ›RnÐ0ñtk~s± AÎcZ¦¾± žAˆ¢¾ÍM.…\@ 蝹uy|0&a|!Iºzh<ÛG BŠª"Ãá€=Ã‡Í§Ó §3999p:°Z,e²,ã©ýûI "’‚WNžÆ¾ýµØÿÜ ðú|q9‡,IزanÞU…k¶l˜Óômv›-ªý>òž·ã†W“CAA‘X8OÞÅæxÙ-DTdz> dÿÌl)RJ"å" €Ó.DbÌT1ž84ó”s“¡ª*ÊËËQRR‚º u8rä¸Ýî„M>39ï¦<>µl!òm–1)åÀg˜x´®5mí0Eô­7“œãìùzøýJ37­V¾œVNQ8ŽaA(Œ'äŒw|gff„ð#„€®ëÃÿ¼>ÜýýèéíC¿ýýýœÃ0M† Î98çµÁ&óëѶ)ŠŒ¡H*Æ$Ȳ I’Bíb·Ã‘á@VVœ™ÈÎrÁbµ@U¨ª UU‡¯kH@ºü,Ç‘¬‚DzÐÒÞ‰'žyûöDs[GÜγdavWWáÆk·#ו˜´mEyXXQ†‹ Mî³cË|êÃï%Ç ‚ ‚ ‚˜)”ŒÚžÚ>éI9¨±¹ÝÝ=—#ÆTÈ™¬c+]öI’°bùr”•–âÕW_Ã'Ob`Ð9†‹ôñ ÀjÁ§W,D®Å±*SÃðf¿Õ5âÞ~¨’Wqh¨-;{ºÐØÒŠ¥‹ЄU+3Ä®*ŽˆÉxéKC dgg£´¤$"‚H¡:üþ À¡Ð  uƒ§’Ò4 Ц‡Ré1‹Õ I’`µhP‹~9EŒù7ÒöÑ5–b>ãõùQsègkûñ"å¢Sgê "rh¢´r±vP1ÆcÈÉqᦛnÄË/¿Šã'ŽÇuqz6^»/€fŸK33†·uƒø¯ºj܋֜ãÔÙ:ÜrÃ.šP'méñÄ!1Ã^HüÄ-„˜‘tÃ'Ò…†æV쫩žšZttõÄí<+—.ž]UxËÎíÈqo˜l¹ê üî¡ûQw©T”',íAAAĸÉ–Ý É×Z¨îÙ+»“]RL âœãbCèA>r‘|¼É6þ‘C!Ë2¶nÝŒòòR:|Íá´Zó§MݺŽGÏ7àcK*‘­©8ëÄãõÍ8ï„&K1léMâ/5¤„B?ƇâãÇA$ŽAö×>½5µxãô¹¸'?×…¯½{víÄŠÒäš Ã’ä,AAA±€Ä!jût³?Å_>O)¨£«^¿DôÐxâÐDiå&‘fä9Qíe qóž=8vü8^y决º³ô$Æðfÿîyí ¬²„>]‡ßà ‡†ðùýhïêFqA>ݘ§dô8` 칸)‚HyL“ã…¯b_M-jŸ?Ý0âr‹¦¢jÛ&ì©®Âæ«Ö’ OAA‘î8DmŸnöOb÷ $!'†uJ DÝ=#ŠÕO$M–Vn¼Tt3òœiÃbѰýêm(.*Âþšgà÷ûçՄׯëè×íÄâÝS êèêî!hÊVgãü_$°çæd8DJr¾¾ûjjñ䳇ÐÓÛ·ó¬[µ»wUa׎­pØíÔðAAAñ¦¢¸pa~ÛHâµ}ºÙ?…ÝREjd+IH¶ÎN¦‰™¥•›ÛÈ¡‰X²d1²³³°¿æ´´´ÒÛ`˜&Z;:±fŲ˜E[¥,Šq??ŽÝÀ¦ž$Ò›Þ~7ž:pûjjq¦®>nç).ÈÃM×ïÀî];QV\H OAA1—äåÍoˆÄ!jût³? »•‚‚”˜~R*‚¨«§œŒ˜NZ¹XÔk™½ÃsΑ››‹›÷ìÆ+¯¾ŠcÇN$í¤O+8çèîîML72ŽÄ!‚H$ºnàðK'°·¦G^z¦iÆå<6«×_³»«wbýÚ•$¶AAA$ŠåË£Gç§m$QÛ§›ýQÚ­¬X‘ÓOÊD\tv÷@Þ2UZ9ŒØo~ˆC#'.»ÝŽ­[¶ ##Ô&Ý„'â>N:º»Á…€L‹š“ôÀtý›Ä!‚H§Ï]ÀÞšZüõÀa¸ãrÆ6\± {ªwâºí›aµZ¨á ‚ ‚ ‚ Œ€m>Fâµ}ºÙ¥Ý @Àn‡5æŸÔˆLŽÞÞ>0Æ&èÇÑâÐdiæ¦ëÀñqxI’°qÃzäçåáÀÁçÐÞÞN)ç†zŠ1ôôõƒ›&dj“ ¦©ñÄ!1ç~LÄÄtv÷à¯ÏÆÞšZ\lhŠÛyÊKа»º »««P˜ŸK OAA1è9w¥óÍ(‡¨íÓÕþh.@ÇË/#ëÃNúkI(¨ÑÓׇP‚91N—ü‰)C™êD4îÇë è().ÁžÝoÅ‹/¾„So¾ú€ÍßIOÌÑIzúúÔu¨ªJOãôÁDã@`<ßH/ŠŸÝ‚„6b Á > ûjjqôåשQcO†ÃŽ]UÛ°§º kW.£†'‚ ‚ ‚˜¯'67ÃmšpÊòü0ˆÄ!jût´ÑCýœÃ×Ò’óOÊDºa Ð3Ç€˜#ßpf:±«úzääæàù^®I‘vÂÐð¹Ünè†AOÓí ‘Èž›GN4Ç ¢Sœ‰æµSg±¯æ ž®}¯/.ç$†­ë×awuª¶m„F:AAA¶¢Ý0æ@D$AëFÉÒö]º©¹9%.=e"Ã0áHi?eŒaÓ† ÈËÍÅ¡#GÐÑÑ $JÓ<ýƒq+ÞžF³™Bq¤µ£OÔ<‡}5µhjmÛyW–cwuÞzÝ5ÈÍɦ†'‚ ‚ ‚H"T¯~!àã¶D—R è!j÷t»†iÚíã€æõ¦Äü“2‘Ï燪iЃÁ¹ðš„]'ç *+‘“íÂKÇãS'aš&XE¢Dµ†ªjðx¼Èu¹èI"Éüx>›B³ÅëóãÙÃ/boM-N¼v*nçÉvfâ†k·cwuV,YH OAA‘¤¨ŒÁ Ï4+‘8D¤›ïLÓnÀXPR$h#e¢Îžî9šïì‚ dffbgÕ”㙃"%²5:{zPQV b&=Çæ)‘ôÏA'^;…½5µxæÐ‹ðñ¹i+2¶oZ=ÕU¸zÓ•P…Ÿ ‚ ‚ "Eè5Mä) ÔD,<“8”Hã‘äŸ6v…@ŸiBJ¡Œ^)³²ÔÝÓŸv Ͳ$cõªU(--Eí¡çp±¾º®'4šh®qOo?==L¿áÈ‚ˆ Í­xâ™P ¹öÎî¸gÅ’…Ø]]…¯ÝŽ,g&5·›ž’uÒ&qˆHR=^ì¯}{kâõ7ÏÅíúbA^>òsópåëp¾î<Μ=‹K `ŒM[(ól ƒ^=E$ˤMâPZÑÓ×{xŸix[yIîþìíØ°nõ¼³×0 ~éeìÝ_‹Ã/½ Ó4ãr›Õ‚ë¶oÁžê*¬¿bUêDvAAA3F›`®Mס2{,ë‘8”Hã‘äŸvv{…@Ç©å4)5ʤŒ@ÏIc°Y­X·ö ¬^¹ ]]xýäI´µµb`pº®/†N´X)’x 4œˆ¹eÐãŧïþê›#¶7¶´á ßü.¸ç¸rÍŠyaëé󱯦=pýî¸gý«pó®¸nûfجVr‚ ‚ ‚ bË$/¶è:hZlÒYÑúZâ q(é0„@[08£q›L@4_>&pÎ!IŠ‹ŠPZR‚Átuu££³]Ý]èììDOo/LÓ„"¤È36,ÍäMw! 0\iÚ)îèæ•Ü7u_Zaš߸÷Çcġ᛫aâ‡>ŠÇþí» ³±»§O>{{kâÂ¥¦¸§¼¤h8…\QA9AAA1.Š$A ”ÎjÌßÑB !\HžÍ‚t²G%ó58”t¶›B Y×aL0.+ D)JŠŠC#ᜃs›Õ†ò²2”•–‚sÃ0 ÑÑÕ…ÎÎôõõ¡`ƒ|>ü~Dçx‚ÑÐçCb##v« v» ®l22xáèÑô™@SÕ‰bxä—xáø«“îsöB=º{ûëÊž3»Á j_8Ž'jjñ‰WÁy|ÆH†ÃŽ];¶bwõN\±j9AAA15’‰±qÓ§1ºh Qi± m•“8DÌQÛ „"÷t!&o{<êƒ%ˆæÛ€M€ Œ1Ȳ Y–a±Xàt:±lÉ¡EU]×aè: Ó„×çƒÏçc =½}7.E–‘™™ &1d8Ð4 U…¬¨P‹^¯7zˆ&Ðy À$E’!IFj…B7Á >Oú0äãI–ýYØVÎ9 nBpA²Ú,ÙWS‹_ÿi_Tûz}~äºâoÓëožÅÞšZì¯}ƒoœžã¶\unª®ÂÎmaÑ4r‚ ‚ ‚ bZäÉ2: cÂi¿hQ¬i˜ö²4Õ"fÞøie·®9äŸDr%eÖ®SG biêìqšt‡j©ŠUQ› ¹9˜4åÜÈ¢Pf9qL“sêxÆÀÍP”‘"0V¾a³hp:ì°[,°h 4E‰8‡É9º@PǠϺaÆÔŽhmU™;6+,š MUB‚Öˆ}tÃ@P7àõàöxá cÞfBpΖºïù¼qú¾ûãÿŒj_»ÍŠâ8¦\këèÂÏ<‡}5µhli‹ÛyV”aOuÞzý5ÈËqÑäCAAA̘lEA—aLøò*àáMátsQCâP"G’7~ÚÙÝjðs>©Ô È’å”yÑè†Õþï¾ùF(Jlo?>¿¿„¿ì?ˆã¯ŒÛµf93qÃΫ±§º +–.¢‰‡ ‚ ‚ ‚ˆ ‡,c0üÒö„ÿrŽóZ,SG‘8”Hã‘äŸVv›è˜:Å!I:PŠ9‡¨?’¾É:zÝx½®=ý¨ªŒ+¯Úøýhh¸„S'_ƒÇã™ñÕª*(ÉυâMꮣ?cŒAUæ¹ i*Ú{úC‘4ñ| ’$¸²“•1Öž©¾+Kp93`µhhéìF hÌÈÆV¯¹å•°Z­€z¼A<{âr³X»¸.çœGVÅš@0ˆ»¾szzû£Úõò%¸ýƒïŠ™ïŸxýMì«9ˆg½Ÿ?—k”eWoº{ª«pÍæõ1·‚ ‚ ‚ rdnÓœtñ™!”«>@‰¦Á6ѺB¯± 8DöÏÝ !ѵÕ0`bêµC ;…^úÒ=‚ˆÄˆäŸ|¨ q¡¹'Î\„¢($ÀÅ¡Y,X¶|Ê+*ðÚ«¯àì™7§uƒȰÚPVUQfusÎÍÊ„ª(híêEP×c.Œ! ©*Šó\p:lcê±i¸”ÍjAEqZ:z0èõEm«§U\¶lÖ®»v›œsp3$ŠqÎ!1·ÇÇßÀ†•‹±¤¬(©ýïžûÄéó¢Ú7?×…{¿ñEhª:«s6µ¶cßþƒxâ™çÐÚÑ·k[¾xvWWáÆk·#;ËI“ AAAWl’„ Y†wŠ("0„@C €RMCÆèÌ1´æ™8HJ*»9G«®G½¿C’`—$ðrÙ”ˆ¬VK¨è|²9;Í×3îÀfKïbðg[ñF]cÂÐåf Õ~Ò4 ÖoØ„œœ\ôõtF?éY­(+È"ËSŠCSõ öÐñÚº¢¯Gíd&Ë(+ÈÍj"Â6Í¡(„€"Ë()ÈAs{7¼QF¦È²ŒmÛ®Á‚…‹ IR¨–—Û'†a@’$¼z®\,+/NJÿ{ôñÿÅSDµ¯¦ªøÞ?~iƵz<^/ö?÷öí¯Å«§ÎÄíšr\Yxëu×`OõN,^PNs3AAA1§+ .˜fÔK†MÁ ò¹CÙ.HJ$%•Ý=¦‰nÈú¥r HQRJRH ÊËËγäqvš«gc(ÈËKÛË÷ƒ¸ÐØÓŒnZ’$ -F°¤8ªˆI’P˜5e­œéº²ÍjAŽ3}GŽ36«eÚiå&³_‘eä»²ÐØÞ5ej<ÆV¯½šÕv¹}ÅdÓ€i Ô5¶¡¢ VKr‰‡ŽžÀÏûmÔûýÎObåÒÅÓ:‡ir¼ôÊëØ[S‹Úç_B ¨ÇåZ4UÅŽ­°»º [ׯ‹¨±EAAAs‰Â²d½¦ÕÂ5Ðeઠú‹6A8”4vs„„Õ ÓÊ8”%Ë“¼TĸsNª\ˆEÓ¢ëPRÑSjÒ´ô jhëÂÀ4k®0Æ=¥@Ä…@3›b•c¦=—Ÿ“…¯¾@pÖ©æ„°YTäçdû4[v+ò²2ÑÖÓi[‡ÚÖN«q¼~\jïÂòŠ’¤ñ½‹ Møæ÷ˆ:åà‡ÿæÜxíöiïþZ<ùì!tõôÆí:Ö¬XŠ=ÕUØUµ ™š‹ ‚ ‚ ‚ ˆyA¡¦aÀï‡95³ç8 \Ua“$0jƹƒÄ¡¤ÁÇ9šõé¿€¬(TÕ1%-R”ˆr³³©š)­Ü|›}fÕŒ1亲ӶõÎ5¶BblÚS4‹úš¢ ß匋84Ôw…¹ÙhhëšuÑAI’P˜ëÕ,Ã…ç<—=îASäæ\L»q$‰á\CKÒDýƒøò·¾¯ÏÕþÛ7]…O}ø½S×=€§k`ïþZ¼yîBÜì/ÌÏÅM×ïÀîê*T”ƒ ‚ ‚ ‚ Ž‘ë–B \Uq) -1L´v2œÁäòçÁ 2d%Š’Ü"Ñè6™Ïv&{;§¸í ¡¨¡6À'Šú^c‡C©ªŽÍxÆ K IDAT.”"bQÊD…y¹Ø$ý"/Ì09vfÛ‚¡0MSÌõ{|€¬‚ó@\Ü4Ç™ Æ&žçfÝuBÀnµÀfÑ¢®ï36‹6\whäÄ?[ب筜¬L´÷ôMzl†é?¼pn² ýƒ^deØçµß™&Ç׿{?šÛ:¢ÚAy)¾}×g!I㸆#Ç^Á¾šZ:z†aÆÅn«Å‚k·oÂžêØ¸nõ¬£Ö‚ ‚ ‚ "ÞXC¾¢ }¢)ãˆCC š&Κ&ÊT‰’ÎÅ ÊT•mïá-úÌÊy² -…×’R'‚(Ç… —…i°¦æÊrÒ4‚¨±­ºŒj_Î9z{{ÀÂãCSeø<Ö ÷×T‹Š â&]î>«&ãÂ¥ö/Ø ¬\²pÚߟêØ8îj·hèëíAP7&ôé`Äç.W΄âÈHt=ˆÆöNdeTÎk¿»ÿ?ñWOFµ¯3ÃüÓ?Àa+z­«ÇÞýñׇÑ爛½ë×®Äîê¸þš-°Û¬4ïAAAI…K–àýæÌ^¨lÔudIrzY2¶¤Âzs ×bB ×0àæ|Æ/“gÉ2\²œÒq)#ådgcÜeßùàè¤OŧO„÷{úÑçñ€›<êý›qþÜÙÐÉØ¤“bYi1Ö¯\:'®ìõx°ïÉ' 1\EQŒšÍEøõ9 Õ ªü؇¼ÜáÝg;'>ÇáçŸGcsË8_bç!SÃþ½déR¸\9Qµ79z½óÚçþß_ŸÅÿüù¯Qí+Kîùê(+.ÞÖÝÛ‡¿8Œ}ûkq¾¾!nv–`÷®¸éú()̧ù– ‚ ‚ ‚HjŠTÎaŒ\O›$zh$ €›s ƒpÉ2ò…484¯íf: ý¦±¤8]Š’òKû)3+hšJï4ßÄ¡ˆ¦ˆes0X4-íÚÑ0Løý:„ˆN ’$ «V­Fww7ÚZ[¦ÜßnµÛ®ñpåL‡L|äIب# ˆ2ŽØùÔ$ŸY4 6«>¿Ê›SaQV­^UôPèë~¿Ã4¡Èò¼ó¹WNžÆ÷þýá¨÷ÿ¦+× ¨ëxî…ãØ[S‹¿ “ó¸Øg·Ù°kÇVìÞU…+W¯ É– ‚ ‚ ‚H)Y,¨ ` µ84 Û4ÑÏ9ŠvI%žKcRPª3äå†CˆY• PTX,iá)#©Š—3 ÝÝóÇÑIŠ+.§ª*§Ýuœ#Ô‡#U¢A³XpÍŽ*¼|â8.Õ_D ˜P¼Èp8 iLÓŒûµ8vX4íòµ ‰A#ëTŒ"uO°hûð`6Ãqªïkš†Ì ÇØv"dç°X,¨\¸ë×o€Åb‰º„ƒ0 >Ž.|õžE]èm7\‹UËãÞÆþçžÇÀ '.vIæ+×bOuª¶m‚Õ’~b1AAA‘0 4 —tº`3X÷dL!Фë°2†E“êMŸdJAqHÐÏ9z °04SqHPC¹¦¥ˆš2‘¢(Èrf ³§"No©OÛ›ˆˆæˆi¦SÆåÌ„ªªixš¾.„€ÅbÁÆM›±léR´¶6£¹¹^¯£SÐZ­–9seI’àÊΆÇ럑?9Ž)SæEs Ñ|_,§ÎÌÌËÇB8ìv”––¢¸¤ÎlE™–€7dƒ˜g‡ßÀ?|çèíwGýc¯žÄŸŸ:7›”—bÏ®*¼õºkŸ›‚ ‚ æ•e%¸ëÓ‹Œ ;‚Aüúxþø«Ô0A#ưXUq>Àl^éªÍÒ¢ëè•$+ 4ªO$Í;»ƒB Ý0àך­'+‰±i5·¤Ê…hªŠW6ÄÅKóÀii¾Œ{s Ç• - ¢™¶©²,£¸¸Ë/œp?—3<,²ÆÛ•UUÅÛo~A}Fß·hê”ig+ Á…À–M›°lùÊ ;àõÁ?Íè®;çÑÜ!„À·ôSœ»0½9µµ½3æ¶833𖪫±gWV-[L“*A‘pdIBv–®l'v¼>?úúÝèésÏIö|¤¢¬Ö­þ}ÍÊe$ADX ihÒuøg™B‹ðqŽº`™’„,YF&EM ‰CóÂî¡DCÎÑošð„×0g+ ÆP’†kÍ)#1ÆçrEd§JŒÓÒ|9ÍÁ—ãKÓ7Ø,§½©ÛMÌ©ƒÌªE|¯BŒú_¼|Ž1ã0»Ùñȯÿ€g½˜°ó˲Œ«7^‰ÝÕU¸fóz¨*Ò$‚ ‹¦ªxÛ ×aÇ–õXŪq_T ê:^~ãM<÷Âqüqßþ¸Õß#‚ " /”+Œ¡BÓÐ Â?ËEÿ¡ˆ ç¡Ú-òTY$ÛödbífÜœ£[×a"¶«lŒ¡TÓŽ+Í©³âÆòss IRâÞÚ#qhΚƒ1  /HCH‘%È ÓÙŒ"U¦Â0Mp.æ¤mMÎCçš!œ ˜œC–¥iû›¦s.`˜f\üš1Eaã^G"8pä(þó—¿Kȹ—.ªÄž];qãµÛáÊrÒDJAÌ ”—à;_ù<–.¬œt?MU±åª+°åª+pÓõ;ðOßM­íÔ€AAÌŽQë?€JMC³®c0F/¤€]GcÈ•e8$ jº§Ÿ#q(ávBÀË9zLA!b^È!I(NÓ,U@ Dc‰(!qhN›C’däåä@JÛ”,˰j*ýÁ¸_×M˜\@–ãß¶†aÎêÍZ“s† mTtI¬Å! $éFœÄgÆ`Q4(²œpÿ:_߀oÝ÷ïszΜì,ÜxívìÞU5åÂAAÌ5·Üp¾øÉŒ©Ó8«—/ÁcÿöñÃÅ_ž>@ IAÄ̘d¿TUÑkšhÕuÈ1Z#“Zo3 ¨ŒÁÊ\² »,'¿Xö'ûãCèeêAÓD¿i" ôpJÅXŠC¦(TUdσõ¸D’R9{ òr¡© à›G"çž98‡¦*¡¢4Db 6«Ò€±, ÃDÐ0`“ã_Í‚s1ã`%Î| ì6KLýo¼c #n‘Ä$Øm–„ ž}ýnü÷¾Ÿ?÷s©Š‚[7`Ou¶lX7/Ä1‚ ‚Íß}ä½øÈ{Þ1fûņ&î#¥¢ÜTU|þ9lt¯JDs¨ªŠ¼\WÚ¶s^¶m=0 =æhpƒ?ìVK\RØ]¾O x}³ÊdÇ0àõ!'+,Ê }&Õ—côøapsÖõŸÆC–eä¹2êS†aà«ÿr?Z;ºâzžÕË—`Ï®*ìÚ± ÎÌ š4 ‚ ˆyKa~.>pëÍcž_îÿÇðÛÿ÷dÄö§X-ÜóÕ/`û¦«">¿ãƒïƳ‡_œUj]‚ ‚ ÒŒi¬ÉXC¥¦¡Ý0àá<æ+ €.Z ªi"C’àÿ“Ò¼íÉþÙùC(µ¡—sxMΡ‡ÓÈÅã5jPJ¹|EF r²³aÕ,èÇõl"æž9<—U³ ';;mÛº4Ï…3Mü1ï@ƺûÝÈw9¯ÊlŒ1 x|ðÆ RÅë`Ðë‡3Ã>¥ Åfè t÷»ã"€ªi(ÍËI¨OÝ÷³GñòoÆåØù¹9¸éúس« •e%4YAIÁíxÔil ÃÀ·~øÓa1h<üîúöp÷çïÀÛÞríðö奸ñÚkðÄ3ÏQÃA153Xà×C™ª¢ß4ÑÔs#‘JËÕošp›&»$!W–a•¤´m{bú0þp]!çÀðº^¼<‰‡SÊ9e ÷1„’j´ ¼ÝݳŠ|Ð4-ºTOäE i ÆT”§u{[4 0uH’‹b„£:Ðä=}(ÈÇ9G{Of¯A í=}pØ­“Ö c³ð᎞>˜œÇ¥Æ™$I`F0Ô§ â÷{ŸÆŸØóãn\·þ›·cãºÕ$ ADêSV\ˆÅ *°¨² ²$¡»·§ÎžÇ™ºú1û._¼ÎÌ p.ÐÑÕÆ–¶ +KV-_ ›Å Ý0p¦®^Ÿ/n×Q^R„ÝÕ;#¶ÝÿŸÿ=©84ò9êžûÄÒX±tÑðöÛ?ð.¿—šZpîbê›ãڳř™e‹*±|ÉB83¨olÁÙºzÔ7µÀ4MTA1 €ìpʹV]‡DŠ®X#Âÿ8‡›shŒ![–ag *cPÒ°nø¼` \Cž`„k yÃ> û'‹³Ý¡(»Mƒ•¢†ÆrѪåËðÒko@Ìð !¬V+$IšXd"/Jhs0IšåKÓ¾ÝË rq¦¡%.ÈCÏ€v›vkÌmïìéGP7bòÂu]½näçdMz#š‰zýèðÄE¢¬0/a~tüµSøáÏþ+.Ç>öêIœ½p ‹+˱xAyøg/(‹¨Ï@A$?;·mÂ]Ÿù[äºÆ¹¤¹µÿù«ßáÉg”—àÑ7âüÓwgÂhÖ»>ûq¼ýÆë‡ÿ_üùõâv=|×ÍåË/7x¼^ìÝpZÇøÓ_ŸÁÝ#¢ÒâB¬Xº'ÏœØoÛ†+qß?ß5üûmŸù Š‹òñ­/v[äsØC¿üúÕï +ÊðwîÁ®[a³Nü¼öü±Wð£ÿx Í­1ïÇÙ°bÉB|ãï?…% *Æý<¨ëøÃ¾ýxà‘_Â0H("‚ Òˆ,”[C…¦Ámšh5ŒË‹ðq`8M˜è4 0! 218eÉT³ˆRËÅÞ/xLáÔqºÃ/ŒKq¶{HÄ,T”áÚY´¬?–”ˆ–—AðÙýaš&ENc Íù99ǂʲ´oû¢¼lœkjŬˆ&éÀ¡(‹%ª,ÇææÀ€ÝîÁ˜·GwÿlV 2Öˆñ;qH7L´÷ôÅ&JkdIBq^bêiµ´uà«ÿò£1o2Ç÷À ^~ãÍ1 ~…ù¹ÃÂÑ¢Êr,YPÊòhªJ+ADa³Zp×g>Ž›®ß1é~¥Å…øç/}eE…xèW¿GcK;[ÚP^R~F`¸ë3ÇmŸýʘ¨‘u«—ã–®‹xF9ô≸^×hÑb_ÍsðM35îSã ·ß!Þäˆ$9rÉäºí›ñ±÷Ý:nômP7„„œ{îþ<eê?ç¶m¼›®\ƒÞùuœ¿ØÓ~œ)¸uþî#ï‹Há7MUñ¾·ß„u«–ákßýW´¶wÒ€#‚ RŸ.HJE9% M†8WqÛߌ+*x}~ z¼c¶_·} €Ð Y÷>ðPÄg +JñÁwî‰Ø&Ë2îþìíÏåÿù¯8{¡>®×UZ\ñû¾šÚÝk_<ñZäß*ùS~ïãxׄ©Y³23U[7Œ‡zûÝxãô9œ:[‡AOÄgŠ¢à‹ŸüHÌûq&¼ÿ»ñùÛo‹‡Á ^ó,Ž{eŒ´réb|ç®ÏÑ`#‚ R!â¶P.1† UE‘ªÂÊx<¯aCQ$>ÎÑ¢ë8àR €6]GïˆÚ3ó¢ý“Ý昡 À®KÕnh q1D«aÀÇùäÑkq°›#AW¨ª(UU‡¢ å"ˆòrsPŸ‡wÆN&à÷ûa·ÙF}@3Û¦³;1c(ÈÍE~nNÚ÷ªÈ¨,.@¿çÒô#\¢ì@À ¡µE9.d8lÓ^´gŒAŽî¾AtôöÇuÑßä&š:ºPàÊBnv›8U¤˜ÄÞA¯m]½ðõ¸¦–“$ •ÅPynǯøçü.5Í+Ÿ69G}c ê[ðÔÁ#¨ÚºÿòÕ/Dõv4A1÷”—áoÞvcĶ“gÎãž}pøS\‡÷ߺïܽkÌ|~ìÕ“x¢æ9ÜT}Y˜øÛ÷½O<‚¶Ž.Àßy3V\ŽoïìÆƒý6®×e³ZàÊrFlknë˜Ñ±:ºz"~/.Ìú»†iâ¿û39ŠâÂl¸b EN =û†¿<}zòœ>aø»šªâŽÛÞ½û–ámë×®ÂÒE•8wáRLûq:df8ð±÷ݱí™Ã/âÿüègðú.¿äóÎÝ»ð…;>4\£qÍŠ¥¸fóúáë'‚ bú8% NMÀi¢Í0b+M±Ö3R$€i¦9¼Ý"IÈ G©saDâPÔ õW !4hš„SƶBJ€Ý @‰¢ C–iÀOƒ”\u[½l).^jÄlb=ÈqHûDâPLšbÀã™Ý@g «WPý¡! ¾¥ ¾`ÔE|§;ÿ2Æhìè‚ÓaG~v4M‰Jèa`ðø|èìqÃãÌ öž> z}Èw9á°µÄ× èìë‡Ûãç"¾â,#ËnÁÂ’Â9÷›ó „@iQÁŒ¼æ‚ÚŽáá_ÿŸüÐ{h°AÌC>÷ñFˆ¯œŠ'Ÿ=„Ïü6¼~úlÄ1þõ¡ÿÆÕ›¯ŽŒ±Z-øâ'?‚»¾sJŠ ð·ïgÄþ?øéϧêmº”EÞ›ƒº>ãˆÛ®ž¾ˆß£ˆ„øÖ~‚§kŸœ>Ï~qøóÇÿüW\llÆ3‡^ÓFÛü“ŸÿkW.Õ«W o/+*#Å¢£åcï»Îp_À#Gñµ¹Ì~Ø·ª¢àïGD=}ì}·’@DA¤.s¸ÈŸ® ÔipsCˆÙ 2³°}HXðq瀃]–ä*cPƒ@!‰$l÷dñ›‘ml ŽP})¿ðs/ç0ÙյСÝ€Â2$ yŠBu†f@J DkW,Ç_ö?;«cô÷÷U”‡–É«"ÝŒ'ÆÐ×Û;»IŠ1¬]¹œ:"ŒªÈX½hžãô´n¼3s^÷ ½~¸2p:ìiL !80tÝîA x}à³}ؘƒþ¼m]È´ÛëÌ€¢*%1JôáœÃ48Ü/z<Ð #®ÂЈcõ¢s=KUâûßü2Àç÷ãÂ¥&ÔÕ7âBCêêPW߈ž¾þyáã¿ûËS¸ýïŽ(NA$Y’põ¦«"¶=ôËßEˆ #9u¶ŸúÊ·Ælïsàßþ%¾qç'‡·Um݈[6à»wÁjц·?{ø(ž{ñxܯ­¤(2•ñh‘g:øüþÈv‹ò~öÈoþ8,ǹ —Æ=ãñÌ¡#¢â¢ü¸ôc´¼ýÆë‡ÿo˜&~òó_O¸ïŸ¨Á‡ßóv亲‡Ÿ_B‘éôÇAAÌ @QàÎÑn˜Á‚Œk&1ƒ¦ ·iÂ""1Ø$ [x=jÈ‘@ÛB ìg£~ú8Gpè§ÑÈïH ¶{¨¿‡üØ.IоCOŠÓ'%¢ò²’Y/F÷-Ž’WE ¼ÙÒ×?ËEg!P^ZL1r#ß…òÂ|4´uÆýfÆ ÓD{o?:zûaQU¨ªiÄ€3¹@@סáPád‰Uýƒô z *2,ª i„±B„Š=t¡¼¼s!1ÆPQ”’|WÂýÇfµbõò%X½|ɨ±êF]}#Î×7àbCÎ] ý™þe.ô «§…ù¹4Ø ‚ æEyPF¤n¸ÔÔ‚c¯žœÑ±þòôì®ÞõkW oz‘a׋û~ö_srm#E©Ùþ;:ç¹Í2uýÈÓç/â‘_ý>&×2:º(+33ný89®,8ì—SxŸ9-mîÔuœ»p ¹B‘¦ªÈÏuIÛGAÄÌQC¶,#+QÔošÑ§ž‹ãÔðzRX¤0èBÀ ?œAG„íwH,ŒA ÿúþÈŸsiûœ0ûG i!ä°H¨x¹›Ó‰²{ÜglNYF®¢Ä>ª, IIÈa³¢¼¤g|Œº1aØt#ƒÌ0 ¸Ý³:FEi v;uÈ(Ö.®@¿{ý^߸"Q¬'ɡŎ a hQ-ˆ$Š!Ê49¼f`ܶ™K‹1§ÝеK*çµOeg9±aÝjlX·:b{K{'.\jD]}ãp´Ñ¥æ†7[ì6+ r‚ ˆyFYIQÄïgÎ_œÕñî}àaüâ{¡ªãÿyòïÿõtõôÎɵuvGžÇ•íœù=Ìnu:µëá—^†9Íú’MEA^.rs²‘çÊF–3ÃÊ¥‹æ´'£|Ô¹V,Y„¯|æã“~窵+#~/-*$ˆ ‚ â±VP$FŽ,ÃÍ9ܦ ç¯í$P`)b˜BDFª$Aa BÑGrø§Þ6TãHÅÄÑGóYl`£Ú~d{è½,­#õÃ…€Ø !`„êœ#ÖÂf ³ô.láZU™áZUœ†nLHIHÓ4”ÍR r#¨ë°Z,iï$±šƒÁ ܳ:Fiq!4M£‘; ›EÕ+áðkg`F„H$ƹ‘Î(l˜üzö7qÆ (2Ö¯\›%9ý¸¤0%…ù¸fóúám†i¢¡©u—Â)êÂRK ê]±r234È ‚ æ£û;g)Þ\jjÁ£ÿ ·ðÝc>;yæ<þ°oÿœ][[Ggäs–Õ «Å`úµ†Ò£ ÑãÚ;·mÂîêغa,3xFŽu?N~®È,²,áÖÝ»¦u «…þ ‚ ˆx¢0†Y†K–­ºŽÀè²ó,ú&"ê…±PÄ‘Œ·_Øö¡ï Õ8ú)ÐÂÙe,Q¾ø,bx SàBˆPú7„Åž°¤‡¯MDaÛ|y©;Úöµ0†Mƒe„¨EâP Ç}J^”,cae޼t œó¹^¿ÛA–4ˆb9åzæóÇ_W jli›Óº3]=0MQ/(/'M­íÓ>ÖèzF-í1³ókŸÿn¹ñºyבëÊšÕ÷9ç8_ß@ƒ ‚ æÀÊjüB Û0†S’ }žlŒ÷2u@ø'xÉzh•1Èáú62uD¶) 9Oü£ÚTç&.GI飄­Éþ?O?ú]Ã?µpÍ)—¢ÀNÑBq%%"Æ/¨„$1LWa7ôù|èëëE^nnÚB1¾>ø|¾Ú" K2–,\0'5b’—3×nXƒg. £× ~³ÉtÓHÁùˆI ùٙذri”.ÍfµbÕ²ÅXµlqÄö¡úFC‘Fu—BâÑÈúF‹*Ëp÷gïó]‚ b~0èñDüž•™1«ãIÃ?üÝÇÆýì†Wã·ÿûÞ7Oæm]Ñ-7\;mèæ·ìŒ¨©Ä9Ç¥¦ÖÙ?ëÙmxÏ-oØöÇ}ûñÓÇ~ ÷@äÅ›áw! IDATæ+×N*ź'£yTVcKžxæ9LA‘$(Œ!ƒ1dH¸ª¢Û0Ðg„²×¤ÓÚ)›â÷X7‚‡Xø_–,Ã¥(1qm("Nc;U/,Óá@iq.\jˆ*HŒr9I’pöìYìØ~Mò™ŽÝ¸Ìž gÏž -žOË–p%c(+.D†ƒj‘D×Ü ‹J ±°¤]ýðø „€Ýb‡@]cëðÛDl$†«V,„o Æ€L‡yY™õ¥Ï䣸 Ÿƒ " *mõò¥% æ Ò;æã·ýMĶŸÿæøè{ß1|]¶xÞ÷öÝøÕ÷ÎÉõ=÷Âq¬[µ|ø÷E•åX½| Nž9Õ÷eIÂ;ÞZ±í©ƒGÐÝÛ7kÛ®X¹,â™úÄë§pïONx?NEó(1jQEci›­ ‚ ’ @¾¢ _QàåÎá1MøÂ/(Ï«UdÖH1qH ô’°M–ac vI‚]’†£¨èÉ01ã9e©Úº)*ABŒãzŒ1ôõ÷£±¹iÚ¢FRÏ9ñp2IBcSúúú¦µP>²_$IBÕÖ­4b§ c ùÙN,*-Äâ²"ç»aµ¤¥`!æ ­3¬6çç`qY•!?ÛIâA‘’ŒŽ<)ÌÏÅuÛ7OúüÜT–•ŒÙ~÷go‡Õz¹îgíóÇðà?Ž¿<} b¿;n{7Š òæäú~÷—§ÐÛïŽØöÍ/þ]Tç·h*¾÷_Biqáð6Ã4ñð¯~Û–/Yñûëožp߬)êƲ§¢£«ºn ÿ^\˜êk¶Ð`"‚ ˆ$Ç.IÈWTZ,Xj± OQ …Sƒ%‡æÝ!!"GQ°ÈbA™ª"OQ`¯»“0”8RZùXµ|¦Ò¬Å$îÇ9ÇÙ³gÓÆâ9Ïž= >7G÷ ÃêËhÄÆE‘ÓJ´˜«·cP‰Œ ‚H úÜxùõ7#¶}âCïAñÊ{oy+~ÿðýøá?ß±ý­×_ƒ-ë¯þÝâGÿñ(àŸÿý#R¦Ù¬Ö ëÅ €_üþÏÛ*ËJðÐ}߯’…§<ÎÌpàÇ÷|Û7¯ØþDM-[Úbb[¿{pŒ]ã±cë|ý󟘓~Œn@੃‡£>×HÊKŠ`±h4ð‚ bÃJC—§(Xjµ¢RÓàRØ$i¸–ÌœŠ$%Ì–ƒU’-Ë(WU,±Z‘'ËP’©~U ¤òÅU”£´(-mã¦.SLK’$áÂŋؼqJm6cPWWu$Öqˆ1”–¢²´”3X5 ²$Á0MjŒ"1MS©!‚ ˆ´áÁ_<ŽŸÝûOÏÞÜ|ì·8uöLÓIJŠð[÷`é¢J@Pׇ÷ÏrfâÎÛ?qÌGÿZ;ºýîüôÑßàîÏÞ>üùöÍëqÝö-xöð‹q¿¾ßÿåiÜö®·Á•åÞ–—ãÂC÷}oœ>‹7Ï]ÀéóÑÞÙ W¸¾Þ×n؉^ÿú1³ëì…úˆß¯½z3>ðÎ=øÍŸösâÂ|ÜtýÜþwEõü=Û~œ?{ì·¨¾fëpÄXEi1þû{ñã‡~ã¯DK{'„P+–,¶Wâ¦ëw ¤0ÿñ‹ÿÁ#1lG‚ ‚ ∰Il’À†0M pŽ`¼SÑ‘84÷&#ôB®S•eÆ …?§r󓔈4MúU«ÐÔÖ1j1\D¡Y3Æ088ˆ‹—ê±võš”ÍÏ«bŒáâÅzx<ž¨¢V&J÷·zÙ2Z|²,AS #ås¾ÏÕÕ1Æ`ÑT(²LFA¤ ¯¼qG_~›¯Z;¼Í•åÄÝŸ»cÂïX-—SÉÝyLJ=BLihnÅ/~µó¿O>ƒ[n¸«–-ÞöÅO~/žx ^Ÿ/®×çð÷ßü¿øö]ŸCEiñˆkаqÝl\·fÊcx¼>|ù[ßG[XôŠgëêÑØÒ†ò’¢ámŸÿømøè{Þ‡ÝY–æ´§Cgw/~ñû?ãö¾{x[†Ãޝ}!é‘™áó·ƒÃf£AGAI 2•1Ø$ LnÓÄ€i" „±[Ã!qhNû–°HŒ!S–¡bl6’…æ7)Ÿiýº5c¼p:SŽuuuÐgø–\º£ë:ê.ÔE%DLÜ/ ›¯º’3†dجS\̘˛,ËÈ´Y)<– ‚H;¾öÝûñìá£Qíñ³Ç~zF¿bnº~GÄç÷ýì¿ Ƙgñïý䑈TÅù¹9¸ýƒïš“ë;}þ">òù¯âÏ£ê!EçÏáS_ù^~ãÍØ>_þñÞGÔógfÆqèðÑèUK)–ý8ùÍðã‡~ 8æ3‹¦Á™™1î‹e-í4à‚ "E¸dš†Å Ê5 EªŠ,Y†M’À‘ª,jHнIá!!Á*IpÊ2 ¥ªŠ ÊU.Y†Þ¡äBIõ ,.,€3Ã>·;ìÔÓwѦæf´µ·¡¼¬<å".â=ÔÞÞŽ¦¦¦(ì˜Øg†#¢È/1{œvtô¹Söúæz” !à̰“cAiÇ Ç‹¯þËðî›oÀ{ny+ÊŠ Ǥ5ôxñÌ¡ñèãBs[h‘éÂʈ}ž:p/žxmÜsœ>¿ýß'ðþ[÷ o[P>w©‡}þî¹ÿA9ú2Þ±»W­Y M?²Ýïàè+¯ã©‡±ÿ¹¢:¾Ç åñx§üÎéóñ¡Ï}ŸþèûQµuã˜Ï/5µàOOÔà×Ú‡‡øáH­‰¢®fÚ3¹Î~õǽ¨}áîüćñÿÙ»óฒûÀóß|ïÕ  7@\xgóêƒìûÔ}ز-[ž¯×±^ïxccg"fcÿ˜‰˜˜ÙuÄÆ:£ñŒbƳ^Ë#Ù²d–¥VK–Z}±/öI²y“ â¾ê®wdîU@ƒ @â(…Âï  ªòåËÌW•¿÷Ë<¼¿—ºÚø‚›H¦xë½3<ÿ‹WxùÓÒá„Bˆ dAa9: Î¶g#–EBkrZ“ÒúÞKÒIp¨´Å)~Å,‹¨eQcY„‹7ñ¨yåÖÒŒ75e*|©L6Ë¿ý“ÏÙó—Xi²¢Öš]»vò›¿öwÜÕ¸iÇœuxPÈáo¾ý®^½z×õÏïu^îÛ¿—õÏÿgªeY‰’Içòœ>wyv}ÐJ²ÞG£Šk©>pp5ш4.!ÄŠüñß¾8ûý_ÿÙפBĦDس³›®öm¤³YFÇ&¸tµ@ßù±±¡®–PÈÁu=¦É{>wm±fç±Ç1•HòÞ™óÒ„Bl]ƾæ/³:ò}±ý½çþÝJþ¦'ùçíMdq¥PÅ-4ÿF9­q_krÆàÎ .Íÿ·,Ï©ŸrÃJU DzSX.Î*Ö‹­Ôl–ÖÌcô½ÊeŒôÛ Pñ"…â¾ýûøÛý³Š» •R¼öÆëüJK ‘pxs·Ýõ˜ÈåxíÔ©×ÿ¸fIõ~xÿ>ÙÛe ´Ô×181]1¢’mÇ¡¥¾N”B!„B!Vf5ÍKylMäω¢8±]œ{ŒÎìµmÛ³A޼1øÅ`Q®˜}4³GÎÌŠ:ó)›éÜß+y, lYD•"\ÌŠg¨Kîñ³JoS[³òè¡DBa²ùÜÊ;œR råÊ8°i7ÛZ—r+¸|ù2ƒƒƒ‹ˆ–ºÜ_4æØ}¥§®ÆÚ×G*"ø¶‘ýÑÍçi¬IƒB!6@uU¶m•ä¹r¹|Å,'-„BˆMF©•e•âo6ëDÿ¼r/ø#53‡^ü]PÌ: ŠßÏý™¡°œ*þ;ó\îœ×3Æþf^]––¥d5/kÌCH©Ûžsîž?!¥0Åg‚@3Y?¶R…¥âæül~}Üu™¸UÖ½ØÜ¶D€H)Å#çÅSo¢W‘-¡µæíwNÓÓÝM,Ût{·˜uªëT*ÅÛ§O/ºßRƒC–móè‰îš…$V®:!V]µâåL¶R»¾›XuÕU²¼œB±Þ÷9'T–ÑßõÌò}ŸS¯½F*^`ƒ¶å—ÄñC¥‡®ƒííÍœ½Ò¿¹®KeP† Ðloo–$„Bl Ññ ©!„BT Ýûø¥üRnQ[jãŒî®NjãñÒTœmcaØÓՆѦ얘ZÏàֆݭøXóöb1+,I]=mͲ÷B!„B!JG‚C÷>~)¿”[”Ô– E"a8@)ætôîáо^ºZ›èli,Loð„ñºu]¥PJÑÑÒÈöÖB çÈ¡ìÛ³«X³Š§V;xH8,=tÔÖTS[]]Þ×¥2*K}¬†ÚXµ4!„B!„B”†‡î}üR~)·(9k«ð“žÄèÕ5rÛvøí/}ꪪ⸫èíî §½Û¶6,«`=—•sl‹žööuwÌoMu_ùÕ/`­r©2m O>rRzç: 96;:[ •á2s†ò 9ŽCO[ !Û–†#„B!„BˆÕ[Í\b¥NæKpHˆu±åD-9teY³ÏËùR–Í‘ƒû¹oÿ¾;ž»{[ ;Û[7$‹h]‡¥ØÙÞJ϶–;~uüð!ìEYö ë×âè¡´45Jï\g ñÍõ58›d/¢à8­õ54ÔÆ¤2„B!„BQ+ͺ×ßH‹”«Õ½X6k+ôÓž\q–O8dó•_ýü•i)ºZ›ØÙÞŠmÙë–I´®™C–Í®öVºZ›°¬…ïËŸÿ–µâáÙÇ‘ž¹Avv´v¬²Ø[§Ü2‡”R„‹íÒP„B!„BQ:ó'å%8$¤îÅ:Ù’¢#öS¿‚ ¥Çdïî]wý»î¶f÷ö„qB¡µí·ëTgN(DU$đ޺ۚïú·÷öòà‘Ã+ 2Ôׯ9|`¿ôÌ 9ììØ¶á„²¼îŽm„C’a%„B!„Bˆ5²Ô}‡îö72Ñ_>çOÊ-ÊÜ– Õ×ÕrpïÞe?αmž{üQ¬% Ìõ±jŽíÝI}M¥Ôšdd¬G·){CM”£½;©‹U/©ž>ùÔã8Îò÷h¹oß^êj¥gn –†:º¶5Zãàæf …ؾ­™Ö†:© !„B!„B¬¥‡æþ]%‡di9)·Xw[2@ä8‡î+ŽŸKÛG)ØÞÑΡýK,EB‡v÷°»k¶UÚª^·Ì!ËbO×6íî!^zÀ`ï:gö(2fI_ 8|`ŸìSvwµ²-,k}‡ˆr[V@YŠ°í°»«M†B!„B!JË÷ ÿJphsŸG ‰MÊÚª~dÿ~bÕ5€ZÒ—RŸ|úqjª«—õ: èjmæèÞZêQJaÙöêúíZ7 »°RkC=GööÐÕÚÌróŸjc1>ùÔ“(Û*\¬–ðÅ8rð€ôÊré#½=DÃκ‰Êj€´,ªÂaŽôvKƒB!„B!Déåó’à”{ÓžúÍ_‡[væ·µ¹‰ý{v-ié7¥¡PˆO>ñøŠ_/^];éí!lYK[?t¡F·æ5£ÛG{{Ø¿£ƒÚeÄæúìsÏvì%«Rн»w±­¹IF–2 ‡ÙÛÓ‰m­Í‰ëß¶—Ù”±ûz:—•='„B!„B±d55·ÿ_‚CR~)÷¦±s¦kmˈlÛæäýÇ1K ^XŸ{ö©UïÉb[ ñÚ@2Åô²&ÅײÛÀ4ÉL@C<¶êeñBŽÃgž~jI(F)yàö*³«DiÕÇjØ¿£ ­Íš¶½r¼$imØ¿£‹úXµ4!„B!„B¬™Éz Iù¥Ü›†W!õhm哸ä#'ˆWWÝ3Ò …xì¡KÔÿ É\–t>ÏD2E"!çzh­ïZ޵hnJ)´Öä]d*Ãd"E&›'™Ë–,='ˆÜ#°¦”"^å©G–‘¥ 5ÕÅ9¸«‹cWDT|)ý"äØÜÙIS]\€B!„B!ÖVU•‡¤üRîMÆ­®Œ›Ê·t€(óÄɨ{d¸ìÚ¾Ž¶m%yMÏ¢,ʹ‰L–ÉdšéT×ó1ÆÜ6_Ên«”ƒëûL§2L&Ò$ÒYr®W|]ç%y½®Žvv÷Ü}ÿeY<ùðÃDÂaYÊTkc={»;°”UÒ Q9.+g)‹}Ý´55ȉB!„B!ÄÚëèXøçw›Ì—à”_l(µX¿Ýd¬­~"Ÿ8ù¶²ïëFqèÀ>jª«Jòz®ïãÁ¼×1øZ“u]&SiÆgùZ¯zÌ1­Éç=é Ó)¦iry—@ë;²…ü Àõü’oMu‡ì»kPÁVŠ'>!£J9x@KCGöv—dO¢r\VN)…m)Žîí¦¥¡NNºB!„B!ÖG{û?›Ÿ94w.F‚CRþ­X÷eÆé쬌ãØê'²«½ž®._ï[ð÷¡ÍýG•ìõ\×Ç¿GvN 5é\… lËÆ±-l˲ …ħ¹õ­ ßi£ÑÚhh€áŽì¤…”2@ðÀáûøîßÿxÑçÜÕ³îÎvU6ºššê›¢Ò›ÓÚꨛ¿9¤B!„B!ÄZ:t^~ùãÿWêD¾‡¤ìBÎ}÷UılùQ£,‹‡ŽßGU´t§:›ËcfÒ|í´÷þµ1fñ¯U–QkM6Ÿ/Ù1WE£œ|àØ‚{=9¶Í‰ãÇJº¯Xû>£µ&™É’Èdïú­¢ioÀõÑÈdIf²h­i‡B!„B!„X_gϾ1æÎ¥åæ’àÃV¬ûr¬V`ôÌ™Š8KN'ôîÚIwWJ©ÛúŒ {è¡’¾V&Ÿ¿û$¹)ž)a€àÑïÇ·Æ”Rtwu±w×Ni„›T6ï2‘L“÷¼{^oÊérd€¼ç1‘L“ͺr"…B!„B±aüÑQÆ=ïöVJp¨HpHÌíšÀD®ˆã‘rž~äáÛf°•R´mkeWÏö’¾VÞóÏ–)—>«9×/éSîê馹©áŽc汓„BŽ4ÂÍü&&˜NgId2ø^0§lš6àšD*Ãt*[ØL’†„B!„B±¼¾>&‚àãù“J ɾCRî £Iß'××WÇ#¢¢g„h4:ÀPÊâÁ£‡Kþ:élnÁ¥ÖÊ)½BYél®äÏûÈ÷Ï©_E4æÙÇ•ÆWŒ1ä\‰dŠD6wÛ>[åÒ´cHdrL$Rä\oYKã !„B!„B¬•˜Öh «µ‡¤üRïe.[ì¯ÕAPÇ#¢¢xM 'ï?:'ÃÅp°wOI_#К\Þ+ëNjŠƒHÎõðKÜÈîSŸJñðýlj×ÔHã« Æ2¹<Ó)RÙž`ŒÙ$U,ï¤29&§Rdry !„B!„¢,ÏŸ‹“à”«Õ{™³€Éb?5tL¢è©GNb³{â±:Û[KúüA qýy{µ˜òiMfv1ä}Ÿ`ÞžA«ÕÙ¶ÚX! d[VaY?Q‘´Ñ¤²y¦’i¦Ó2ym  Pk-*<·BC&ï’Hg˜J¦ÉäòFˉB!„B!DÙJiMZç/$8$åßjõ¾IúhJWÖ£ˆæØÕ½î®Zšijh(éóZ“ɹ”c|q~‰²Ù|qB¿tšhnn g{»vtK£«T¦Á£Áu}’é,c“ ¦“i29·äÙiPØ )“s™N¦›LLgÉ»>Úäò(„B!„BˆrgCž'Á!)ÿÖ«÷MbÈó*. âÈiýX}]-GìãÊõ´·¶RS]]Òç÷|'!ŸË;n™ŒwŒ''Åó}ª"á’½NMu5ÛZ¹|õ÷íí¥¾¶VÝ“÷ýÌìê)ýþ8©L¶ *“†}×ßë€d:[ò×ÝÝÓçŸyRÙf½àø>ÆuË>øbVúÏ+|9TÈ /Ê© Zf.ŸgdlœñÉi”Rh­g3ãÄú ?H$SÔÆc$B!„Bl¨…&hóZ3îû4;e>}+™CRö fcA€;¿®©˜ÀŠˆðø‰×äy©,zÞÝ2v,áo´Ö$2¥Õ×ÕòÄÃI#Ûl Ì¥‹0>†ò~4þqԼ̮E+-Žpù¦ï"45£z÷‚ì“%JöžÍàû>ãSS ŒöÌ*ƒëÁV¦‹{—%Siâ55„Ã$B!„ë/ì͸׌(©È"7©{a¥¨•6"æ“àк˜Öš‘ À^ îÃ’A$–×g y×ßð;ÄÍ Ê«$“bK·[.^À¼~ ¥ua¬kÁÌ •SyWùX•ËÁð(  Ï|ˆzøØ»Oú€Xµ¼ë18<Âøä”d •Ù‡qc ÉtšÕ„C!éïB!„bM%’)~yê ^?ý.·†Fhnj ‰Ëå Gô66áyCsCÕUUdsYª"QZš›ÐZ“Ëçij¬§>^G&›Å²,Zš°,‹\>Oî.äŸ IDATMu5ÕUUx¾‡1Õ P¸žK4Áq‚ @C¨˜¢µ¾m/n™Z_U‹ìƒn+Å絬òœŒ–ì!)w…R@Þ†}Áà@TDb9r®‡WœˆÚ°±cYãŒÁÓ9×£*–¸U}tóÆ oºV!™C ŽñÆ ‚óÚ«¨ €ƒ‹—!V0þçó 319-•Q†´ÖxžO*!VSM$,×N1P5?0µUU–en\]è3 p5Ÿ§7¥¬òˆ$8$å®`>p}þ¾CsêÞ°x`w³‘Ñ:q=m6n9¡• Zkò Ú²ÌÀ¼ý6ÖBí§ƒC ¾AÌÛoA]=tvJ£Ë·†F˜šNJe”±¹A"…"ränI!„BQR¯¼yš?ýóÿŠçz·Ýk×-~î˜óÀ÷gÿŸH$f¿ŸœBññþ¨cÅ2††P–…).…­, …"ЖeaYZ–Êvì¾½:ðQJ¡l£5–RºØBk¢‘Ù\K)b±Zk\Ï#V]Euu5ÙLÛ±i¬¯Ç¶mRéõ ÔÕÆÉæs(ÛZš9!²¹µ± õu¸®‡e)Zšq§5UUESc#y×EkMC]-UUQÒé ¡Csc#¾ï‰DʳX!¥ðï2ùߗϳ#¡,¦¤%8$å®Pª8Žöß%8à(%DbyÒ™<^lÈÄúJ^Òƒ¤³yêã5r· sêU,­ïÌzÛ"Á¡™~ ´FŸzëË¿%B,ÛðØ8S‰¤ì7´ ̉’©4ñx áìI$„B!J#Кÿë«ÿßÛà­Œ¹í3ôí*sGpjö½r çüß÷½yXø]0ïÿéL¦ðx`júãr¹jÎòÛãã Àh͵X¶U(1XÅ@•ÖJY(ÛmÐ:À²m”e¡}”*dᘙ³5Æ€1TUEq]®ö6~ó‹Ÿå™Ç)Ëš‡Áù“Òs¸Æp9—cg$Bh#Ë/Á!)wsá†ç¡ïR÷hªì!KNûúO&7Mphîƒ'’)9y[¹r+—›½shK×…ÖX¹\¹, C,K6›cpx”`îí|¢¬i­ñ|ŸD2ëzR!B!„¢$¾þ7ß%ðÙtîgíùYT³ó…ÿ›Â÷AP¬ixÞìÿßÇ/f é À÷<ßÃ÷¼â÷…¥íR©4®ërµïôÕ¯ñgßüVù}N3†zÛ¾çr¸áºxÕ–$8$å®P ðŒaÀ÷ï°:Û¦RΊˆÖA 5cS‰õ;JðcS  l-¹æÃÁ+ÿÉÑuˆ=ýá‡ËIûKvkhX–)Û„´ÖA@"•Âõ$H$„B!Vïý3ç$8T&”R|ïùŸ1>9U–å‹Ù÷ÞeÈ5†ë®Kv½Û”‡¤Ü,k ýž‡;·¾©û˜UY! ­ƒL6Ït2½¾cG‰žg2™&ÍËIÜBÌ­ÔäÄ¢o^u™Lx¯Ååq±c3Æ &'àÖ€4±´7¹SÉ”dmRAšD2…çùR!B!„bÅ<Ï'Q\jMl¼™Ì£çñRY–¯Éqî9ß¡€À®çóä×+x Á!)wË—• Šýënuo€ú%r7 ­ƒþá1\ý& K9tx~ÀÍá19‰[…ïùswÍz¨w³ÔøîÆ^×à9k|—/»ø0¥0»m³O!39}‡6¹™ ÑT"IÞu¥B„B!ÄŠX–UX2Mª¢lh­yÿìGeY¶ˆRK Aa"ûJ>Ïx|¼ñÒZà¨Ôñ˜ ®Ïßûë.Á¡Û¦ªÂV‹‘ÑKçó\êZ¿1o žóbÿ)YZkk軆¹ëÞCÛ³ žígWzÇ ë;(š’>—Â1†]éIžíg{fñ¥ Ö02‚¹~UÚ‰¸§éDR*¡A€çy$“i  !„Bˆ±m‹†ú:Y~ºŒ(¥p=¿LW|hrœ%¶ õ<úòùµ BJpHÊ^¡ pÝó ‚%H¥h´m*ív` ­±w?º†¬ÏF„kñ Ƽ à½ó×ådV:­Ño¿uÏ»N ŠZ/ÏñÉaë§ÞË­[¨ÔÁ¡z/Ç£cýÜ?9L—¿çq(¥0§ß-ˆ‰»u%M>/Á„Jâù>©TF‚DB!„bE¶57£Q9i¬¯Ã)Óe¢l Y Ë™ÉhÍu×-í’s Ø8R÷kjfI¹ûËÝ––³,ì ¬ ­¡Ë7‡¸5>¹é÷ ‚€ñI.ß’“ZÉמÎae²wÍš;(ÚÆÐ–KñÜÈuö'ÆpÖx9­R^­ÙŸã#×éÈ¥°YÒó­±Ò̹sÒ`Ä¢ryW6 ­¸÷憼ë’L¦q]O*D!„B,Kg[ëÚ.&–Ų,N?ZÖelvb–µä¹ä´æz>ψﯾ½UÂgZÙwHÌïûJ1ô{î2ƒC5JÑdÛ•Y/Ò4ÖÆÐøg¯ÜXÒd{IÆŽµ~~­9såCãSrr+Éf1W./{RÛ iÍÑéžï§=—Z“öhJøí¹ßäØô!­——ýd ÆÌ•+˜lVŽX¤™hY_¼B¹žG"%ËÍ !„Bˆåéîê”J(–mÓØPÏç?ñLÙ—µ#ZòRsP`Ü÷¹šÏãoå@ƒ‡Ä<1\Íç™ZlO¸»Ô½´-qo°M9.Jó(½Á±IN}x×_ûÌ!ëÖ8]?àÔ‡¸5:!'¹Â¨›7Qcc+nƒ­¹  ðÐÄ U)ÑÝQ¥hßZ)¢:àÁ‰A -—^þsϹP¨±QÔÀMi8BlA®ë’L¥q=É$B!„KÓѶMV(J)jkù_ÿéïoŠòÚJÑ -{¿䵿j>Ϙï/^Eö’rWJŸ§0÷7\÷}ÜÔ½6†VÇÁ®à,P •6†k£¼öþy\ÏGëÊÚ²JkMÞóyíà \AËÀUç4“Lâ½÷ÊZÝp`€ÑìJOñÜðu¶g«^a¹-LÛ3 >1|=é)ÂZ¯úÍ‘²,¼wß!“LV\?BÜ›ëz$)<Ï—ÊB!„÷ÔÔPO8äHEl…eÙ„Âêjkùÿô÷9zèÀ¦)}̲h …VpÔ ±b6Qv©s’rWLχ¬1\s]ƃmYCèußê8Ä­Ê¡ÈÕ©}›JÐwk„©T†`:õF 6\bb:ÁŽŽVZêk¥lºëŽáòõ>^|ã-nÜàË-õ%»›É±Àåáñú«k¹ob"Yv4zµ¥Ñ@£—gorœ™–Ö+ËjZ ^ŒÖ„’I¾ÿ§ÿ‘‰í=<}ò½»zP²¦´[†6†édŠºÚ!GÞN !„BˆÅÅkjˆF"äóy©Œµ¦–eaY6 h¬¯cßž]<~â~à8‘pxÓR£m“Óšé XѸ^q’¼É¶itB‹Í]HpHÊ] CàÔÖLø>ÖÝæêîR÷ˆ[ ¶]ñÛÈŒÆ*LNséÆ-F&h»;[™Neðƒµ]^n#¦mY4Õ×re`˜þ‘qZjÙÛÝAkC4ˆMàòµ>¾÷ãxÿÜy‚l†ÿБ’_Œ Ëv¤§hÉe¸«ãlm ¦8P¯Gû><=ÊÎÌ41ß+,ÅX¢àÐÜß=[[þð¼ôÊ)Ž:À—>÷)zwîF&Äàû>ŽãH¦¨ÅÉ¡B!„bŽãÕ0HHe”˜R 5' ÔÔPÏþ=;9qüGî§¹©±"޳#BÓ+œo´(,³•Ðz6+é¶Y I¹7ûX@áfñaß'­5>¬88PkY´m‘›Ae6cÙýÖÊæèçòÀ ® µÆƒRа¢µ¡–щ5Ë&0|ü­ µ„‡ ða`t’Áñi"a›Ýítok"V•lŠ2¢µæÊõ>¾ý£Ÿpêíw c 1üÚövjC¡5kSEuàqhzŒölŠSM]¤Ðš¶ï*ßçщ›4糫{¾{\( P ñ‰ŽV¾{ã/¾þ&¿|ãM{ð~ã ŸeÏŽn,KVñ¢’ù¾1v!H/]è+ÿä¤r…B!*LSC=ƒC²Ñ*©b†R6–‚¦ÆzöïÙ͉ãG9rh?Í {ìí¡9cȯp©{Æ0Lí¡µ¶’à”{“3@Bk†}ÿ¶ö¾Òº)µe‚C ¢%ѺŸN00:Áèdƒ"nßgÈÃèÔ4‡÷t3‘H‘Í{”:F²±Á!¨Š„9´{;^¾1û¦ÆCäò†ó}ƒœï»EkCœ®–&šêâĪ¢X–‹6B&›ãì…‹¼têM^?ý.yÏ›=oõ¡϶µ°^§¦)ŸåÉÑ~Þnlc$Z³à³ª¾¡hÍg8>5Dc>»²Œ¡e^¤-Ÿhk惣LyÆÀ«oæ­÷?䑎óô£'8´o/ÕUUÒ·Ö'©ƒ-$Œ1$R… ‘cÛrƒ„B!„¸CǶV>øè‚L /ûãUa¡™ÀPsc=û{wsâø¨ì€ÐBv„Ãô».i­W½ïó€ç1áûÔÛ6u¶½97ª—àÐÖ€HÓZ“_l¡eÔ½ª”¢kû~mf ºk{1Üc`d‚ñD‚LÎŶ´½ãcxbšc–Íñý»xíýó@é&‰Êaè8¶w'Že3<1}Çï´Öhí¢”bh|š‘ ª£aškkéhm¤§­Y&ÍÖÑ+ožæù_ü’ó—®àù·3}cx ±ž±êu»{É(E­ŸãÄÄ-Þhì`4R}[ï0«ìÍn†“Ä|w]‚C³oÎb5<ØTÏó·Fp,…1†|>ÏK§ÞäÔÛïp`ïn>óÌSͶM̶ —s½Ëçî­7V®1¤´f< dYoˆ[ŽƒÞ‚u+3ô@.ŸgxtŒó—¯òÊ›oóÑå+<ðÀ z{÷.;»Â²,FÆ™èîàðîn¦’i&©U]Ì6zÈÓÆÐX+,—Lssd|Ù{«cÐÚðÒ›ïðÎ[¯³o/Oœ|ý{v³­¥™h$" q&&§øà£óüøç¿äܥ˅»’i»Zk~¥«ƒmÑ膭}2OŽösº¡›UñUõÎ\’'†™`C‚CÆZ£Q¾´½¯_íÇ^ ¯cá¾ú5ííåsÏ=Å‘ƒhl¨—Æ+D1ÆàzÉtšxMMÉö$B!„›_G[+HþМ€Â¶,š›9¸·—Çshÿ>êêd›‚%ê …¨¶,†ºÖÏcGpò¾^^:}–L>¿¢ ÑF3û=|¨—êh„w.\Åóý‹6…zÍæ]Þ;sŽ÷Μ£¡®–Þ];xðÈž{òQ"á°Œ|ËÉdùáÏ~Á+o½ÅÕ¾›(¥0ÌbCš14†C|ª½uC7Æ4(ªŸ'Qúªk±–Y­Û3 š"lLpèã‡>ÕÞÂn1éz‹þ (Î^ºÌÙ‹—ØÕ³'N>Èç?ù,5UÕÒ˜…¨Zk<Ï'‘JQáH¶¬B!„êë‡Bä‚ü–:JáØ6-M ìÝÉûq wd­¶]Ù6¶R ºîÒæ—0ÿ¡€ÀRÆt]"JÑUJ’àVC‚CË«. W XºsöR%¬{´9qËÚÒu½åf-<Ïã£KWøéK¯ðÊ›oß±„ÜLƒÉd2ø¾¿ìL™™‹ßàø$·F'éhià‰ûñò»gÉæÝew„V óäñCÔÇk¸56ÉàøäŠ/Üï“Éf0ÆÌÖùøäã§ßãwÞçkùMž<ùŸxê1öî!$Ëp-jlb’Ÿ¾ô û÷?!—ÏÏÖç]ƒ>ÅÁô³Ûh o|Æ–"AÀcc7¡¹“þªÚe=¾;3Íãc˜Õ®á\¢ tS8Âg:·ñW×nr·M.üöÊõ\íë篿ÿ#~ã ŸáÓO=AÓ_KYˆJJ)¦)êjeO"!„BUÑ(ÕUUär• RJ¡ŠsiŽcÓÚÔÌ¡}{xèèî륡®vEsmbqµ–E$aÐóÈhM)kwfY¯kù<¥ˆÛ61Ë¢z£Î¡ì;T¹c…¹ÂŒÖ¤!­5yc°Xå꜋l¿UŠmŽCTÉÞp[bÆ"›Ë142ƹ‹—øù+§¸Ò×G  F/ž8–I§ð<È —> Í¥þ[´4Äiˆ×pÿ¾Ýœ>…ì3‰6>sÈP‰ðÀ¾Ý4Äkð|Ÿ‹7n+O¶óß®µˆRì‡ö}&ýEöO_E¢á0ûöìæÄñÜ8~”öÖÖ-ÙQ¯öÝà…_æÔéw™œNÜ–…µÔÁ0<ÛÝNw¬æ®ÁÑR¼9ĶHçò¦”ºWI£9>9LHûœ«mÆ^䨥8˜˜à¾é1BÅ ×½.2Sž‡£,bÑ0z6XVê ´zb5<ÛÞÂ7¯Ý$jÛK~¬4ƒ##üßú÷“Ÿñð‰øÌÓO°»§[®RBlbžWXr2™JS‹ÉžDB!„[ܶæf®Ý¸¹¹'Œ•ÂR(9m-ÍèÝ͉ãÇ8л›zÉÚØ6æ8D”b|Þ²\¥ls†Ât#žÇ°çQgÛ…,&Ë"¼VËJp¨bÌd ¹ÆàCRkZ£ ´KΫ{„”¢Ñ¶©·, ÍQq3©L†‹—¯ñ½ç_àì… x¾Fßk2|ΚÏç˜NLQßаâ½Z”Rœ»vƒXu„žöVÚšêyúüöáy©Ì¢ƒìF«‹×ðèáýÔÇ û¢Üåܵ« )¥H$¦ÈçrKìÃíÒ¾Ï;|È{gÎòÿ~óÛ>°/}æSìëÝI¬º¦¢;§1†·øËo·Þû€@Ë¿® ¡'VÃoötÁ‡fÖ¨½4â‡7‡xs|‚/÷tòåžÎ{ð†BèØÔ!c8[Û|G_PÀÑéQM-¹¯~Ð?Èßõr¢¹‘/nocW,FØZ£ôQ­ù­ž.^d0“ÅZbŸ™»DàØä$ÿÂ?𓟽ÈÇð{¿ùktwu ”¼Áb³Êç]&Em¼F–OB!„ØÂº;Ûyã÷6×ĤR…϶J9´67qpo/ߌý{vSa/ãI±öê‹ËÀú>¾ÏZ™ ýD¥+Eܲ¨µmœR‹$8´éÍ´H)­ñ¿XGÖ×}PìÍ–EH–”»CEˆ²¹<#ccœzû]^}ë4×úob)uïÀÐ]xžÇØè(==;WU6m ^¹A]¬†úx u±jž8zwÎ_ahbê¶àÓF7N¥mõ<°7±êÂ’nSÉ4ï_îC—`PÅó¼½qÐZ“w]Þùð,o¿†]=Ûyüă<úÐýlki&‰TL§Ìåóôßä'¿x‰Ÿ¿z ßVÖ–çœ3K)>ÙÞJ]8´â€çBƒ;(\0‘÷øh:É? Žpv:AÖ×8–âƒÉi¾ØÕ¶ŒlÅÁÄ8R\ˆ5¢‹o&,cØ—šà`b|YeÌøoO‘ 4?áÕÑ1ÔÖò\[3‡êëhŠ„[6`JÒÿ Pñ©öV¾~õÆO“Yðg^àóæ;ïñî‡òìãò™gž¦»³½¢Úº[‰ëy$Riâ±YnN!„b‹êéêD)U²ÏåkŲ,PŠH8DkS÷íßˉãGÙ·guñ¸dmŽR´‡BÄ-‹AÏ#XÃ67 Œ!k ­ò}¢–EƒmSWœZQ«‘àЦfŠ_I­™ òŬ65¯í¬eÝÛ@»ã/f É™Y`¼ØìðÚÛïðó—_ãƒóÈåÜÂòqÆ,- q—¿±,‹ëׯóàC'W}áNe²¼qö"9@¼:J¬:ÊãÇr©ÿg®ÜÀõ¼ÙõZ7‚ÖšpÈáðîz·w`YªXîoœ½H*“]Uö‚Oׯ_[Ò›ˆ»Õ÷L äÚ~®÷ßä;?zž£÷óÜãòèClúùê[§ùÅ+§x÷Ì9<ß/´ã•´¿yi‹Fx®½¹$oB•R`Y¸žÇ›cS¼>:Á™éÃÙBv˜¥N± ]L¤Húþ²–[SÆpxj”¨ïónÃ6ŽNЛš\Ö…C™,W“iÀàX 70¼31Å;S´UE9TWËÉæN4×S6«®#c Ÿhoæ§·†ºÇæ£÷z­B`Ôðü/^æç/ŸâþÇxö‰Gyüărõ*gk•R/6÷ccp]T*CÝÑJw±¬æÂщðL[ ߸~sÑ7^ËÙCÊCÞu9uú]Þ~ÿC¾Ùõwüίÿ ;ŠãÈ$³›çsB¡/›”‘L"!„Bˆ-¨­¥˲V5ÇTJ¶bß®öìÜÁÉû ¡XM„*ãÃÇÇçhq‡›®K¦¸ßËz˜iI9­ÉSA€¢07ScYÔÙ6Õ µ7 m 3¡´1LkM&ÐÜ>§Vò½…îQ÷¨RŠŽPhvyE Ýݦ›YœN$xþ/ñíþ˜\Þ]ùÝþKxœ1šk×®ÑÚÖV’“Ìf9õáENÞ·—Úâ$t¼¦š§8ȵ>º~“L.¿ÆµË¢:áÀŽNvvl»-p“LgyýÌÅBp¨D¯yíêUŒÖk–)ø>¯^ãû7ÄW¾øy>óÜSÔ×Ö–ñõÂÊd8{áßûñO9sába“¶Õ^HæoÀf 1âí-+JcŸÙWhÊó¸”HñâÐoO‘ô<˺gûPÀ+Ãc|¶sÛŠî5¾ÇÅ IDATØ•š*Ç2ƒ” ˜v]>˜œœ}ÝÅÝ׆±|žoõÝäÇ·†¹¿±ŽgÛZØW£>Â^Á]J)žkoáù[ÃLä½’&”x¾Ïµ¾~þõ•CûöñŸû4‡öï%VS½ê`®7„b}Æÿ|Þ 6“L"!„Bˆ-$‰„ñ×àFç•pl‹ÿã_ü3êjãrr¶ÀgRè‰DHú>ãA@vE·ÂÞ3SAÀd1`³,¢–EµeáPX"on`Álâzßìæ¶‘ xîüâr‚3K Îd¦© ¬{D•¢Ñ¶©µ,´ŒK¿l¦Â~øÑþü[ËÅ«×fïª_ËkYý7ú8|äUUÕ%éPãÓ ^}ïG÷í¤£¹±øsÅ®Îmt¶624>EßÐ(Cc“¸¾]Ló]}?1ZvÚ›énk¦­©žÈ¼»—oMðÞÅkL§2%›lN¥Sôßè»ç(¥XÚ+Ÿwù‹ï|×ß{Ÿÿþ+_æÈÁýe׎§¦¼ôÆ[üòÔë\¼r ”5;•úBäjÍ—{:i¯©Æ,ñ%UÜ|2Þ›˜æôħÇ'¹•Éქ¡¥,X|®sÓIúRivÅcË>Çf¥mвxw|Šþt–eݳË+ dYdƒ€—†Çymt‚Žê(56ð`S=Çë°{ÉKÐc訩æË=|õüU"¶U²v>÷9Î]¼ÄÙóØ»g7O?z’§9I}]ùFÅÒúŸm[ù¶jÑíß%»Ñ¶í5 P*žçWä¹Îç]¤¨‹ÇqÙÔW!„b+°”E¬ºšt:Såq=¡ÑQ U’{Í9Cܶ©±m²Z3äyäa#rÆæÒZ“Òe ¶RØ@Ê"JQ¥Ë"4ûé´ Fš{^< Ù_3 hcŠç@±Á¡by"JÑbÛTYHph™6M€è¯žâk_ÿ+RéôºvØl6Ë­öôî-Ùþ-‰L!“èÀŽNöïèÂ*NªEB!zÚZèli$•Ëskdœëƒ£¤³y¼À/t¸eLÀ™bÐ!d;Ä«#t·µÐÑÚD<-N@ÞÞ™.ö pöÚM<ß/ÙDŸRŠ[7Èf³÷,kiÆãž=.]á_ÿñ¿ãüÝÿ†çžx¬,Úp6—ã§/½Ê÷üã“SøAq²Õ¬Í°e€]±jžik%‡fÎøh.Ï룓üàæ-¦ò¹bzèL ðR_{æ9óAÀ? ޲«.Á:]0•â'Ã…ìŸe¼¤‚Ùý“293ƒ¼08BC$Äç:Úxb[-‘ðmǸøJó\[ ?¾9D6·&Ë™e .\¾Â•k×ùÞó/ðëŸýŸ|ò ªª¢r…ÛŒeÇæ`ïÂáòZzÌó}Þ;óÑ‚¿³m‹Ý=Û×ìCåï|0»7^¥ÑÚH¦¨Ç¤ñ !„Bl õ Ž•Ga ܼ5ȾݻäÄT‚eL€X@e±;eÊ÷ó}ü r¨âëÅDNkÒ7UBPÈ2RŠhq_™rPlúf3çË/î”-þëÍ©Ûùu\un­ŽCƒd ­JÙˆ´ÖüÕ÷È7¾óXíÑ :c ýý7رc'v‰öú0&ÛÞ¿tÉdšûvuS[S5”ql›úšjêwVspçvƧ“LL'N2Êàzº˜A¥õÇÇdY ¥–R„C!êbÕ´ÔÅi¬‹ÓT_ôø’™g¯ÞàÚàÈl°ªT<Ï£¿¿ï® ³F t&Ëÿýÿ3C#£ü£_û• [C7К³.ñŸ¾þMún &õKyÌ‹¥+ÅgºÚˆ‡œEƒŠBT=ãûô¥2ülp”7Æ&Ë»„æ,!§VÑÚmËâÔØÿ×Ch&yÇ2Y®§Ó¨U”\ûj6ÈdþôâU¾Õw“‡›ùDG =5ÕÔ8 ‹ 9|¦«ÿr¹o¶Ï®?çkù×<ÿ‹—øƒßýÇÜ·oïÁ`QþtÞyd´ÙteÞ <σPˆD2%•!„B±EìÞÑÍG/—Çû|£é¿5$'¥¬b•¥zۦζISA@º¸ôœ*‡ò®HIŠÛg3¥¿³ óbwf™r©û5¤æ}?S1ø@^k¼b Î-.7³|ÜÜs¯Ö²ÞVZÝ@•eQoYÄ%c¨$Ê:@¤µá?ã[üð§?/ÎÖš 鬃·n119Ikkëê—A›ÛA•¢oh”ÑÉivu¶±»s5 ÜõßT ðôR˜„s=Ÿ¼ëáž`0(!ÇÆ±m"áásÏ`O:›ãÊÀ0W†ÈæÝ’‡”RLNN0xëÖ"§dm‡–Âó+¾ñÝ¿#™Îð?üÎo¯{(™Êð=Ïžÿžï•þ˜ïò|Í‘0O´6-|‘(nŠy!‘âͱ >˜Hp9™"§5^E=™Û¾SÅý€<>šNp¤¡n]êýÜT‚Œ_ºËƒ*ÖÉ”ëñ£[Ãü|h”]ñŽ7Öq¢¹‘}µ±BÛ2úŽSòdk?èd8›[—ˤ®÷ð¿ÿÑÿï}öS|ù Ÿ%«‘«eÊó<Œ#û !„BTÚ{¼±‰I¦I&¦§Éç]FÆÇ¸5Äù+WV´GðZ¹54,'l³+ÅŠG@mSkÛ¸Æ0èy¶GÑRʪ(Ì‘jÀ3†ì¼ßÏU]ÜßÈVŠ…-æ~3\÷¥ª“~±NòZ2±ŠËÃÍ-ëb³r¾Åxf¡öPˆR³gr»êê•õŒÄù«¿áÏÿ”Uïð¾ÊÎêº.çΡ­í“¿ª†<Ÿ¥9×ã£ëýô °½µ…}=T—±Zèï£áÑU,?”Í»\è»EÿÈ(™\cX“½#,ÛæÜÙ3¸®»aÙ;PȲúþó/`[¿ÿ;¿½n¯<5àÿüê࣋Wf—[/1<ÛÞBs42û¦S(EÆóymt„GéOgHù>Ú—W[e;˜šù>|0™à¾úÚ’"çÓÆpf*·un)…Eᎊ ‰—’)~<0Ìöšj>ÕÞÂÃÍÄáÛö”jŽFx®­™¯_í_ò}«ohp=oÿðÇœ¹p‰õÏþúº:„å©\6)B!„‹s=l®pã_6“c`hˆ›ƒCÜ!“Í’J§Ÿ`b:Át"A 5–²f? «J]^÷ºËɳQŠá09c˜ð}2Ål“5›Ñ(qeþ³¥ŠûÝ1†Hq®2Zü7Ba~4T\ºÎâãùÒ¹»ÆZÅߣõìœõjv• (ÿÜU:‚y?7@ÆòÅßåŠãJž;³‡àãåûîUWåÆPÈ «RŠÇ¡Z©B¶¬bRRe úîžçûÏ¿°áÁ!˲¸~å Ç¥uMîò0ÒÙ<ç®÷se`ˆí­lßÖD}¬DZW5©nŒÁó¦Riú‡Ç¹>8BÞóÖt¢^)ÅÈÐ×®\Á²íË´Þ—¸¿ýñOhl¨ã7>ÿÙ5µd:Í¿ý“?åÜå+k7h-ò¼Ú:ªªøÕí³ƒ©«5™,/óâðƒÙ¶b¶ ”¢%Üí(5ðÞ俵£“ðI¼@s>‘º-DµVošŒ„çóáä4ïMLÿÿìwœÕÕ÷wfÔ·÷]wãŠmÀ`Lï%ô@J„7öQw‘±Èº—€1‚˘u5–èï³f•þnLDŒÙ•As eܾ㓘„Õ,±(aÿ«–$‹<ÐNâPfP¥@ôñæ­øÇËÿ™~§Ña˜ `ûÖ-8ý̳!%™ &™VŒÁ`Gv÷¡Ôh@uE*JŒ(+1¨ÓB«‘ ¥˜µEdYA@ÂÂíóÃîtÃætÃl³Ãáò"(Ë`c„L°uËf°‘C9 £æÀÓ/½‚ƺ:œxì1ûšÁ!3øËØw°-ëâȸzþ,”j5h³;±ÃbÖa+öÚpƒ`@ÚkM ReQ[öÚöùÑdÐgì"Ä8Ljßý6GÖ¢uFÇ­À€A¯ÏvtãÕž~^^‚UÕ8¢²óËJð¥ù³pÏÎýYˆFÇÛþÖ6üòÿÄ­7|õµ5tå#‚ ‚ ¢àñx½ðùüðù}p8]°ØìèêéEß N§#6œn7‡Ìp8Ýá´oáÅoAƒ¢È‰eÉãõJ§ËŸÏÝ$™l“¥õ5‰1”Š"JÃéç‚Axä4ÈŸ*[ègq†up¬ð;VŠ÷&‡bmOp_oß$l¬ÖS–€PDW$A?-¤bŸ)T'Y­v<òÄÓpM·^Gš†1†tuv`ÞüÃoFŠßÅX(ÄêrÃêtCQ€F¡Óh ‘Dˆ¢I#KñAY DA¾@Y†"+ÃO³0‚ÅéÎÎ ôOH]—ë»n·ýíi,]´•H¹åñzñÀ_žÄÖ{r68¬ÔÆ8~¾}/8œ°ú(CFD“ÉÒÊE_¾8Š‚µýf\sØìPn&ì"X?8™ó¬ D‘ Yø;=²ŒOÌVl±¡BÛ‹…¥%8©¾‡•– ÓåÎz»Eƶ]{ðÀcÃ÷oý/ ºúÀ$]Íi‚ ‚ ‚Hÿ4›YS`ÆÀÃ+,V+œ.7ú‡Ð70‹Í—Û™+dpJ/ÅŠ,‡Ö*‹¯<ù3fÝB)ž’ç^ŸÃ šêÉÉè¾sJ´Œa–Fƒ€$Á£(p( œ² q¢h é¾y\äPR¿/'SûB=âÐhô”„P¨A€A  C$eU DÁ`üõ ˜­¶é¹k†FQdìÙ³MMÐëS¶1­`áÿ(œC Êex|þбùøoa!(‘óDÆ&^/öîÞE–£Ã&U19Ì þðø“øÁÍ7% 6/þç lݵ;§€v§÷îl†ÈXD°È”.È“ØKbÞ0ãÒY (‘¤ô^¹pƒ²ŒÍ9‡Æ·G`¡§J†}~ y‡±>Ü.!‡mÛ¼c^xõ \{ÅetõS)Š¢`ÄjMû¹iÚ×gYŽ{£ns8ÊiŸj L#õ‰ ‚ ¢¨Ùwà Þ~ÿCtv÷ÂátaÄfƒÛã,‡kŠðCu@ÀyB?œsZlsÒ?8DQ>¡ßÕ0(¢LEÁp0¢L-‘8”›}¡.qh4…\ɸìSJ!ùLž ª¨5ï­Çžý-ã’S©é$É`6aǧŸâ„N‚¬ÈqmIÔTƒ(ŠØ¾m ÌfsTôWÑ@fvïÙ7ß] Ï>#mÇÝÝÜ‚×ÞYž HІB–Dˆ©ÒÊE¿¥`ëq»±ÏæÄªšÊôÕ1è6‡ m.wNE˜X¾.äX:4o`xõ­wpôŠåX¶d!]Uˆ,+èêíWeÛØ$>¬(Cæ¬/AAQØ(Š‚§_ú7þþ¿Ơ„ž9-¦}­¡»¯G±œl‘'ý¥FJ¥Z-‚œÃ¡(pÈ2œÃ?¾V‰C¹Ù¹‡F¿WË4Œ¡$-¤ ×uâyêó…„ –† [¬Xûá0Æ ‰¢jO’°ß^´µµNºhU¬nËC[ëA4ï߯Zq@(-ŸÀðÎú0l±¤å˜N—O<÷!3‹™*³áÄ´rñÅ¡±¾ðvßPúBšÆÚ…1l¶Â'+9“MÕ~£J_)à¯Ï>§Û BÍý¤¾¹j3AAQœÜõÛßãé—^…N ‡¨sD:éîë##äyàÿc¨EÌÖj1G«Å\­Õ’¡ìH<ŸíNâPâ&C(HP-Š˜­Ñ`–Fƒ™ *ER8‰Cê@5Ѻ?F_ÿ@èd"IÉ/ðfÙa6² 6«5çNMX­V|òɦqÝ¢¾‚s£©›zðÎúÓrܵ|„žÞ<ý‘"ª‡âíÅ¢¶1& Ùá€ÕãK»]¸¢à£¡‘œ¥—ˇ•QßìîëÇ;ï}HW@‚ ‚ ‚ TÉ›ëÖcÓöƒ2Fîe††Éê屮k²Ä ‚€:IÂ"½ t:ÔJ´‚‘±üªe›iq(ýžcqˆJ?X-Š˜¯ÕbQ¸Ï ‚)l^À>Ÿ¯¨B rº\xííu‘'…%QLî©á8ŒÓéÀ¶m[Šþ gƶmݧÃ5ÉPc[G#Ô^]óœ.×´Žép¹ðÑæ­‘ß+¦—'Cóò26 ”»4ŒÝ'ôÚê ÙáL»]ºÝŒø|¹±Kž\´F}“sŽ>Ù‡Ó‚ ‚ ‚ 5 ñúÚw‹û Ü,c±ÔL¾/”‡k~iC$a~8²h¦V‹ZIBi8¢Dæ<±¨’l·=âP¬ýÓ°o&l9º (s)œ2®F’ФÑ`¶V‹ùá~Õ&%Tˆ>Ÿg¨B z}í{*Jd‘U„DÙsä0Œ1t´·ã“M!89çÕ?A”°iãt´·EÄ2µ.˜ ¢!\üŒsYáøÏ[ïN똟îÚƒ®žÞÈk)]Q¤•‹Ž4âðÊ2vYì©;œ¢ÄÜ~Ðî‚3(ç`®?­±¾ÙÕÛ‹m;vÓU03²AAA¤ˆËíAK{E&cd ‹ÍY&{«’B‡b a &A@µ$a†Fƒ:éõhÐhP"‰Õ¦QƒíÓ ”ÌBûfJ2 j% u:Ì×jѤѠJah­+D¨Šœ D#+Öoø$”Ovì‰"‘EöŸ$c8ÐÒŒO6} ¹H•MÎ96müZšU/Åò+E‘ñþ†©?-Ãw>ø8":€( ÓO3—7‘C±öš\}ÿýsjc&Îg¶[¬ðeyB›OâÀXTt›À¼óá‡tÕ&‚ ‚ BU´uvQ­¡,ãöx`§ DŽÑ0†*QÄ,­KõzÌÑhP'I(EÚpZ:d^„H´îP,Ò!ød*e]¼Ÿ<æ e FA@¹(¢^’0[«ÅR³5T‡£¾2j{"kH¹nÀÞ`±Ù&\ü%IüqF»Jœ…1†}ûö¢¬¼ó[PdîÃÐÖzû÷îÉ qŒEêr#‹ÍŽ}-qòq«’>ä yZÛ¡p%Ê'DQ„ ĉp¢8+r˜X“(V"ŽN·.,.+MÜgâìä;,ö¬¦{Ì·q\êNEQÐÜÚŽAó0êj«éjX“A`éM™&dY¢(“^c%)ým˜AÉW ‚ ‚ TˆV«Wh0›x}>˜‡GPY^FÆ ûJÕü]¸nMeôçà\Š‚çð( <œGòÜDî%ÓÑîL§–Ë 8Ïò±ŠFÂõ¢taQˆ!ô ±€PtÉègòû‚DÊíy‚cçžý1†A€$JÆZdW‘³p@›6~ÀeËŽ€, ßq$ ;?ÝŽíŸnÔaRû‚¹4&½ÜXEÁŽ={qÒêc’Þ߸ L`€<ñ»üÉÖ§R¡ý‡ý[b ÿîêÇÿ[^6í]ƺ]´:Ý0JbÖÎ]ù5'ã1S LÀº?ÆU—\DWÇ1†“ ófÏTW»ÀÐÝ×?i§( X¾x!”49ÀãõÁér“sAA¨Œºê*h´|^Jy–Íû…Þ,œ?—ŒA¤‡iÞÃ1€^gÓEꋇðsçð* ‚¼Šr(Ê(,*ñdÚ] âPD8 ‹=`H pFÆ }è?Æç,&ž!q(gäT ²;h>Ø:éûZIB0ˆ*‡F/¤Á`[>Ù„@ €eËVÄ" EQ°mÛ|ºm+8çy!sh¥ÉÝ}ÿÁ6ØN”—–&|HYQ°·å`LS£‘ož_Ë'z:bGÿ;väÐØÏJ‚€;ÜÔëuñ/6Sùcx±³eÉ•òó‚¥ÑhbŽå}-­bŸ³ˆC×*m ?È5Sùž$efªâÉ)‚ ‚ THYY)L#|^#‹ }ƒdÕô§öOí²Qÿ/1†2ÆP6æþRæ2BQ/AÎ!sŽ`x»?,ù9çpRøæ9f‹÷û2$øðûò1ûrΡa,t¿?*ž1‘1ˆa»ˆaqH ¿/¾)äóEON¢®î^XlöIßEL-ȪP»xι‚ŸnƒÀ-^ ‹. gŒÁï÷£yÿ^|º}[ä·åÃod‚7µÒˆÅŠÎî^¬Xº8ácÚlvX,Ö˜¿?”fN€¢L?}šZü;ùÈ¡X¯Cû3pŒø}ØaµãœÆºÉÿvaì>?öÙ\‘§H2{½ÊÏñ<>½ÜØß32b…ÍfGUe] ‚ ‚ ‚È9Ze¥±XÈÙZà}ƒCdutµ?Õ¯÷Z5£K "(Žcp+ € B"%ôþøÏf@âՒ‚—–1HáíÆ°È£e,é:@ ù ƒœ D;:ŒST^DADPªXŠN£Å[·l†yxÇ­>£± D"Ʋ¼Z0Ÿª0;°¯å`RÇì2#LþyX=¹´rã?™Hš¹h8\0ÇJ„o}lI{}’‰ÍÉï~¼^`f3MÄ ‚ ‚ ‚P u5ÕI×&¦uÓ‹áI2¤ÙëjîÛÍÇýSÆý“âŽ$’‘=þ1ƒ À8æßhíŸXûJá´vc?þ»Ç·|žÚŸ.r*hkòâ#M’)'ý>átÁÆ Ëèè«Õ‚µo¯ÁÞ½»á÷ûónr3šRnïÞÝxç­5°Z-cÆÏ«ß1U Î9$ XŽÝÐ<7N…ø}ž·iåb‰AãÄxñùÐâp"Õ¡á ÈØcudø|Ÿß'üPºÃÉÅJYV084L7„jý— ‚ ‚(>šêš f«ÝŸê>)P âA¶/Vr&9œ.øƒrÜEIÎ ªX š˜Vnê´[‚ ÀëõbË'›ðÞºµèëí$Iª_ðTz{{ðîºw°yÓFx½žHж|[HfŒMYsŸŒ›2n, çèíˆÛ—BAŠC ÑâO¬m,ƱãoŸÂ±Ál8;d—mÃt¹=èóz3x½*PÑ)Î9=ýýÂ"‚ ‚ ‚H”™ õd„,ãöxas8ÉDr8D›í“X»ÌrVƒ¨¹­ Š¢Lé+B¸vL¼}3ÞïSî«&Kèõ¨€00ЋáuC˜={.Ž\¹%%¥ª\|fŒÁét`û¶­èìhG0ŒlõKþ €ÑúCSµ]Q´lÇ1G.Ÿò˜²,£· îù@Œ…ò‘ªøD˜zZ¹XGb ŽšÐû>Á·¡ÅäBdÃÖa+\9#–’8¯þç@oßdYžRH%÷MÎ`”eØUvÃǃ?NªN…s8œ®´GÆ ù*AA„Jil¨ÝO“)²†,+F}m ƒHð6“D‚|¦È™@t µ\áSú çIŠY÷Û M94Ùë`0ˆ––}èÃÂ…‹1wÞ|M&ˆ¢˜ÓÅhÆdY†ÛåB[[+ZZöÁn³OHK• æ€F’j;W8šÛÚˆü~?†Ìˆ'„0’F„ìST{"ä1[Í'y=µO\"ˆ‚€Ò’”˜ŒäAA*¤º²Z­‡Œ‘µy9Ð;0ˆåK“1ˆÂ¿A&òRnlÉÑ70s6–E)7:ÖÄ´rÑ[“‡FÿEN§Û¶mFKË>4͘…9s梡± ¢(BQ”¬,N³pT,ËèëíA{{;z{ºà §X+qh´‡õNÇ9Ÿ2 `GO/|~ÿ”þ#‰|¨ò$’ºø $&M=@+X×?„3ª!$(ô0Æ°ßæD‹Ý(ûF@Á IDAT¤ýüTP'ù"³¼>Ú»ºéF & ùXÓ'#mfŒRÚAA¨˜“%&# DY^$è";y}Ï[Ðm'¿¡ögˆœ(/#–Œ_LžÌÎB¸&N6£ˆRO+7þ(ÎG»\.Zš÷£­í JKJ±hÉR45΀ÑdJ(%Z*0Æ ( \.'z{z°ß8NÂ)~b-Äåó‚¹$ISÖ_Û_Ã#¸½^˜ †¸{vvõ&tbÃiær™"1¾g*­›dÅXä?Æ Dr8¢…sùË–SûþAN¯5J ÀÀK6é>^¿½}ý ŸG$IR±8Ä'ñÛx>žˆ8Äâ~×x÷böØèpº± ¬dRÿcl Ö ˜‘.åz²gŒA“pý-Ý}ƒðù|$E3IUºI ‚ ‚P3³šÁ+Ø{6UÝ? f6ÕãðE ÈĤ÷PÔn¢¨l_D¾““•@óˆJ$Z‚+Æ2*%&MÚº)8Uš¹Äü2Zð‘§HoVì‚€d«+œÃl±Äݧ¥µ‚(B™Âþ£]•ißMÿH˜JJÔÇÇOy~µ¸{w3~°|昌ÒÇ)œcÄïÇÃÍíhu¸(½\þŸŒ0)ˆš[Û±rÅ22^ùˆ 0Ê”‡!ô`E" ²¢dµ®!AA‘<3B÷ô´À›QDIBuE~ùÃÛÉDlH"ŠÍöEæ;YˆdEAÿÀx ¶’ŒI¸Ï'¼J¬vÐä"O¢‘C‡ÞS“ßÊÓ9¢ ¤”‚¬¯²¢L}ÔÚÑ >…Ø3Ö„¹ˆø¤¾™HÝ ø~;ñX©‰C 0†·wn߇å¥0Ž‹dñ+ öÚè÷xÓ&ò“hÉ ¤\Qp°½“¢t⥠0TWV ¦ª( ‰¨£»n'¡±ïø)­"Q”̙لÛÿë«()1Âïàñg^ÄÇ[>¥ßLdÕŽÔ'A$BC] A€œ[æœᵊù³gá[_¹F½žìBÄöj7QL¶/BßÉúªˆ¢(2§´%‰"üéîó Å‹J$ª(Ö|ê‹æÙ…3’­?4ÊÀÐ0”8QgwOÒçQ‹gÕ§'~Nö~úÅ¡ˆƒ-ÀC#“î#8”˜ÿ§ vöôÒD¦h`Ðj5(1 ËïÅD$áðûý”V±Håe¨¬(ƒÉh€Ûã…ÕfLjÕ^”ÑØ³g6â˜#= °|颼Z˜/+1¡ª²0b±Áît¥å7ëtZ¬>j–,˜—ǃ·ÞûCÃ@)øN"¶Ìw?L7ä›êŠ t:8)ò;‰i~¸,¥L! ²,C¯ÓA#JÐé´hj¬Ç9§œˆ³O=™ìFL¶pBí&ŠËöEê;Y_ñx<èíJiqV_èÒµ°Ë§|'^:¸ø‹áÉ~£:Æ@á ÆXJQ&œsô Âãñ@SZ:á}§Ë ‹Õ6©­&3¡¢X•ÆÞ㇉¤CŒµ/Kx<$ëZŒ|}Ú$+rÎ1J¢˜„˜Ö>Â÷bGÿÄ{¹È!òõ4û?Rˆ¬v;œ.7 DDQÜX+\ÃéDyyyLñ€È_æÎjÂÏï¸ ç͉»ŸV£Áq+Àq+Àùgž‚Ÿüút÷ UÄIÇ®Äw¾q=f6ÖOºÉhĩǯ©ǯÂ7¯» ÷ýéoxgý†¤¿ëÈÃG½žç; ²%ÙŒ ²Gey9ú yb¾£f£ÿƒ Šà`àŠ ½V “ÉÎtÌh¬Ç¬¦F45Ö£Ä`„Ñ GMu*ÊÊP^VY3cT³—˜´ÐOßÙˆÍðRˆÒUË%vZ¹©"#&K+7Ù";¥•Ë%‚ @HRLÝ× o`‹æÏ›°Ï°Å WŒúS}M(¬[˜²vQÚút‚ïé‡XŒo%qH÷‚Qþ½£û¹Ü˜GFÐPWK¢°Çcv‡‡ÕUUd”á’sÏÀm߸z½.©Ï-[¼OüáÜ÷Èßðê[ï’!UÀ/½·ÞpmRŸ©®ªÀ/¾w+ÌÃ|ºgRŸŽz­äyDÙç.8ßúÊÕÐi5°;]øÖ÷ÿmÝYùîB³%ÙŒ rK}] ö4·äÛd3\ã```‚lB÷ËŠ,CD˜L”˜JÐP[ƒŠ²Rèõz4ÔÕ`FC=f46¢¼¬ Õj Ñh %ø ‰CyŠzž 'ÅgûÛïçÚ辬 DݽEr é8çD@ å ŸrëtÓÊñ)¿QUQCxîäœC#Š)‹¢ MZghÐl†×çKº/G}×§(MŸ6udÜø='óñDÄS·”â˜(pH1á;N¼^/†Ì#4±!Š‚Ñ(»‹F£*Ê›÷|óú«pý•—NØÞÖÙ5ï}„Ξ>‚€Eóçàð…‡aÉÂyQéUŒ=~ôíoÀãõ¦B¤KÎ=#¦8är{°·å vï?—Ûƒ¦†:,[´ çGG‹-œ?'ièõµë±`Þl,š?ݽxú¥ÿäµ g4ÖÃd4ª*ÊQ[U™5¨ÐlI6#ˆÜ2«©1­¥Òc˜ÀB€2 €ŠÒku0tÐkµ(1™PY^Ž™31£¾e¥¥(/+Ei‰ 5U•0 ÔÁÄ!ÊËÜ«ë‰ödÞȇÈg²Þ~EEAtaÖ¢ƒíPd9õÅ{Q -A§ðùø5‡¦ª·’ìëñúULlxáFSpö“¢‡@Qdl_ßÀPÔþɘP’$øüþŒjr<æ–Xbgº}FŸtdªó­b‡Æúš’@Lè'A@o!§o ˆqãD£ÑÀj³Á`0 ¡®’$‘aò”úÚj\}ÙEÎq÷ÿé <ûï7¢¶¿õÞG½N‡_|ÿVœtìʨ÷oøÒç±îÃPºÉÌFƒ7]Õ„í¯½ó>~ùû?Ǭ1´rùRÜxÍX¹biÊß»·¥ÿõ½ŸS¤²%ÙŒ ÒÉl• D’Fƒ£W, Õù©¬€É¨GcmêPQZ £ÑI ŠbÒi¿‰"§¶6÷Õ"ŠÍöÓl¿PWWݘõ•Öö΄.'ƒ1–tê0`ªÈ¡lÄt¨hÑœvª-A` G˜Å²ƒÂ9ZZÛcîßÞÕ’8j—щíä>βãTjóóÑ–ÙŸ 0ˆ‚’ïƒs´uvѧ8fqeþ@ ”£|Üy‚;£äúý)çá„íÉœDQ„F£… x188IQS]M"Qžòõ«/‡Fs¨ï‚Á îºï¡ˆ ¯Ï‡Ûö|ï–pñ9§G¶Ï5Ÿ9ýd¼¾v=6|éò‹QUQu½zèoÏâ‰çþ5ég¶íÚ‹o~ïg¸ô¼3qþY§bÇžf2$AD0£±!”²M%4Ö×âç·‡:†È€³ÏöïÏÝ÷“8D›íÓÐ~aæÌ‚èʬ®‚  CæÊôÕ¹$SÔÅÿ¶ìˆCêòÿÂ>yŠ‚8í„Âô ¡¡¶6ÊnáÔs©˜1QL!½bj>Nâ/‰‚ ˆÓJÁÙÑÝMEM§KØNQ8úÍè4uWét:ø|:ø|^ôö÷ƒsŽªª*è´Zòãìkiþ­°;]1¥×éÐÔš»6ÔÖD½7oö ˜-–¨ûáö®žŒÌ•Rµå( u58þ˜#Q_S ,c×¾ìÜÛwŒ: éŸé£©¶!Wþ7–ƺÌŸ3 sf6¡ª¢N·ݽýغk/F,6ºØ9£®¦cPKe®!óÁ 4ôP‘nŽ8X»67ßMâQl¶OCûÍQGDwfõŠÖÖÝ=í›ÆDÆèt7±´rñ´ˆ»PäPvÅÄ"ˆâÙ‚sŽö®ž(¨·v‡sZ6Ì„@DâÐ$-+ÒÔ@’$¦ì÷à‡ =ý˜ÙØ@¢Æ‹½^`0Y–ÑÛ߯χښôzJ ’'|éò‹ Ї"']n7þóö{Iãå7×â{c¢õX²p>vï?µß Ç…ßþôöÈëk¾ujq×ÿü7Œ†è:Vþýy<úô €y³gâêÏ]ˆ³O9>n½«7oÇïþô:{ú¦lói'‹Û¿õUTWÆÎ{ÝÓ7€??ý<ÞXûÁ´m¼dÁ<üè;7aÁÜÙ1ß÷xñµ·ñÀãG0˜ºPtíç/޲çMíƒæè£ ¢K…l~Y{GWZ‘!±fO¾pNâP!3•pΧ´çmÑ©¶ÒQØWJób#‰C“´¬ˆëFÄó±Dú‰|Ÿ ‰j!£×ë¡× 8çA{G††‡ár»@=ªnÆ/¾öÎzx¼¾¤Ž±æÝáñz£¶5ÔÕLœcˆÑsŒ3NZ{xÛ„…_ðB„œv±xò¿ÄÅçœwá-?ý௰`ÞìI÷1èuøÉwÿ ÷þè¶IÅ! $rýô»ßÂׯ¾|Zö½ú² ñçßülÒ…hÐj4øÂgÏÇŸsëkSþ®Õ+WD½^¿q Z;º³âG⸾ã<žN›Œ÷©Õ+Wàé…S_S€‹Ï=þòÇÐëtQÛ«ÊË’úÍåe¥9·å(Ë—,ÀS܃³N9>æƒ^‚ à¨eKð§_ÿ3ê²e"ã3Ýc4Õ6äÊÿj«+ñäî™TmÛÙ§ž€3N\M"gÔTU¨æž“s޾AªÛJd€E‹€lg2 qˆ(6Û§©ý €¬ÕBZ²¤ º5k‘¬(iKe“Ƚ¹K+wè;Ô´h^L©¶„4‰0ýƒƒQ©evtB˜fú:& œ©ù8‰C¼ˆ' ‚ €MÓ¿AÄÁövšôE5nL&#t:}dQÒëó¡«»íèëïÇðÈì\n7<OFþ©3£±>êõk?ô1Ü/6nݵ­±nj¡ãkW_>éu½¼´pêñÇL¨me±Ù±k_ ö4„c\º0I’p[ŒhQn¼æ œæ)1ƒÓåž°ýŒ“ŽKÙ¶_¼ôÜòõk¢„ Ÿß{›ñÑæíèŠÚéÂÃðóÛoNé»JLFÌ›Ãûu¨Îß2m“[¾~ LFcäµ¢(ÆHvÄá‹pñ¹§GmK$òl:ûg’ãVŠ1WP–áóû'ì×ÔP‡ïùqB÷ƒ‰ŒÏLŒÑTÚ+ÿûÑwnŠ ƒ²Œ7oÇëk×cëÎ=Q™ôz]pˆ¬bµÛññæ­xè¯ÇàÐ0Ô”º¿³§—:ˆÈ ·ÜYZª%qˆ(6Û§±ýzA€é»ß-˜®ÍZŠ9¯×‹«5-‹·Œ…ÊYOv¤Ü¥•;ô9”„8éåµç##Vx½>˜Œ(Š‚ö®np>½ÌÇ HKþdž¤ç§ßÇŠR‚Àbžá’é'δuuCVˆ‚‚(DQBII </8WÀƒÇë…Ûã3›¡Õj¡ÑhÒ J79Óàסr\ÔDOjOõšG¢^' ”e<õü+x÷£Mh¬¯Ã1GŽ6mZà€`0ˆWßz/¿±û´F>«ÕhpÃ5ŸÇµŸ¿$²íè‡cáü9hiíˆúžYM ¸ââÏDmÛ½ÿ~ñD"mëjðÅË.Äç.8{¢w2”–˜ð•/\µmí‡ñ¿¿{nÏ¡h«Ï]p6n½áÚHí®åKâäÕGG~¢Ì›=cBäHo¿ºžÐΦM¼>?{úy<÷ÊsÄ2ÜuûGùû/»/üg ”ðüç±g^ŒÔ`ºéº«pÑ9‡jsý쾇°iÛÎÈkY–a±ÙU7¦ý{ú<÷Êx}^¾hnºîJ¬:rydŸúÚjœqâ±x{ý†iÏtÑTÛ ÿ3 X}Ô¡¨=›Ýþç'QÂaEY)¾pé8U%(…‰ÍîÀžælز{ZZ004Œ ¬€sE‘UÓÎPÝÖê0"3\|1ðÐC€Ó™aG&qˆ(2Û§¹ý^“ Õ—^Z0Ý›5Èíö`Äš¾â–ŒÅ^œŽ¿pNiå –qh”a› .·&£þ@]ݽPÒQCKc> š¬§MÜJiåŠQ'ŒdûIáݽuô¤(Qh¼¥ÂŒ†èè! »#µjóˆ5êu¢çwýæ‘Z.û´a݇#ïÿó•7ÑÖÕƒµlŒMïðÇ¿<ƒKá¨e‡RÌl¨Ÿ°ø|ó×¾%úlß½·ÞùKø|‡|³oÐŒûùÞX÷nùÚ5ع¯9%{|å —¡lL„ûmÂî¾Â~/¾ö64’Uãæ+_¸,éÅï²’‰Ñ½ã¢!rM¶lb±ÙñÝŸþ {šF¶mÚ¾·ýä^üåþ_D¶5Õ×bfcCdážs©Ïcs8¢ý{Ø2iíµ`³;pÛOUûk×¾ÜþóûðçßÜ…ÃæÎŠl¿üÂsˆ¦Ÿé£©¶!þ·è°9QóÆ××®Ÿ Yí<üijxø‰gé‚C¤Ì;ص¯·lÃîæ¬(à£ÿÔº–Á9º{û©‰LÝÔÇ ¬[—QÎKH"Û«¤ý €võjpQTQlë4×C²õEV»cBn÷”;‚±Ðd6©ÎNZ9õ8q¦ÚŠøFŒ³dñx¼°Zm¨«©FwO\iJ4s94ù7ôDüp—ۃΞ^,š?ŒªösžÀâ ãj¹)ŠçœÈB៩Záiÿ¢(Âd2A’$ø|^ø|>ÈrP]×w"ЦqµHÆ‹ßûï¯ãÿ}¯.0Ä´±ÚìAhOó˜G,9Ï;A(v‡Š¢¤­¾0AD1{6pÊ)À{ï¥wqžú‰b$Í~/2†ÀgÀ0gNA™)kQyI N^}LÚ. > j‘&wi·ÆHS*9Ùr>¹E Q Óh#Ý>S(Š‚²ð_©É„SŽ[•¶vz}¾¤Ý…Çó½lù8¸ê\‹¢‡Â½Ã cŠO×,%%&2jꑵ>7 ¨ª(Wµ9EAkG×$ç*“јòoðz}áÖŒ<!Š"DQ„V«ƒ,ËPœ+™»ÆzÜ4vR`h8ú ÞÊŠ²”e³ø ½S׿ùð“m“ uZ êjªQ]UšÊQ‘—aéÂùq?7³©!êõþm³ë¬qßµdÁ|Üñ­¯ÅýÌÊK£^Ïh¨OJ ×—e¥ÐHcË%Ù²ÉT¿wxÜSë¥r½öSÚe×þ·òˆÈ¶ÚšJì;Þñ91šÎ6dÃÿ\n6ºÇu¨¾Ó¥çŸ…3N>/¼ºÿ^³.fÊ=‚˜ ‹Õ†{öaã¶íØÓræ‘(<ôÀ¦¢(µ8íñzár{ æL¨ï}غ°§©^`>GÑšOníOí^ß(+Cùw\WgM Z¶d–-YDƒ‹ÈKŽ_µǯZI† B]ó¢ÿý™·c,ªæKæ (ú£kÔôzèu:x}É?ý>šŠi”ÞþÁ´¶õ´ŽÅg‚ã92R<>Æ/e0½Í¬¦Æ¨×¢(ಠÎNꢻ¦`|=Æêk«ÑÝ7  _Ë…MbápºŠv¼[mÑu•Ò=5Ý1šþ÷¿÷?Œ¿þßݨ,?$®——–à«_ü¾|Õ¥xÃfüíŸÿÂÞ–Vºà°Xmøt÷^|´eöl…yÄ^ËU ÈJAÏTÁ †-ˆˆÌQ^\uðè£$9ºÙ&qh<cÐ^}5„òò‚ën‰<ž ‚ ‚ÈGÍ#e%ª^PMUEJ¢ÂøzF½Cikçn¹—|æŒi£®¦:êµÓ•9Q±ºrz7=Š¢à@{gRŸéœ®§±¾V5Q.lRˆ÷êÓa¼ÀáöxU5FóÑÿ††qÍ·îÀ¿ý œ¸ê¨¨÷AÀé'®Æ©Ç¯ÂS/¼Šÿú ]tЋՆ­;wc֭ذ #V8E.ʼ½ƒ˜;‹jwä«_6nvîœÞ€êÅfû ´_d ìÈ#a¼ñÆ‚ìrˆ‚ ‚ ˆ¼DV˜G,¨¯=$žœ|Ü1øÇ˯%uœÊò2{äò¨m½ýé&¾}õž}~?¶î؃ÁáX¬vȲŒófã Ÿ¶ IDAT´Žô8NWtäHyiIÆì:2®¶WgOþò—þüÁö® éÿ¦"”104ŒÆúÚȶ£WŽO¶ïR…¯åÂ&D4@tú½t¥L×ÍWÿ¶XqÛOîÅÒ…óñ…K/À'­†V£‰¼/®»âØN<õÂ+äˆEİŊ-;vaãÖíh>ØñCªÉ¢«§Xu49 ‘Yîº ¸újÀãIíó$Åfû µ_1Pv÷ÝÛí$AM¨"oé2G D—œ{zÒÑEçœæÐ´XQtt÷M»m&£W^r^Ô¶—^{=ñ,ìgÔöÕG­ˆ»ø<>å]]MUÆlÚ3.j§«·¯¯]Ÿñ¾ì鈈®¸ø3xòùWàNuQ¤lBbì8–ú8é£ùî{[Zñ“_?€ß>\‚«>{>¾xé0ô‘÷o¼ö <û¯×USŒH?CælÞ±¶lÃöX¬v0ÆŠ6Bhªù~gO/ÙÈ< Àm·wßü}&‰CD±Ù>Cí—ƒáöÛ!66lד@DAAä-ë7lÁ‘‡/޼ž?g–-^€Ýû$ôyQpéygEm[óÞG¶X§Ý¶#–.ŠJ™¶uçÜûÇÇR:Öø”wË/„(3°h×3NŒš?{&cà¾iü`Ó6¬ÉUb2âó‹'žûWÎý,W6IÛMŸFÝ·}&£!îûUå8|Ñ‚¨m}ƒCª£…âv‡~ê9¼÷Ñ'øã/©±¢ÕhpØÜYØw .<ÂàÐ06mÿ·}Šƒm°9à Bœ¡¸p ™ÉDv¸è"Àb|09'Í×ÁEíUÔ~€î曡ûìg ºûA‘§qÆÔßF!ÎTƒ‚À¦ñûiCÏ¿º›=jÛoû&êj¦ü¬N«Á¯îü.f4ÖG¶e=ýBZÚ¶xÁ¼¨×;÷6OºoyyiÜc ª¯­Æ'­Žû™Úê*̙ٔt»Í#Qé¼ëkqÖÉÇe¼/_xuÍ!ìÚÏ_Œ£W,MèóKÌÇo½GqxÚÛ–+›¤J0(G½®(+Uõ8¾â¢Ïàü3O™ôýsN;1ªÖ˜ÓåÆˆÅ¦ª1šIráÍ­íøhóö¨m’DÏ—æ3}ƒƒø×›oá‡÷ü_¸éV|ùÛ·ãÁ¿þŸlÛa‹Á`r0@âPpðP &Ðb6‘%®¾X´(A%qˆ(2ÛgPbK–@Ýuï4Ã#‚ ˆžzáÜüÕ/E¶Í™Ù„Gû3|ûÇ÷à@[lÿ*-1á7?ùQÑGðú;·?-m³Ù£STM&Öœrü1øá-ñ žZílÛ¹+Lj%7^{%vï?€¾©¶®ºä<|ë«WcÈ<‚Ë¿þí$ï±8Ö¼÷!.<û´„¾k,³š08<ŸÏŸ´½Á yâYÜõÿþ;ªŸ~ÿ‹â?g&IXWS…뮸Ÿ»à‚€Aó¶îØ“æûÎÜØ$UÆGÀ­\¾¯½ó¾jDZ( ¸ó;7A#Iø÷šuQï-š?7^sEÔ¶—^[uc4³ëé÷¿•Ë—bþœYxcÝz¸Ü±Ó8VW–G½_ P7ݽ}Ø´m6mû­]áþ GÑBìt%.7üþtZ-ÙƒÈÆ…øË_€›nvíš|AœÄ!¢ØlŸAqH8òHT<þ8 þƒ©$ADžRIè7¤ôIr" ¯¾…k.¿•åe‘m5U•xô·?Ç®}ÍØÛÒŠ}Ú004ŒÊò2sä2|æô“¢ö¯ÏÇžy1míjnmz}ú‰«qõç.Ä?^~ ŠÂÑX_‹óÏ<_¿úòøÑvayêŸxøÞŸD^ϞшÇïÿyâYìin…,ËXtØ\\}Ù…X8À¤Ôö‡Ÿxg|<ôz]仞|à^üþѧ°eÇnô sI±dÁ|œ°ê(œæ)hª¯ÅŸžz§hÇ7ßýWîB,>ìPd‡$Џõ†kñÙóÎÄÞæVì;Ð ‡ËÆúZ,ž?'®^ I#û»2T³(W6I…AópÔëóÎ8ŸîÞMÛwâ°9³pìÊø÷›kÑÞ¥ž‚ àû·Ü€‡/Âsÿ~²,cÕ‘ËpÝ•ŸJAçð—_WåÍ$éö¿/^vN=~nøÒåøç¿ßÀÞyC!¿)/+ÅW¿pYTÊG—Û“–ÚlDfàœ££«›¶ŠO¶ï@{WODø#A(3x½>ØNÔVW‘1ˆl](ßþ¸öZ`ppâÂ8‰CD±Ù>Cí¯¯GÙï_â@AA‘çx}>|çÇ÷àg·ßŒÙ3Õë´Xuäò¨EÎÉp¹=øŸ»~–Â÷£4lGWo?f55D¶Ýòµkðå+/…ÉhŒJ™•Ûwíæm;±zåŠÈ¶Êò2|ïæ&ýŒ^§K©íCÃ<õÂ+øú—>ÙVb2â·†¢(|~?|>?JKL„j“Á0-»}ïïÃÏn¿+–F§R™;kæÎšóÏ:%îçÓ‘zLm6I–m»öÁãõ ×4 ?úÎMQûìmiU•@„¸øœÓqñ9§OºÏK¯½–a™£™$Ýþ7*xU”—áÆk¯Ä×^ —Û ·Ç‹ªŠŠ ¿ýùWß$‘Aeô âÝ6`ó§;ÑÙÓ—Û®(ÔWY (1bµ’@Dd—ÒRàå—ë¯ZZ-“8DíÓ‚ÈØâŨ|晢úÝ”¼Ÿ ‚ Ò ¥<#R@/Š~0•”’AR`ß6\Ë÷ñÊ[ï&ýÙ]ûZpÓwaÛ®½imS Ä÷þ>ªv”•–LX|ýpÓVXÇÕRŠÅ~y?Ö}¸)¡ï÷ùýxø‰gSnÿãÿx¿ô)xc¤FÓiµ(+-‰ÅØ;08-»õ šqÓíwá±g^„,'^ Ãîpâ×þo¬û c~–+›$‹ÍîÀ󯮉»V£žgï{äop{¼Sî÷÷^Åý~RÕc4“¤Óÿ¶OL¿i2Q[]5á·¼y;~âŸt¡‰q.3ê³þý#V+~÷ÈãøÆwâ‰ç^Æ®}Ͱ;å ™¢…²7ågèí C¹áá‡#ŽE78D£í3ðô‚ᨣPþØcEçADAA¯¿¸ÿ|´i.½à,¬\¾Z&æ¾^¯›¶ïÄšw?ÄÛë7$tüñu:\.÷”ŸÙw  ×Þ|þëË_ĩǯšð~Gw/^~ý<óòkx쾟£"œöÎ=Iš4§Ëïßý;|þ¢sqå%çafcý„ÔWN—k?؈¿ýóeôô¦ü…ãé—þƒ÷7lÆ·o¼+–,DyYlÓîpâ“í»ðƺ°~ã–i÷¥¬(øóSÏaã–Oñ…Ë.Àq+ˆJ3ÙOV°s_3Þÿx3^~cmL»%ò›µKºm’¬O9ÆÕ€qº'ßÿÁ¿þ6»7^{Å„q0bµa÷þIõI¢mMd?Ï1((ËXûÁF|°i+¾ûëqª£¢|Z–´vvá™—^‹[G)•ñ™î1šjrázò9 [pÙùgã°¹³b£³§?ó"Ö¼÷!‰*â÷šÛ:ðûÉ9¦«—Ò.9Â`‰D·Ý|ôÙƒ(.24'QN8å[Zb¬¦Ìh¨ƒËãÅ®}-°;œ‘÷%IDey9dYƈ5±4iz æÍÆÌÆz¸< ™GÐÒÚYQ2ò*ÊJ1wö 4ÔÖÀçóÃb·cÐ<‚ÞþÌFȈ¢ˆ u¨¬(CYI \Ìà šGàõùÒÒo©Úeº6Iö{ËJLÐëu¡úN×”ûk$ sf6¡´Äƒ^‡«mJ™n[ÙoôwØ®¨>¬­®Â‚¹³P^^ŠþA3öµ´%ÔÇÓõítÑTÛ+ÿ€ªÊrÌŸ=e¥`‚€þÁ!ôôf,]c¾Sר„3.¸0³¦WœzTÖ¾ûž?<Œ÷7l¢y¦ D§,¾wó7ÉDnù㧞ʯ¨ÚÊXe2-[gÝöÅÖ~‘1h¿üe˜n¹¥hÝ‚"‚ ‚H#jˆR-ª­ÄY8dŒÅLaSXóf>éÓÒ Xø¯¤Ž E‰}Lˆ‚ "?È•@ÔÓ?€Û~†G,Ñ¥˜ `Éaóñ»Ÿý¨àçÅD°v-pç@>ˆÇ ž¿H Êí‹©ý%¢áž{ =ûì¢v J1GAŠ( hª¯C*Ó¨îÞþIßÓët¨ª( ·”¡Åj‡k’ÔMZ•ååÆÔ ŠyG3n­ `ÄbC $ç$‚ ")zú`s¸HR œÃjw@VH¢Hö rË™g³f?úÐÕ¤%œ­qCíÕÐ~cÎ Í=÷@\´¨è݃"‚ ‚(PA@C]mJŸíé˜tB¯×¡¡®6åè¤|ÀïÀérÅ|"T§Õ¢¾¦Z­&©cz¼^ØΘ‘N#á’c‹_A¡.Æ^§ËLú¬}oKk+d™2Q œs8\Nøý~H„È= =<ú(ðφÔÕ$ 8D¶WCûƒž1°/~ßøX)Ý{€@& ‚ "ÉZÑ?½ÊS‰ËŠóý˜h¤ºÆ&r1‚ ‚P)¦1‹IeÆì DI®Ö¨€"ÅçóGÕ)#ˆœSR|ûÛ¡H"5¥>$qˆl¯–ö3ÍO Óÿü‰Cc ˆ ‚ "Çè$š’AD>A”EhñÂù%J£&ÎáóûÉ„ú¸ààÅÓNr‘Ä!²½ ÚohÏ<Uÿùt_L¾1š]AD:¡"µÔ•éíËxÓ[ý¨¶±‘ŒOA*eìuzVmEÖ¾wÙâE¨¯©AßÀŠ$RÇ\@]u5‚P' ÀÝw[·7ßœ›68”;HŠœ§顇 ?æ0ªz\• ‚ "Çè54Q%‚ ˆ| Wµ%QÄný&4ÍÔ€ X²ð0ètZ2¡^D8öX`Ãଳƒ!{4’8”;H‚ĘÑí9ç jÛ6hV¯&q(Þ5L@A4#buezû2Þ­˜N"uˆL%¥9[|"‚ "q²™b̃§¼ÇuJJJ Ñh JD‚(BÒh Š˜ @Ôh j4`ŒAˆRh„_ ¢‰˜f‚&„—ˆ‹Ž¤¦ù˜óÄ‹Î>‚@ËjDžð‹_?uTæÓÎÑ=1­Gä°ý&A€pôѨxòI”þêWä @)æ‚ ‚H–öôkyh>4à$À”C“ÚÒR¸œr5‚ ‚PuM‘¿gÖTä¤ e%%øù·Áç÷££«Z­‚À0b±ÂçÀdÐÃêpÂf³A’$ˆ’„ÁAx}~ôzøý~ôB’$hD 6‡V»&£>ŸV»œ+0ê °ØlðøüÐë´`ŒaxÄV I’àóû¡ ô”¶¬(à‰LàŠ,CEpÎ¡È Q—ep‚ ‚sŠ¢„Ä+Æ ( 8ç`LÀÃiôcihgº‚ˆ#—-Å'@ƒ‚È/æÍzxçà7¿,²I!QÄÂC(¥»PUáŽ;Pvî¹äÉØs’u ‚ "]¸Ünì?ØY–Ur›ÚSñrÛ3Vøç|ÒÅÆZ¼Hþ¨P”ɧ]ÍCN :|€ö–fl| (‚ ‚PË^…e+¿t.NX:· ~_P–!…# ‚Á ‚² ½N¿?ÏQ %Xv2ôz-‚ Ì+DA€¤‘`µÙáöx ×éàöxa±Úh5 ÃårÃ`0 (Ëèéë‡(ŠÐj4p:]¶Z`2”eØøýAF8N¸=h´hD#V[äs`0,(q!1ID(\W”PÄE–Có7JHœ<$F…ç·<"T±¨5F¨ EY1\|Ö™øæ—¿D‚Èo¼^àßÿþõ/ ½H×ýkš–™9«,C¦eë¬Û>ŸÚϘDÞùó¡¿ì2è.½Ì` _HŠ "‚ ˆ&EŒã‰'Å2ïæ<ýv-×k"AAêcl Øl§—ËÒ˜´S’$A’BKFZ­Z­&ò^½®&êsõuµimG0Œ|w @ „Ñ`€?€Óå† 0èµ:Xìvø|>ôzȲ ³Å Zv§6»Z W04<Œ`0ƒÞ‹Õ«ÝN†Þ~(Ðkµðú|†!üžÍá€ËíAUE9æÌœ‰sO;ǵ‚‘ÿèõÀ•WŸÿ<°f pÏ=€Ï7=‘ÄœÜÞ¤Yû\«»óNT\p@)?S¿þ“ ‚ ‚ rO…áÐÂKmc#„ ‚ TÆÜ…‹"Ϫ­ ƒdˆQq4áúJ@( ©ª¢<ò^£>Z˜jj¨Ÿöws~¨äÒX¡Š AÎ;/ôï©§BbQKË¡‘ÌàI7$8‡’ø Šî\¼úó΃áúëÉÒqÝ%AAä$ \¯Í€©¤uMìë%ÃA„ [¨Ì¨/È"â8€Ä!¢ø¸æàsŸvïþþwà“OB©ç¦ZÀϤHA"QaÛ'Áö *% ŽÕ«a¸þzHË—ƒ™LÔÿi‚®vA‘©»J‚HF¼¡¿ç-\LAA¨„±ÑásÈ A&F#p챡ÝÝÀOŠ(òzcïONî(pqˆ!T‹ z=ÄE‹ Ü}7ÊfΤ~Ï$AA¨„±uˆŒ¥¥d‚ ‚P µã"ˆ‚ ž™3GÚÚ€M›€×_‰E£QE$厇4ŒÁ$p.^ ÝEA{üñçϧ>Ï $A„Š`Œ©.)4÷çióTÇ„ÜCQr3á[‡ÈTRB‚ ‚ TB]áú€TˆøÿìÝy|TÕýÿñ÷™ì™ì I!AÂŽÊ¢€ *¨¸ *nUëÒýk­ÕÖÚݯ]´ýöÛ~Ûªí¯›uW´¶îZ7±Š(›°&$,Ù÷™ûû#0ä&“d’Ü™Ìd^ÏÇÇÜ;“™{ÏÜó¹çÞÏ=çQ¥¸¸ã¿K/•öí“î¹§#aD‚hè.Їáö;$Å:òÌš%ã?PZn®ätò{‡ "„ÃáPZª[Î0ky=^8Tí÷5Ã0”âNRlll¿?×TWߨ¦†+p8ÊÊHS¨›¿†¤ª‡zM\ ó~¦LŸéû7óˆZN§”Ÿ/ýîwRCƒôÔSÒÊ•=ŒèU Ã(9ä4 ņš““å,.VܼyŠ¿â æ$ˆ“›ÓéPA^®âl ¦Ö¶¶D‡¡œ¬L¥¥¤ è³wîÙÛs‚ÈéÐè‚‘C²ÏûVI‚H’rÜqªin“$Mž>S•/>G½`u^nöÄ" ’’¤ë®“®¾º£Wц ÒÒ¥ÿ7MÉ륌Âè^C¸l¿!)ÎáSR˱ÇÊuÅJŸ2EŽÜ\ÉEšb¨PòÐæëc£‚¸Íæ0nPçaærróèEÀcx9èË%ÙñßYgIÒÓOKÿú—tèÔÔD²hØÞ(è›qø?¯aÈ‘”$#=]΋/Vüå—+!1‘ß5\ª1E>:3'IÅ%ãI0D:/7it.ÃË@o¥k¯íøoûviËiõjiíZ©¼¼#YÄPtýaeud踇C5ùùrM›¦ØY³ä?^Î1cø=à "€0Óy˜¹ì¼< €!RT2Î÷ï‚,z@ÀŠ‹;þ;㌎$Gs³ôÏJÏ?/••‘$ D•‘CÉ!oQ‘‹ɸä¥ÇÇK†!9ü–aŒO†µ»Þy˜¹¤d7ÃÌ0’’ÝJJvû–'Î¥P ¿Ž$’’:æ,ºúj©¶VzçéÃ¥mÛ¤Š iÿþŽ÷𦠒Gáy‰®Ž$SR›iJYYræåÉ1fŒbO8A±§œ"#5•‚Š0$ˆÂLœË¡wœ*ëZ$1ÌCáÄSO÷ý{öÄ" ì’’"sNÇÍÍ £C‡¤uë¤7ß”¹fR%5z½j7MµGcÂ(LöÙer†â CµN§Œ3sÚiJ8öX9ÒÓe¤¦ÊˆgøÕHF‚; ²×‹A R‡NOô%ˆŠJÆiû–Í$‰‘¤d·rróJï!’øøŽÿrr¤ñã¥K/í¸¬ªRìŠ2Ö¯—ùÁò¶µI­­RK‹¦)Ó4u$…2ìÒG!Lþo†<†!#.NŠ•#×Ì™rM™"çYg)3;›ãu"A@˜ho÷hËör8Â+5âõ𽼿ծ½å*ßW9 Ïnmmëµ<6~¶uˆöÙ;äåçr(5>Æ7½ˆν‡&ÎUJ"OG@(ÙÙŠ½ê*ÅJJ’dVVª}ß>™{öȳw¯<[¶È³s§œÆå$ë?;I’²óò˜‹€èÖ{¨ÞC0ÔŒœÅääHS§v¬8üPŸiš2êêäÚ´IææÍr~ø¡´e‹UU2¿1=lLuíääÉÊ’Æ“1c†œãÇK“&)Ýí–qd$”#óE!ª S{%%»éE@L™>Ó÷ïI£sUF¡@¸9œÌ0$)-M®9säš3Gºî:ß[ÌäÙºUžÊJyËËåÙ¾]ÞÆF™ òÖÖJ­­òVWKííŠmn–Sòõ<òvI,uþ·7½|¦Ã0¬É#»+ù9IÓTkB‚ärÉ‘–&ÅÆÊ‘’"#)IFb¢œcÆÈ‘—'Gv¶bJJddfrÌÀ/DaŒ^D„NR²[E%ã|Ëô€ÈedfÊ•™Ùí¸ÙÒ"µ¶Êlo—š›ez½RS“äõªý³Ïd¶´ÈÓ£þõP2¤£Ÿq89ä‘ÔXP Ãáèè•™)çÈ‘2=9 åÌÌ”'׸q’Ã¡ÄøxN§/Ãåê˜7(.ŽýB‚ ŒuíE4yúLU¾ø@tž{¨ +ÞC0 qqR\œüÍþë7n`Ÿ¹k—Ì~ oÄÇË9j”eKé„ "€0×¹QNnžŠKÆkû–Í 6*.o™{hÎÄ" ®É R0ó@˜‹s9T˜žè[ž<}…€Í:Ÿ_gO,¢÷öèA€ #d_£WøÊ›šeö0i§ËåT\llÈ·Éëõª©¹¥ÇŸ,.6NNçÀž›iimU{»'èû0§ʺ5·{””ìÖ¬y§kõ[¯Sç°Á¬y§+)Ùí[¦÷ˆ$ˆˆ@‡Cy9ÙÊÉÊ »m[»a“ÚÚÚýl³¡ôÔ*ù6557ký§[z,ËÑùJq'è³·ïÜ­ý}â\JOЖªzIRv^žròòUY¾— À ää嫨äèœKæO¡€¨ÀsØ©‡ž3AùªH,ž¨ûb{pÇ)5>F’””ìÖ‰óN£Î0H“§Ïôý» +¡å@Ô AAÆåíétd¨900§Ÿw¡rró|Ë gN P@Ô AAâ\•dM•ŒSqÉx €~ÊÉË·$‡–Ì=^)‰ñ ˆ$ˆ"ÌwœrÜq¾åÉÓg('/Ÿ‚ @9yù:ýÜ |Ë -¢ "`3ƒ"Ñé‰ÌGÀuwhɼã)uH¡ŒˆÜæ!ÚjÃv…çrt›èôó.¤bЇ®óÍ™XD¡€¨d˜¦iR Ø£¡©I›·n—Çã î Ü0”§¸¸8)\Î䇓,5µµòzýoT\l¬B¾Í¯Gµuõ=–eRb¢bb\+ÌÞ÷Ïòº!546ªµµmÈŠ}_]‹¶TÝ·²-Ÿiõ[¯Sð£krhÉÜãZD-E@ä1MSMÍjljލínimUKkkØ•e}CCÄ #Üqji÷jç¡FIRQÉ85Ô×iýš¨(tÒ594{bÉ!Õb ¦'¨0=ѷݺMuõ Ý;†¡w’Æ)’câý>ñ[V‡C£ ò••‘´ï®mlÖú²r­þtÇÑc´¾Nï¿õ†*Ë÷RaÃJN^¾&OŸIr @ 1ú¥¯gKÂñÙ3JË*%1^'Ž/TQV²o]R²['Î;MÅ%ã9˜ÃFN^¾N?÷KrhöÄ"’C½`ˆ9ìÂ!ßèŒ@”âÌ$ÅÒ–ªzIG“D9yùZýÖë” ¢uRN’–Ì=^Ùi@/HDî8¥%Äè³ÊzÕ4·Iꘗ(;/!ç©§!åæL,"9DØ)„ë”v?~—(Þ÷NJœË¡q9ÉÚq¨Q•u-’Žö&ª*/§7 bL™>S“§Í°¬c¾!€þ!AEâ\ËNVj|Œeȹ¤·²óò´a͇ھe3KGæêŠ!åúè£y–Œ0ìÛdaZV!ذž¾ÁßsGz¯ k>`Ø9@Øð7œœÔÑkháÌ JIŒ§ú‰È4Mª©Ukk[·ÑÓº&úz½ëûúJY467÷˜Øðx¼ÚS±ON‡#àï·ãuCRKkkeÕÜܪÝ{+µß}½ßßvšRÙ)Ó4u°ºFÍÍ-½îgO£ãúº×ôÊãñú}OœË¡©ù)ÚW×¢]‡šÔÜî‘$åäæ)éð°sÛ·l&Q2IÉnM™>SE%ã,ëSã5{b‘&Î¥È0MÓ¤°GCc£6—–ÉãñPˆ(-í^í«kÑÎCÖcº¾ŽD äròòU\2¾[bH’&ÎÕÂ($€A"A€H!Òµ´{-ÃÎuV¶å3E€ ê-1Äprö"A€šš´yëvDˆx½%ŠèU°SR²[Åã:’BIÉîn¯“D؈†›6SÚYÓ¦òƒµ~_¯¬(׆5(ô[qÉx¯œÜ<¿¯O«9‹H  "lÄsnœN§Æ-–Ç4´jS™6î¨ðì×ש¡¾^uuô,ø•“—¯œ¼|eçå÷˜’H „ "lD‚ÃÍ‘QRB‚$©¶±Y»ªªµqG…vï¯î¹.†N’¶oÙ¬†º:5Ô×Q %’’ÝÊÉËW’ÛÝgBHêFnÒè\MKá„ "l) "Ó4ÕÑ0BÛð0$Ã08P"ˆÓéÔøcŠ””˜Øíµ@“E¾úq8At$qTY¾×’4j¨«³¼~:Ï”ävûÖ%¹ÝJJv+Ñíî3tDJb¼¯·B6Š„‘ÃáP~nŽòr²CþÝ5µuÚ\º$Qé-AÔÙ‘dÑîýÕªmh(aˆ.)‰ñ*ÈNSAV=…€‹" ú Õó!<—2|¥$Ækr§¡º&Œj;þDÏy!%1^)Iÿ§—@ø!AÛuMII£Ú†fÕ4MÕ64[^ïü@xÆ÷®ÿ>’’¤QYi*ÈN£ " "ìÄÐià¸îÑ‘§É (Q†œƒ"ˆ.$ˆ¢ "€(ÃDØÉ4#`M546iÿÁC2C¼½MÍ’˜§‰ã 5DDÓ4U]S«CÕ5Còý†A‚`¨‘  J‘¨ˆ^ÌAeH wô6`Ø!AeH`'zZ  ˆ2.Š™fH¾Æáp(/'[YéÝ7AÖm0dø]ßõõ¾þþˆƒÕ5Ú¹g¯ ?½¥\.§Æ)–Ëåðç÷´ MÚº}§ßNZN§SÅ…JJHè÷çæuÃ0´sO¹U×p쀈B‚€H=‰»œŠ’ïí‰aŠQŒËþ&Fkk›$S’áç{¥—kHÊÃé C6ˆ<ÜÑ B™Bv°q´0Ü ˆ2$ˆÐ;?€ˆF‚ Ê ÀN!ìiAŸ ` \‘ÈTSK‹jëë%3´ßÜÔÜÒãk^¯©úú9NÛ¿·±©YF 8Ó4ÕÐØ$¯ÇÒ²0 C­mmŽ â¦išöhhlÔæÒ2y<ž|ßPœÆ>zIm› £×NB_~åéœN§Æ-VRB€a„DD°pLN Õ6u|-ãÌ‚9ˆÐ;:œ0ì ˆ2$ˆ¢ "€(㢰‘aØúq&s¿ØøÓB˜×`è‘  \OÒ.§Æ&Idƒ–ÖV•íÚÓó<íÒP”³Ã)9èÐ Bv²1É`†ÜÉI”© ›š{ÿÍLS2½C°e$‡ÀÐà®@”!A†=fÐ$†9`Ø!AeHDDQÆEžLImmmòžÿÅ8<“Ž)ÿóÁûõ¾ õöõözk{[/nž¤ˆ™Šz-#0¬  Lµ·µkí†O)ˆPpÆP ª0Äv¢§†#Ó¤ fHDD؉žŽèÀ°C‚ Ê @ïèÀ°ã¢€¦ £—¡ÍÌŽ »íˆ$ˆˆr‡C9YŠ‹ ¯í2*ݱÓo2Æ0 e¤¦(Õí–©Ð&‰›T¹ÿ@t%‰Hˆ0ì  Ê†¡Ô·R’“ÃnÛ¶–íô››0 CÉI‰ÊÊLù69ժ܀D4æ ÀNô´@ AeH`'Ó¤ öHDDè=ãvH9ŒÈk†1DeepÀ€ˆg˜&„`—†ÆFm.-“Ç㉨í6M3 ;‰˜r8}l³))´ úäÔPp:L‘’©à #.І¡ðËyl3½y‚!æ° p\€@‚ Ê ÀNLí€@‚ Ê @ïèÀ°C‚ Ê ˆ2$ˆ¢ "ôÎ0(†DQ†v¢§" "€(C‚;™&eŽköHDDQ†@”!AeHDDØÈ02( §cúðq †®ö°Q|\¬ ƒ†Ã0KA0Ì ÀΫá8n¦c‰‹‹•ÃA“€á†«}l–šâ¦Àñ  "l–žšB †¢Ã¡´” €áxÝO`¯„øx¥¹“åt:) D,§Ó©4w²â) †!DA~IA b™¦©üÜà "‚ !!^y#²éE„ˆät:•?"[ ô`Ø"A@ŒÈÊTZŠ›ùˆYC‡Cé©nådeR ç{Áát:•Ÿ›£´T7…ˆ‘–êV~îz¿0Ì&$TÍ-­*ßW©‡ª™—áÛ(4 e¦§)oDŽâãb)†û½D—išjoo×êjUTî—Çã•×ë¥`‡œN‡rs²”‘–¦—K†aP0 s$ˆ#§Ûæ–Uî? ‡jd†¼^¯LÓ¤gB×ø3 †!‡Ã!Ó4•™žªœ¬,_¯!’CDD öövÕÖÕ«º¶NMjim•—S2‚ÌaŠ‹URR¢RÝÉJq'ËårQ0D!DQÆADDQ†@”!AeHDDQ†@”!AeHDDQ†@”!AeHDDQ†@”!AeHDDQ†@”!AeHDDQ†@”!AeHDDQ†@”!AeHDDQ†@”!AeHDDQ†@”!AeHDDQÆEÑ©¶¶VKŸzZõõõŠÕùç§ÂÂQ œCêø-£ " JíÙ³W¯½öoßrÑèÑ\¨zÕÜܬƦ&%&$(>>žs(çPPǨgà·Œ`$ˆ`›?üñÿiëÖRI¦&Œ¯/~ñ ÿíÁC‡ô»ßÝ«ššÅĸtÁùçkîÜS(TÃÞºuëõò²ejmmëöšÃ0§ÌŒLŽÒ 'Ì”Ûí¦Ðê,øí dÖoØ ?\£O>Y§ýû÷«µµÕ÷ZLLŒ²³³4eÊpÂLM™<™ 6lÔË//SsK‹ CÊÉÎÖÅ/Vfff@ÿç?ÿE;vî”a8tê¼¹:ãŒ*u ]¬ùè#-_¾Bíí¿¯ÇÆÄ(/?O£ 5zt¡FŽ)—‹Ûñ/‘°ÍÚµ«¶¶V’ÔÔÔܯ¿=tð 6oÞì[ÞüÙg$ˆD…‡yD»víè½üýAÍž=KŸ¿öZ%''Qxuüö4úÓŸÿ¢Õ«ßïñ=mmmÚ»·\{÷–kùòš7o®®¿îóQßë!<øÐCÝÎ[›>ݬ{îþ™bccûüûwÞ]¥––IRjJ "êüxôÑÇ´woy¯ïùhíZß¿N§æÌ™­k¯¹š‡‰6H0„ú“Pooo×Ê•ïhëÖ­ºýÛßR^^Pg-Ö®ýXO.}J­­­r»Ýºå–›•–šÊG¼€~ùì³-úý½÷iÿþýýú»·Þz[Ÿ~ºY7Ýô5•ŒKA†Ùy«¼¼\Ï?ÿ‚.¹äâ°ÝîhiËPdž=Ë{ãñx´rå;úøãOôùk¯ÑÉ'ŸDýÂ#A@9r±fš¦´o_¥>Y·NÏÑ.ëûô?¿úµþç—÷ÈétRhuÖgûöí*++ó-8p€‹>â5ôËÛ+Wêü“¼^¯eýèÑ…š3{¶FŒ!RÙö2•nÛ¦mÛ¶«±±Ñ÷¾ÊÊJÝyç]ºë®;u̘1h˜yþ…µ`Á|¥¥¥…åöEC[†:6|-Zt‘¯}ØÒÒ¢ºº:UVV©´´Ôò{×ÕÕéÞûî×;K[o½%dÃÎq­H&ââât©Ÿ§ùêêêô—¿þMï¿ÿߺòòrýû߯ëÌ3Ï àê,øíÀÍÍÍzô‘Ç,72‡¾ø…uÚi§ZÞ;{Ö,I½&ï»ÿzï½Õ¾×¼^¯žZú´î¸ãv 5Ì´´´è©§ÿ¡/~áF ƒ:›Û‡—_¶Äïk Z¾|…žþÇ3–ßþ£µkõ /úKÀPpP„7·Û­¯ßô_šÛ²þãO>ÑæÍŸQ¨a !!Á²üúëoh÷îÝ u !’””¤Å‹éÇ?ú¡²²2-¯ýó_Ϫªj?…„!C"D,Ç£ÊÊJíÝ[®êêj%&%*/7O……£äpô/÷yàÀmß^¦ºú:Œ©ÂÂBÅÅÅô·µµµjjjVRR¢’““%u<ɱqãFí«¬RŒË¥ÑE£5º°0¨û`˜Ÿ°].}ÎBmظѷ®§É0—:knnÖÎ;µgÏ^%%'iTÁ(‘3àøÔÚÚªòŠ •ï-W{{»’ÝÉ*9R)))~÷·ë÷ v¿jjj´k×.UWר¹¹Yñññ92_#GŽ h¢^ßÄ®]»´uk©’’’4bDŽ eF·Ï0MS¥¥Û´wï^9NS¬ÜÜ\¿ï Õ9CSg½^¯ª««ÕÞÞ®øø¥¤6Imss³êêêd†222|¿wkk«$©­½ÝZïZZ}¯I’aЉ‰ x?*++µyógŠ‹‹Sqq±²³³U.ƒ+½Õêªýútó§Š‹µg[ƒ¯íŠÕÁˆ×G{Ë˵{×ny<¥¥¥j̘1JLL YÛ®˜G¼×ÐРçŸÁ²îK_ü‚¦OŸÖç߆¡k¯½Z;wíÒ† |ëŸ\ú”~ôÃï%¶m¬Øêˆ¢¢"ÅÅÅjíÚ}mÒÇ{B·ßþ­°Ù·`µe¢µŽ…ÃõÓP׳PÖ±@W¢»þs}ëÛ·«¦¦Æwì?ôðúíÖoöù÷ýMìª_Á8&¤Ž^r›7¦ýöËåri\I‰²³³ûý9vn_¸ÜwÉõ M DšÖÖV½ôò2=ûìsjnî>ébJJŠžu¦-º¨× [SS£¿?øÖ¯ß úúún'Þ¼¼\]pþù½>űmÛv}ÿ?ôýÍý÷ý^õõõúõo~«òrë€/~áFÍŸº­û ºŒ+)±,777«©©ÉòDà@ãÒMMMzìñ'´nÝzíÛ·¯Û6ÄÄĨ°p”®þÜUš0aB@Û]YY©G}L|ð¡LÓ èo¦Nªï}÷;ƒÞ¯††½þúzëí·µk—ÿ§$N§Î9çl]¼xQ·§+{ûþøøx-]ú´Þ{ï=ª®¶¼¿  @×\ó9;uªoÝš5é‘Gë¶½‰‰‰ºàüóV€óÇð©³O=ýýë_Ïú^»å7kÖ¬{ý¼ƒê–oÞ¦¶¶ŽÉp,˜¯/Üxƒ^xñ%=úèc~ÿ殟ü´Ûºÿ臚0a|ßcš¦ž{îy­xõ58pÀòšÛ¬‹/^¬³. ¸ìŠ+þêabb¢žyæŸZõÞ{ª¬¬ô¶†*^6V+^ïÞ½[{àïúì³-–¹”¤ŽáoŽ;öX]réŽÎw`GÛŽ˜G¼úï•W–[æ9IKKÓìÙ³úõg,˜o¹y½iÓ&UUUYnô &ÚÕ¶³;öÊÎÙ^¯W—]¶Ä— ’:†¶Z¿aƒ¦LžlËw fßìnËD{ ‡ë§¡ªgCUÇú#99I‹]¤¿?øoÝ|¨ÒÒRsÌ1ÝÞ?Øßd°õ+XDŽԑzî¹çõÒË/«¡¡ÑòZzzºfLŸ¦k¯½¦×¤•ÝÛN÷C…"Jkk«~ø£;µsçÎßS[[«§žþ‡Š‹‹5mÚñ~ßóɺuºÿþ?ú²õþnŒìÝ[®ÿ÷§?kÃÆºñ†ëßí}---–¿)-ݦûÿðGË ¿ó;÷@ôéú´¼išÝzž $.±uk©~ï}ª¬¬ìqÚÚÚTZºMwýäg:ÿüótÙ’K{Psõê÷ußýðÝÐÔû½_ÛËÊtÏ=¿Tm§!üñxú¸~þ³Ÿ ú9Ø}³³-C ë§¡¨gCYÇúkÁ‚ùzáŵÿÑc¹¬lG·‘¿É`êW0މ#:¤üðGÚ±Ã{íСCzõµkoy¹¾uÛ­~;vo_¸Ýw•BDyôÑÇ,•ÔåriÚ´ã5oî\Mž4ÉrbõwÒ“:žà¾ûî_X*ill¬Æ¯éÓ§)''Çòþ•+ßÑ_þú·€¶ïý›¿·õð ;ö@têz#0;;»ÏFD qI’>úh­îüﻺ}‡aÊÉÉîvábš¦žþýù/íµaù§?ÿÅrC;##C'œ0S3¦Oïvµ³®±x ûõÎÊwº5SSSUR2VcÇŽíöý‡ª«µtéSý?¿ûKrÈåru»°öx<úÓŸÿ¢ßýîÞnÉ!O@½õöÛ–§:ƒqDøÔÙÙ³N´å”“uÕ•W(--Meeeú¿ßþÎ2,ÖËË^ñûçáWgSSS5íøãõáš5¾czÕ»«tÎ9gûý¬ŠŠ mß^æ[5ªÀw<ŸsöBÍžu¢ÌÃZ/½ô²ï}ߺíV)>Úøwºšï(!!AK–\ª‡Ÿlýì³-úý½÷éàÁƒ¾÷<ÿ =Ž™¬¸Œm ‡xhL F¹666ê©§Ÿ¶¬›4i¢n¼ázåççKêÞpÙ²W´ì•åjkk“×ë j{01x \çijašÿA’233,ËUUU¶µëìjÛÙûìûé(ãììl-\x–^|ñ%K›}Ö¬óÌ?UWwt\ôãŽ;VߺíVK¼ÎÈÈÐUW]©SN9E=ô°¦L™Ô6ö`bñ˜êêjËDå)))}>°Ó“Ô.í¨ªª¾{7ílÛÙûŠûý±xÑEzã7ÕÐÐ ©ãýË//ÓE]8¤ûfg[†:×O¡ªgáVÇÕµ'I×¹¡ìüMZ¿‚}Mž–¦ïÜq»%©s̘1úÆÍ_×O~úsË=…¯¾Ö-AdçöEÊ}ç``ˆ9D O—§yB¢«ÎYòØØX]~Ù’ß{òÉ'Y‚õg[¶ôŸ»êJßÉ6û zx½^íÛW©¥O=­ßy—å‰ñÂÂB½ð¬AÇ%Iú翞µ,_~Ù’‰ñññºþúÏ[Ö­]ûq·nÝÍÍͪ¨8:Û‰'Ìô;¾vJŠ[c:MDº·—§¦ú»_ùùù:÷Üsú¼ œ5Ûšê:ѽ?IIIúîßñ%‡:;ëÌ3º_ˆ/^dI‘››«É“'YÖ:tˆóG”ÔÙéÓ¦YÛ¶mó{a(I«Þ;š 2 C'Ÿ|RPö!))Ißûî~Ÿ^ËËËëVçü¯ÁŠ+ÁÚÖp‰×}Å´`•kס….¿lIó!ŽÒ~ð=-ZtQPÛØƒ‰yÄK``º[™>ˆò)nëÓç ¶Ä@;ÛvvľpºÑßsý%/¶¬{ö¹çU[[ñûF ¯ë§PÕ³H=sr¬=ÈüÕÁ`^Ó"˜ßŸ ;îøŽ%9Ôùw¼æê«,ëÞ{ou·¡ íܾH¹ï ô BÄÈÏË“Ëåòƒ×_CEEE:óŒý}{{»e¾ˆ‰&ôzBv8:æ˜1¾“ø¡C‡ü>ÕÝÙĉ{|ò׎}0¼µ´´èÛ·ßÑÑ8ñxTUUåw"O§Ó©ÿúÚWšÐ´¯¸ÔÐÐ`™„2''G3fLïõ3GŽ©’’méÔ€Ù·oŸRRR|ËŸ;ÒøëIçaÚ;ÝTÌ~õGV¦õ© š>摤 /8ß7,‚¿ò‰õ=!˜‘‘¡Ë–\Úãgiýú ¾åvWóÇð¬³.—K'Ÿt’^Y¾Ü·îwÞÕ¥—^byßîÝ»µk×nßò¤‰•‘‘”}ºð‚ó5rd~¯3fŒeXÈCÕÕÝ4ÁŠ+ƒÞÖC‡l¶ÁîxHL F¹655©¦SÂhì1Ǩ¸¸¸_eŒ6ö`bñ˜ÆÃ½¹Æ¯?OÜî±ÆC¯Ç²v] m;;bßPÆH;œyæZöÊrß÷455éÏ<£ë¯û|Äïu,<®ŸBUÏ"ù8lo÷tk_š¦ÙmnÛ`]ÓÚi@×Ô^ÐëÒ%%%JMMõ}V[[›ª««»õê±kû"á¾s° BÄp:š1cºV¯~_RG÷»¿ýíýû߯ë‚óÏÓ 'Ìô;1ñUUU¾aF$©²ªJÏ?ÿB¯ß¹sç®nŸ1räÈß?sÆô^ù`÷Àð·{÷î^_/9R×]÷ù€çæè+.UtjÀt\8LÃÑ÷ÅʔɓºÜp¬´t÷ÎḬ̀4®vtšè±k#ª¢S‰Q£ lÙ¯žx½^ÕÔÔ¨ººZuuuòš¦vu‰õf ¨>nöfd¤ûzPõ5tDZjªe¹¹¥™óGÕÙyóæZD+ý$ˆV½·Ú²lãu 1-åÚõ3;A¨`´±óˆ—ÀÀd¤[<¨©©ðg55Zo„gçd¥]7ж±o(c¤]çû+¯¼\¿ýíï}ë^{íß:{áBåååFô¾QÇÂãú)Tõ,’êýÖ*IIIýFv]Ó”]ßÛG{Ì0 Mœ0Á2§ïÁƒûL tû"á¾sЮi!’ÜpýuÚ²e«eÒá²²2ýþÞû”œœ¬3ÏX … ÏRj—›m’T^Qa]./×c&DçÉÖ‡bD'‡Ã¡œœ-\x¦Î<ãŒUíϾ kÃ=333 ¿KK·Þxí:d‚ÃáPAAÊÊÊ$I«W¿¯ 6vNí™þK‡ª«}Ë………¶—ßž={ôöÛ+µúýÿhß¾}–F[°ÄÆþÔO Oqþžuv̘bø’ ûöíÓÖ­¥;öß{:Ï?£OI=þ„ï¢fÕª÷tÍ5WËáphÕ{ïYÞÌáå"!®¯í-×®mÛ†úþÃÌ6ö`bñè¿ì¬,íÜut¸›õë×÷»×j{{»>\³Æz}ŸcOï»Úvvľpˆ‘v7®D³fèRI’â ýðßïs¨«pß7êØÐ^?…ªžEòqøñÇ[–'L˜v×´Cýý¦i_+>!>èÛ7î;÷ "ØfdÁHˉ®¢b_À ¢®ÝðFô1]X¨¯|ùKºöš«µ|ù =ÿ‹–žKýÛ–Š:b„õ‰ŠÜÜ\Í›7wHˬ¿ûvËé»WГ]ždÏa«¼¹¹YüãŸú¬ŒŒ ]uÕ¶>ÅíõzõàC[ÖwÞ¹ºxñ¢n®ÒmÛ"&AÄùcxJOO×qÇ«µk;.kjkõɺu:þ¸ã,Ã˹Ýnwì±QWˆ×ö—kv¶u¸›ûÿäc(ÚØƒ‰yÄK pYÙ֛ׯ¿ñF¿o^¿ÿþ,óÓ9ÎŸŠª¶±/œbä`]yÅåúàƒåñx$I7nÒºuëûLE¾Qdžîú)Tõ,RÃõë×kýú –u“&N«kÚp¸¦>xÈ:¤\v§¡ƒ½}Ãá¾s l22?ß²üÁ‡ôw¦ijÝ'ë,ë úÑ¥.11Q‹]¤ŸÜu§e ȃZÆŒìÚå¶óp-C-Ð}ÛÔ]bã'ëÖÔ5{ãÆ]?Ö‹’­[·ú.\ Ãн¿ÿ­nùÆÍºð 4{ö,rÊɺlÉ¥ºå7ë7¿þ•íCüìÚµË7|ƒÔ1,×ÕŸ»jØ=¥Èùcø8uÞ<Ëò;+ßÕÖ­¥–á9Nš3§ß×y½ÞaWˆ×ö—kv¶õ3˶—õ{»BÙÆLÌ#^}›>}ºeùã?éwýxõµ[–,˜¯øøø°jÛÙûÂ1Føü2b„žu¦eÝ“K—úÊi(öm(Ú2Ô±Á_?…ªžEBóçñ'–Z–G*иqãB~MÛ[ý ö÷·NJ÷øz{»6mÚdY×yÎP]ó‡ûÎ}!AÛtíõ³lÙ+Ú·o_Ÿ÷ÖÛo«lÇßr||¼²0Á^~~¾æÌ™mYW[{ô©Š„„¹Ýnßò–-[UZZVeØ×>€Ý233-7œ+++µ¡ËÍÄ®6múTÛ;5ð“’’ºÅí-[¶úþm†œN§fÍ:QW^q¹¾qó×õ__ûª/^¤Y³NTll¬íûµu«5¾Oš8±Ç÷¶µ¶EüïÈù#ò͘1]IIG/fþóÁzãÍ7-ï9唓ûüg—¡;?aéq…xm¹fggYžßZZÚ-~v‹™‡çLÊ6ö`bñèÙ©óæ*«ÓÓÙ¦iêOþK·9ƒ{òÄOZnæÅÄÄhÑE†]ÛΎبH¸!I‹/²´CJK·©¡¡aHö-Ú2Ô±Á_?…ªžEJë¼?üˆ¶mÛfYþyç…äš¶?õ+Ø×ÔÏ>û\¯ÇÄš>RCÃÑ;YY™–{¡¾æ÷{B‚¶9îØc•‘‘á[öx<úý½÷©²²²Ç¿Ù¸q“{ì kÃdÑE~»2WTTh]OJvÎKRZZªeyn—+?þd@'âö.c·”û¶6ŽnÝžzèaêÒ•ûˆêš=ôð#–u‹]Ô­WCB§ÉV½^¯~ö³»õ<£5k>ÒÆµuk©víÞ­ªªý¾á,ìÔõ©¡ýûø}߮ݻußý÷‡ýïÄùcø‹‰‰Ñœ9s|Ë---z­Ó¢¹¹¹MžnÞw[é¶aWˆ×ö—«ËåҌ֧™â‰ÛÇ6lÔ-ß¼M¿øå/ƒÚÆLÌ#^çr¹´xÑE–uü‰~úÓŸ÷šHõz½úÓŸÿ¢gŸ{Þ²þ¬³ÎTzzzصíìŠ}êûHNNÖ¢E‹Âbß¡-CüõS(ëY$Ô1©#¡qÇw¿¯—^^fY?uêT¿‚ãš¶?õ+Ø×ÔúùÝ÷hóæÏº½VSS£G}ܲîŒ ‚º}Ãá¾ó€cM Ø%!!A7Þp½þçWÿë[WZºM·ç»Z¼è"?^jmmÑîÝ»õŸ>Ô«¯¾fùŒÂQ£tÞyçúýügŸ{^o¼ñ¦Š‹‹tÑ…jÆŒé¾ Ð½^¯Þzëm½õÖÛ¾÷gddt›iñâÅzó­·}OÂlظQßûþõå/}AÇsŒå‚¹®®NŸ¬[§·ßZ©uë×ëÆ®×üù§ªŒìذۥ—\¬•+ßQ[[ÇS5»víÖï¼K×}þZ•”Œ•ÛíV}}ƒ>Ûò™þö·¿[æ³ÈÊÊì6$…$M;~šyä1_òg÷ž=zúÏøý~‡Ã¡œœܱ:ÿüó,ÝÆª¨¨È²¼âÕW5mÚñš:uН¶æ£ôÇ?þ©[#/qþˆóæÍíÖ6êéb£'é–åå+^Õ´éÓt̘1ª¬¬ÔÇŸ¬Ó¬Oè6‰j$ħ\/½ô}ðÁÑ¡¡7nܤ;ï¼K×^{Š‹‹äp8´sç.½üò2½»j•$)55%¨mìÁÄ<â%0øsÑ¿ž}Î2ÄéÖÒR}ïû?дãÓ˜1c4fL±222T[[«M›>Õ믿¡mÛ·w»?páض]v·íìˆ}êû:{áYZ¾|…å·Š} —¶ ulð×O¡ªgáRÇZ[[õÉ'Ÿø–kjkUQ±Oååå*/¯ÐŽ;º%²³³õõ›¾æwHÇ`ü&ý©_¡¸¦njjÒÝ÷üBK.½D Ì—ÃáЦMŸê‰'—Z:$$$èÌ3Ïjù ‡ûÎE‚¶š>}šfÏž¥÷Þ[í[×ÒÒ¢'ž\Úçß:N}á‹7öø´è‘lêöíeú¿ßþN ÊÍ¡øøU”—ëP—‰zÏ9{a·ÏHNNÒ%—\¬‡:Mb¶wï^ýøÎ»ät:•——§¸¸XÕÔÔèÀƒ–À]kC·f;öì–‘‘¡sÏ9ÛòDZUU•/áïv'«®®Þoܾ暫Óíµ#rôíoݦ{~Ñ÷`^¯WZVQ¡¯¾¦[¾q³fΜ1¨}Ê͡ѣ µcÇN_üýùÝ÷h̘1’¤={ö<”C8àüJÆŽU~~žöîí>^õÉ'– :æ˜1–:[WW§üàGЉ‰ñ%ÒÓÒ]dž"® 8å:º°P³f¨Õ«ß÷­+Û±Cwýä§’:† íz3ÃôšAmc&æ/AÞ$r¹tû·oÓïŸvîÚå[àÀ޹OºÌâOrr²î¸ãv¥¤¸mÛ.»Ûvvľ@ õ}ˆþüöW]y…~û»ßé¾…K[†:6øë§PÕ³p©c¦iêî{ï5yÒ$Ý|óM–aÉ‚ý›ô§~…ꚺ¥¥E<ú˜yô19¿ó"{ÎÙÝz Ù½}Ãá¾ó@1Äl÷µ¯~E—^rq¿.ìǯ{îþ™JÆŽíõÄÒYSS“¶o/Ó¦M›ºUÒÙ³géüóÏóû9g/>>ÞïæG.ø"9® xåú•/IsçžÒãÍŽÎââât饗µ=˜˜G¼¯  @?ýé]:{áYýþÛ‰'ê¿ïü‘Ž9|ƒÎ.ÁhÛÙû5”÷!úcöìYš2er·õ§#dûNmêØà¯ŸBUφªŽ 䡦ÜÜúÂ7è{ß»£×žpÁøMúS¿‚}MýÍo~Ã2÷Ù‘û]-Yr©ßãÂîí÷ŠD°]LLŒ.¹äbrÊ)züñ'ôé§Ÿª¦¶¶Ûûââ┟Ÿ§3ÏUkkk1äØc§jîÜS4sFß½:ílÛÙûú³vÆÈœ·’’’þ»¯ßt“~ý›ÿÓæÍ›}ç”Þ2ƾõ§-C³¯žÙ]ÇBYφ¢ŽIÒ駪'—>åw^]§Ó©¤¤$%&&*;+KãÆÓ„ ã5iâD¿CÊ…ê7éOý²õš:þh"Çår騩SõË_þB>øþóŸ,Ç…ÃáPá¨QZ¼xQ¯í5;·/ï;‡Šaö6ó`“úúíÝ»G••UJIMÑÈü|eddô™êImmvíÚ¥ÚºZ™^SÙÙYÊÉÉñû4z`ÛW¯={öªªªJ±q±Jq»•™™Pö¶©©IŠ‹‹Srròí€ÈäõzU]S#CRJJŠ-“²4.uU]]­Ý»w«¦¦Vyyy5ª  '¤ÚÛÛõ•¯þ—oÜÝÜÜúÅ=w+66¶Ç¿Ù±c‡îøî÷}˳f¨[¾q³mûÕØØ¨òŠ UVV*!!A%cÇZ.”=jjjär¹z|Š«?ßïñx|G¤ð»Ö×7¨¥¥Y }^¼qþ~u¶³––}õk7ùÆÉv:úãîP]nooWyE…ªU«­­M*,Õí‚´¿u«¡¡AÍÍÍŠï× §ÁÄ•¡ØÖ`ÿövÅj;ʵ«ÖÖVíܹKJHHPff¦ßc'˜ml;bñ°O[[›öìÙ«ÚºZÕÕÕÉ¡”·RRR•ŸŸç›§!T1ÐŽ¶±o0ûaGŒì³=ZS#‡Ã¡”””~ÇðÖÖVµ·{Óïß×®} ´-C³ÿø F u= v 5»“þÖ/;¾ÿHÛÜív[îTWWkÇŽª­«Õˆœõzÿ ØåN÷C°UEEÅÿoïnZ¢ Ã8ßJœ‘”Y™ø*WVÐ,²¢¶Ù&$kÝë¢èí“dAô92*h,[ت²­` .\µ°æ 6¶:1³º¯k÷{Þ¹¯Õjq`ÿ>C’Îêêj,--u΃ƒ;ãàÁqÃÐ;LtSœ:u2?žíÜ-,¼‰……7100õz=Úíïñùó—ß~Ìóê•Ë122bHÒi6_VÎFã¿Þ7ÿœ@×]8?ß¾~æÜËÊ}Y–Q–åoŸŠ©©³Ñh1 élmmÅüëו;¯— —" ëúúúâÒ¥‹19y:šÍ¹xûî}¬­­U~1TE Ç®]Ã1>>'Ž‹Z­f` (the BSD license). A detailed description of cx_Oracle changes can be found in the :ref:`release notes `. Contents: User Guide ========== .. toctree:: :maxdepth: 3 user_guide/introduction.rst user_guide/installation.rst user_guide/initialization.rst user_guide/connection_handling.rst user_guide/sql_execution.rst user_guide/plsql_execution.rst user_guide/bind.rst user_guide/lob_data.rst user_guide/json_data_type.rst user_guide/soda.rst user_guide/xml_data_type.rst user_guide/batch_statement.rst user_guide/exception_handling.rst user_guide/aq.rst user_guide/cqn.rst user_guide/txn_management.rst user_guide/tuning.rst user_guide/globalization.rst user_guide/startup.rst user_guide/ha.rst user_guide/tracing_sql.rst API Manual ========== .. toctree:: :maxdepth: 3 api_manual/module.rst api_manual/connection.rst api_manual/cursor.rst api_manual/variable.rst api_manual/session_pool.rst api_manual/subscription.rst api_manual/lob.rst api_manual/object_type.rst api_manual/aq.rst api_manual/soda.rst api_manual/deprecations.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-cx_Oracle-8.3.0/doc/src/license.rst000066400000000000000000000035661414105416400204620ustar00rootroot00000000000000:orphan: .. _license: ******* License ******* .. include:: .. centered:: **LICENSE AGREEMENT FOR CX_ORACLE** Copyright |copy| 2016, 2020, Oracle and/or its affiliates. All rights reserved. Copyright |copy| 2007-2015, Anthony Tuininga. All rights reserved. Copyright |copy| 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: #. Redistributions of source code must retain the above copyright notice, this list of conditions, and the disclaimer that follows. #. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. #. Neither the names of the copyright holders nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \*AS IS\* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Computronix |reg| is a registered trademark of Computronix (Canada) Ltd. python-cx_Oracle-8.3.0/doc/src/release_notes.rst000066400000000000000000003273451414105416400216740ustar00rootroot00000000000000:orphan: .. _releasenotes: cx_Oracle Release Notes ======================= For any deprecations, see :ref:`Deprecations `. Version 8.3 (November 2021) --------------------------- #) Updated embedded ODPI-C to `version 4.3.0 `__. #) Added official support for Python 3.10. #) Support for dequeuing messages from Oracle Transactional Event Queue (TEQ) queues was restored. #) Corrected calculation of attribute :data:`MessageProperties.msgid`. Note that the attribute is now also read only. #) Binary integer variables now explicitly convert values to integers (since implicit conversion to integer has become an error in Python 3.10) and values that are not `int`, `float` or `decimal.Decimal` are explicitly rejected. #) Improved samples and test suite. Version 8.2.1 (June 2021) ------------------------- #) Updated embedded ODPI-C to `version 4.2.1 `__. #) Added support for caching the database version in pooled connections with Oracle Client 19 and earlier (later Oracle Clients handle this caching internally). This optimization eliminates a round-trip previously often required when reusing a pooled connection. #) Fixed a regression with error messages when creating a connection fails. #) Fixed crash when using the deprecated parameter name `keywordParameters` with :meth:`Cursor.callproc()`. #) Improved documentation and the test suite. Version 8.2 (May 2021) ---------------------- #) Updated embedded ODPI-C to `version 4.2.0 `__. #) Threaded mode is now always enabled when creating connection pools with :meth:`cx_Oracle.SessionPool()`. Any `threaded` parameter value is ignored. #) Added :meth:`SessionPool.reconfigure()` to support pool reconfiguration. This method provides the ability to change properties such as the size of existing pools instead of having to restart the application or create a new pool. #) Added parameter `max_sessions_per_shard` to :meth:`cx_Oracle.SessionPool()` to allow configuration of the maximum number of sessions per shard in the pool. In addition, the attribute :data:`SessionPool.max_sessions_per_shard` was added in order to permit making adjustments after the pool has been created. They are usable when using Oracle Client version 18.3 and higher. #) Added parameter `stmtcachesize` to :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()` in order to permit specifying the size of the statement cache during the creation of pools and standalone connections. #) Added parameter `ping_interval` to :meth:`cx_Oracle.SessionPool()` to specify the ping interval when acquiring pooled connections. In addition, the attribute :data:`SessionPool.ping_interval` was added in order to permit making adjustments after the pool has been created. In previous cx_Oracle releases a fixed ping interval of 60 seconds was used. #) Added parameter `soda_metadata_cache` to :meth:`cx_Oracle.SessionPool()` for :ref:`SODA metadata cache ` support. In addition, the attribute :data:`SessionPool.soda_metadata_cache` was added in order to permit making adjustments after the pool has been created. This feature significantly improves the performance of methods :meth:`SodaDatabase.createCollection()` (when not specifying a value for the metadata parameter) and :meth:`SodaDatabase.openCollection()`. Caching is available when using Oracle Client version 21.3 and higher (or Oracle Client 19 from 19.11). #) Added support for supplying hints to SODA operations. A new non-terminal method :meth:`~SodaOperation.hint()` was added and a `hint` parameter was added to the methods :meth:`SodaCollection.insertOneAndGet()`, :meth:`SodaCollection.insertManyAndGet()` and :meth:`SodaCollection.saveAndGet()`. All of these require Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11). #) Added parameter `bypass_decode` to :meth:`Cursor.var()` in order to allow the `decode` step to be bypassed when converting data from Oracle Database into Python strings (`issue 385 `__). Initial work was done in `PR 549 `__. #) Enhanced dead connection detection. If an Oracle Database error indicates that a connection is no longer usable, the error `DPI-1080: connection was closed by ORA-%d` is now returned. The `%d` will be the Oracle error causing the connection to be closed. Using the connection after this will give `DPI-1010: not connected`. This behavior also applies for :data:`Connection.call_timeout` errors that result in an unusable connection. #) Eliminated a memory leak when calling :meth:`SodaOperation.filter()` with a dictionary. #) The distributed transaction handle assosciated with the connection is now cleared on commit or rollback (`issue 530 `__). #) Added a check to ensure that when setting variables or object attributes, the type of the temporary LOB must match the expected type. #) A small number of parameter, method, and attribute names were updated to follow the PEP 8 style guide. This brings better consistency to the cx_Oracle API. The old names are still usable but may be removed in a future release of cx_Oracle. See :ref:`_deprecations_8_2` for details. #) Improved the test suite. Version 8.1 (December 2020) --------------------------- #) Updated embedded ODPI-C to `version 4.1.0 `__. #) Added support for new JSON data type available in Oracle Client and Database 21 and higher. #) Dropped support for Python 3.5. Added support for Python 3.9. #) Added internal methods for getting/setting OCI attributes that are otherwise not supported by cx_Oracle. These methods should only be used as directed by Oracle. #) Minor code improvement supplied by Alex Henrie (`PR 472 `__). #) Builds are now done with setuptools and most metadata has moved from `setup.py` to `setup.cfg` in order to take advantage of Python packaging improvements. #) The ability to pickle/unpickle Database and API types has been restored. #) Tests can now be run with tox in order to automate testing of the different environments that are supported. #) The value of prefetchrows for REF CURSOR variables is now honored. #) Improved documentation, samples and test suite. Version 8.0.1 (August 2020) --------------------------- #) Updated embedded ODPI-C to `version 4.0.2 `__. This includes the fix for binding and fetching numbers with 39 or 40 decimal digits (`issue 459 `__). #) Added build metadata specifying that Python 3.5 and higher is required in order to avoid downloading and failing to install with Python 2. The exception message when running ``setup.py`` directly was updated to inform those using Python 2 to use version 7.3 instead. #) Documentation improvements. Version 8.0 (June 2020) ----------------------- #) Dropped support for Python 2. For those still requiring Python 2, see :ref:`python2`. #) Updated embedded ODPI-C to `version 4.0.1 `__. #) Reworked type management to clarify and simplify code - Added :ref:`constants ` for all database types. The database types :data:`cx_Oracle.DB_TYPE_BINARY_FLOAT`, :data:`cx_Oracle.DB_TYPE_INTERVAL_YM`, :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` and :data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` are completely new. The other types were found in earlier releases under a different name. These types will be found in :data:`Cursor.description` and passed as the defaultType parameter to the :data:`Connection.outputtypehandler` and :data:`Cursor.outputtypehandler` functions. - Added :ref:`synonyms ` from the old type names to the new type names for backwards compatibility. They are deprecated and will be removed in a future version of cx_Oracle. - The DB API :ref:`constants ` are now a specialized constant that matches to the corresponding database types, as recommended by the DB API. - The variable attribute :data:`~Variable.type` now refers to one of the new database type constants if the variable does not contain objects (previously it was None in that case). - The attribute :data:`~LOB.type` was added to LOB values. - The attribute :data:`~ObjectAttribute.type` was added to attributes of object types. - The attribute :data:`~ObjectType.element_type` was added to object types. - :ref:`Object types ` now compare equal if they were created by the same connection or session pool and their schemas and names match. - All variables are now instances of the same class (previously each type was an instance of a separate variable type). The attribute :data:`~Variable.type` can be examined to determine the database type it is associated with. - The string representation of variables has changed to include the type in addition to the value. #) Added function :meth:`cx_Oracle.init_oracle_client()` in order to enable programmatic control of the initialization of the Oracle Client library. #) The default encoding for all character data is now UTF-8 and any character set specified in the environment variable ``NLS_LANG`` is ignored. #) Added functions :meth:`SodaCollection.save()`, :meth:`SodaCollection.saveAndGet()` and :meth:`SodaCollection.truncate()` available in Oracle Client 20 and higher. #) Added function :meth:`SodaOperation.fetchArraySize()` available in Oracle Client 19.5 and higher. #) Added attribute :attr:`Cursor.prefetchrows` to control the number of rows that the Oracle Client library fetches into internal buffers when a query is executed. #) Internally make use of new mode available in Oracle Client 20 and higher in order to avoid a round-trip when accessing :attr:`Connection.version` for the first time. #) Added support for starting up a database using a parameter file (PFILE), as requested (`issue 295 `__). #) Fixed overflow issue when calling :meth:`Cursor.getbatcherrors()` with row offsets exceeding 65536. #) Eliminated spurious error when accessing :attr:`Cursor.lastrowid` after executing an INSERT ALL statement. #) Miscellaneous improvements supplied by Alex Henrie (pull requests `419 `__, `420 `__, `421 `__, `422 `__, `423 `__, `437 `__ and `438 `__). #) Python objects bound to boolean variables are now converted to True or False based on whether they would be considered True or False in a Python if statement. Previously, only True was treated as True and all other Python values (including 1, 1.0, and "foo") were treated as False (pull request `435 `__). #) Documentation, samples and test suite improvements. Version 7.3 (December 2019) --------------------------- #) Added support for Python 3.8. #) Updated embedded ODPI-C to `version 3.3 `__. #) Added support for CQN and other subscription client initiated connections to the database (as opposed to the default server initiated connections) created by calling :meth:`Connection.subscribe()`. #) Added :attr:`support ` for returning the rowid of the last row modified by an operation on a cursor (or None if no row was modified). #) Added support for setting the maxSessionsPerShard attribute when :meth:`creating session pools `. #) Added check to ensure sharding key is specified when a super sharding key is specified. #) Improved error message when the Oracle Client library is loaded successfully but the attempt to detect the version of that library fails, either due to the fact that the library is too old or the method could not be called for some reason (`node-oracledb issue 1168 `__). #) Adjusted support for creating a connection using an existing OCI service context handle. In order to avoid potential memory corruption and unsupported behaviors, the connection will now use the same encoding as the existing OCI service context handle when it was created. #) Added ``ORA-3156: OCI call timed out`` to the list of error messages that result in error DPI-1067. #) Adjusted samples and the test suite so that they can be run against Oracle Cloud databases. #) Fixed bug when attempting to create a scrollable cursor on big endian platforms like AIX on PPC. #) Eliminated reference leak and ensure that memory is properly initialized in case of error when using sharding keys. #) Eliminated reference leak when splitting the password and DSN components out of a full connect string. #) Corrected processing of DATE sharding keys (sharding requires a slightly different format to be passed to the server). #) Eliminated reference leak when :meth:`creating message property objects `. #) Attempting to use proxy authentication with a homogeneous pool will now raise a ``DatabaseError`` exception with the message ``DPI-1012: proxy authentication is not possible with homogeneous pools`` instead of a ``ProgrammingError`` exception with the message ``pool is homogeneous. Proxy authentication is not possible.`` since this check is done by ODPI-C. An empty string (or None) for the user name will no longer generate an exception. #) Exception ``InterfaceError: not connected`` is now always raised when an operation is attempted with a closed connection. Previously, a number of different exceptions were raised depending on the operation. #) Added ``ORA-40479: internal JSON serializer error`` to the list of exceptions that result in ``cx_Oracle.IntegrityError``. #) Improved documentation. Version 7.2.3 (October 2019) ---------------------------- #) Updated embedded ODPI-C to `version 3.2.2 `__. #) Restored support for setting numeric bind variables with boolean values. #) Ensured that sharding keys are dedicated to the connection that is acquired using them in order to avoid possible hangs, crashes or unusual errors. #) Corrected support for PLS_INTEGER and BINARY_INTEGER types when used in PL/SQL records (`ODPI-C issue 112 `__). #) Improved documentation. Version 7.2.2 (August 2019) --------------------------- #) Updated embedded ODPI-C to `version 3.2.1 `__. #) A more meaningful error is now returned when calling :meth:`SodaCollection.insertMany()` with an empty list. #) A more meaningful error is now returned when calling :meth:`Subscription.registerquery()` with SQL that is not a SELECT statement. #) Eliminated segfault when a connection is closed after being created by a call to :meth:`cx_Oracle.connect()` with the parameter ``cclass`` set to a non-empty string. #) Added user guide documentation. #) Updated default connect strings to use 19c and XE 18c defaults. Version 7.2.1 (July 2019) ------------------------- #) Resolved ``MemoryError`` exception on Windows when using an output type handler (`issue 330 `__). #) Improved test suite and samples. #) Improved documentation. Version 7.2 (July 2019) ----------------------- #) Updated embedded ODPI-C to `version 3.2 `__. #) Improved AQ support - added support for enqueue and dequeue of RAW payloads - added support for bulk enqueue and dequeue of messages - added new method :meth:`Connection.queue()` which creates a new :ref:`queue object ` in order to simplify AQ usage - enhanced method :meth:`Connection.msgproperties()` to allow the writable properties of the newly created object to be initialized. - the original methods for enqueueing and dequeuing (Connection.deq(), Connection.deqoptions(), Connection.enq() and Connection.enqoptions()) are now deprecated and will be removed in a future version. #) Removed preview status from existing SODA functionality. See `this tracking issue `__ for known issues with SODA. #) Added support for a preview of SODA bulk insert, available in Oracle Client 18.5 and higher. #) Added support for setting LOB object attributes, as requested (`issue 299 `__). #) Added mode :data:`cx_Oracle.DEFAULT_AUTH` as requested (`issue 293 `__). #) Added support for using the LOB prefetch length indicator in order to reduce the number of round trips when fetching LOBs and then subsequently calling :meth:`LOB.size()`, :meth:`LOB.getchunksize()` or :meth:`LOB.read()`. This is always enabled. #) Added support for types BINARY_INTEGER, PLS_INTEGER, ROWID, LONG and LONG RAW when used in PL/SQL. #) Eliminated deprecation of attribute :attr:`Subscription.id`. It is now populated with the value of ``REGID`` found in the database view ``USER_CHANGE_NOTIFICATION_REGS`` or the value of ``REG_ID`` found in the database view ``USER_SUBSCR_REGISTRATIONS``. For AQ subscriptions, the value is 0. #) Enabled PY_SSIZE_T_CLEAN, as required by Python 3.8 (`issue 317 `__). #) Eliminated memory leak when fetching objects that are atomically null (`issue 298 `__). #) Eliminated bug when processing the string representation of numbers like 1e-08 and 1e-09 (`issue 300 `__). #) Improved error message when the parent cursor is closed before a fetch is attempted from an implicit result cursor. #) Improved test suite and samples. #) Improved documentation. Version 7.1.3 (April 2019) -------------------------- #) Updated to `ODPI-C 3.1.4 `__. #) Added support for getting the row count for PL/SQL statements (`issue 285 `__). #) Corrected parsing of connect string so that the last @ symbol is searched for instead of the first @ symbol; otherwise, passwords containing an @ symbol will result in the incorrect DSN being extracted (`issue 290 `__). #) Adjusted return value of cursor.callproc() to follow documentation (only positional arguments are returned since the order of keyword parameters cannot be guaranteed in any case) (`PR 287 `__). #) Corrected code getting sample and test parameters by user input when using Python 2.7. Version 7.1.2 (March 2019) -------------------------- #) Updated to `ODPI-C 3.1.3 `__. #) Ensured that the strings "-0" and "-0.0" are correctly handled as zero values (`issue 274 `__). #) Eliminated error when startup and shutdown events are generated (`ODPI-C issue 102 `__). #) Enabled the types specified in :meth:`Cursor.setinputsizes()` and :meth:`Cursor.callfunc()` to be an object type in addition to a Python type, just like in :meth:`Cursor.var()`. #) Reverted changes to return decimal numbers when the numeric precision was too great to be returned accurately as a floating point number. This change had too great an impact on existing functionality and an output type handler can be used to return decimal numbers where that is desirable (`issue 279 `__). #) Eliminated discrepancies in character sets between an external connection handle and the newly created connection handle that references the external connection handle (`issue 273 `__). #) Eliminated memory leak when receiving messages received from subscriptions. #) Improved test suite and documentation. Version 7.1.1 (February 2019) ----------------------------- #) Updated to `ODPI-C 3.1.2 `__. #) Corrected code for freeing CQN message objects when multiple queries are registered (`ODPI-C issue 96 `__). #) Improved error messages and installation documentation. Version 7.1 (February 2019) --------------------------- #) Updated to `ODPI-C 3.1 `__. #) Improved support for session tagging in session pools by allowing a session callback to be specified when creating a pool via :meth:`cx_Oracle.SessionPool()`. Callbacks can be written in Python or in PL/SQL and can be used to improve performance by decreasing round trips to the database needed to set session state. Callbacks written in Python will be invoked for brand new connections (that have never been acquired from the pool before) or when the tag assigned to the connection doesn't match the one that was requested. Callbacks written in PL/SQL will only be invoked when the tag assigned to the connection doesn't match the one that was requested. #) Added attribute :attr:`Connection.tag` to provide access to the actual tag assigned to the connection. Setting this attribute will cause the connection to be retagged when it is released back to the pool. #) Added support for fetching SYS.XMLTYPE values as strings, as requested (`issue 14 `__). Note that this support is limited to the size of VARCHAR2 columns in the database (either 4000 or 32767 bytes). #) Added support for allowing the typename parameter in method :meth:`Cursor.var()` to be None or a valid object type created by the method :meth:`Connection.gettype()`, as requested (`issue 231 `__). #) Added support for getting and setting attributes of type RAW on Oracle objects, as requested (`ODPI-C issue 72 `__). #) Added support for performing external authentication with proxy for standalone connections. #) Added support for mixing integers, floating point and decimal values in data passed to :meth:`Cursor.executemany()` (`issue 241 `__). The error message raised when a value cannot be converted to an Oracle number was also improved. #) Adjusted fetching of numeric values so that no precision is lost. If an Oracle number cannot be represented by a Python floating point number a decimal value is automatically returned instead. #) Corrected handling of multiple calls to method :meth:`Cursor.executemany()` where all of the values in one of the columns passed to the first call are all None and a subsequent call has a value other than None in the same column (`issue 236 `__). #) Added additional check for calling :meth:`Cursor.setinputsizes()` with an empty dictionary in order to avoid the error "cx_Oracle.ProgrammingError: positional and named binds cannot be intermixed" (`issue 199 `__). #) Corrected handling of values that exceed the maximum value of a plain integer object on Python 2 on Windows (`issue 257 `__). #) Added error message when attempting external authentication with proxy without placing the user name in [] (proxy authentication was previously silently ignored). #) Exempted additional error messages from forcing a statement to be dropped from the cache (`ODPI-C issue 76 `__). #) Improved dead session detection when using session pools for Oracle Client 12.2 and higher. #) Ensured that the connection returned from a pool after a failed ping (such as due to a killed session) is not itself marked as needing to be dropped from the pool. #) Eliminated memory leak under certain circumstances when pooled connections are released back to the pool. #) Eliminated memory leak when connections are dropped from the pool. #) Eliminated memory leak when calling :meth:`Connection.close()` after fetching collections from the database. #) Adjusted order in which memory is freed when the last references to SODA collections, documents, document cursors and collection cursors are released, in order to prevent a segfault under certain circumstances. #) Improved code preventing a statement from binding itself, in order to avoid a potential segfault under certain circumstances. #) Worked around OCI bug when attempting to free objects that are PL/SQL records, in order to avoid a potential segfault. #) Improved test suite and samples. Note that default passwords are no longer supplied. New environment variables can be set to specify passwords if desired, or the tests and samples will prompt for the passwords when needed. In addition, a Python script is now available to create and drop the schemas used for the tests and samples. #) Improved documentation. Version 7.0 (September 2018) ---------------------------- #) Update to `ODPI-C 3.0 `__. #) Added support for Oracle Client 18 libraries. #) Added support for SODA (as preview). See :ref:`SODA Database `, :ref:`SODA Collection ` and :ref:`SODA Document ` for more information. #) Added support for call timeouts available in Oracle Client 18.1 and higher. See :attr:`Connection.call_timeout`. #) Added support for getting the contents of a SQL collection object as a dictionary, where the keys are the indices of the collection and the values are the elements of the collection. See function :meth:`Object.asdict()`. #) Added support for closing a session pool via the function :meth:`SessionPool.close()`. Once closed, further attempts to use any connection that was acquired from the pool will result in the error "DPI-1010: not connected". #) Added support for setting a LOB attribute of an object with a string or bytes (instead of requiring a temporary LOB to be created). #) Added support for the packed decimal type used by object attributes with historical types DECIMAL and NUMERIC (`issue 212 `__). #) On Windows, first attempt to load oci.dll from the same directory as the cx_Oracle module. #) SQL objects that are created or fetched from the database are now tracked and marked unusable when a connection is closed. This was done in order to avoid a segfault under certain circumstances. #) Re-enabled dead session detection functionality when using pools for Oracle Client 12.2 and higher in order to handle classes of connection errors such as resource profile limits. #) Improved error messages when the Oracle Client or Oracle Database need to be at a minimum version in order to support a particular feature. #) When a connection is used as a context manager, the connection is now closed when the block ends. Attempts to set ``cx_Oracle.__future__.ctx_mgr_close`` are now ignored. #) When a DML returning statement is executed, variables bound to it will return an array when calling :meth:`Variable.getvalue()`. Attempts to set ``cx_Oracle.__future__.dml_ret_array_val`` are now ignored. #) Support for Python 3.4 has been dropped. #) Added additional test cases. #) Improved documentation. Version 6.4.1 (July 2018) ------------------------- #) Update to `ODPI-C 2.4.2 `__. - Avoid buffer overrun due to improper calculation of length byte when converting some negative 39 digit numbers from string to the internal Oracle number format (`ODPI-C issue 67 `__). #) Prevent error "cx_Oracle.ProgrammingError: positional and named binds cannot be intermixed" when calling cursor.setinputsizes() without any parameters and then calling cursor.execute() with named bind parameters (`issue 199 `__). Version 6.4 (July 2018) ----------------------- #) Update to `ODPI-C 2.4.1 `__. - Added support for grouping subscriptions. See parameters groupingClass, groupingValue and groupingType to function :meth:`Connection.subscribe()`. - Added support for specifying the IP address a subscription should use instead of having the Oracle Client library determine the IP address on its own. See parameter ipAddress to function :meth:`Connection.subscribe()`. - Added support for subscribing to notifications when messages are available to dequeue in an AQ queue. The new constant :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` should be passed to the namespace parameter of function :meth:`Connection.subscribe()` in order to get this functionality. Attributes :attr:`Message.queueName` and :attr:`Message.consumerName` will be populated in notification messages that are received when this namespace is used. - Added attribute :attr:`Message.registered` to let the notification callback know when the subscription that generated the notification is no longer registered with the database. - Added support for timed waits when acquiring a session from a session pool. Use the new constant :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT` in the parameter getmode to function :meth:`cx_Oracle.SessionPool` along with the new parameter waitTimeout. - Added support for specifying the timeout and maximum lifetime session for session pools when they are created using function :meth:`cx_Oracle.SessionPool`. Previously the pool had to be created before these values could be changed. - Avoid memory leak when dequeuing from an empty queue. - Ensure that the row count for queries is reset to zero when the statement is executed (`issue 193 `__). - If the statement should be deleted from the statement cache, first check to see that there is a statement cache currently being used; otherwise, the error "ORA-24300: bad value for mode" will be raised under certain conditions. #) Added support for using the cursor as a context manager (`issue 190 `__). #) Added parameter "encodingErrors" to function :meth:`Cursor.var()` in order to add support for specifying the "errors" parameter to the decode() that takes place internally when fetching strings from the database (`issue 162 `__). #) Added support for specifying an integer for the parameters argument to :meth:`Cursor.executemany()`. This allows for batch execution when no parameters are required or when parameters have previously been bound. This replaces Cursor.executemanyprepared() (which is now deprecated and will be removed in cx_Oracle 7). #) Adjusted the binding of booleans so that outside of PL/SQL they are bound as integers (`issue 181 `__). #) Added support for binding decimal.Decimal values to cx_Oracle.NATIVE_FLOAT as requested (`issue 184 `__). #) Added checks on passing invalid type parameters to methods :meth:`Cursor.arrayvar()`, :meth:`Cursor.callfunc()` and :meth:`Cursor.setinputsizes()`. #) Corrected handling of cursors and rowids in DML Returning statements. #) Added sample from David Lapp demonstrating the use of GeoPandas with SDO_GEOMETRY and a sample for demonstrating the use of REF cursors. #) Adjusted samples and documentation for clarity. #) Added additional test cases. Version 6.3.1 (May 2018) ------------------------ #) Update to `ODPI-C 2.3.2 `__. - Ensure that a call to unregister a subscription only occurs if the subscription is still registered. - Ensure that before a statement is executed any buffers used for DML returning statements are reset. #) Ensure that behavior with cx_Oracle.__future__.dml_ret_array_val not set or False is the same as the behavior in cx_Oracle 6.2 (`issue 176 `__). Version 6.3 (April 2018) ------------------------ #) Update to `ODPI-C 2.3.1 `__. - Fixed binding of LONG data (values exceeding 32KB) when using the function :meth:`Cursor.executemany()`. - Added code to verify that a CQN subscription is open before permitting it to be used. Error "DPI-1060: subscription was already closed" will now be raised if an attempt is made to use a subscription that was closed earlier. - Stopped attempting to unregister a CQN subscription before it was completely registered. This prevents errors encountered during registration from being masked by an error stating that the subscription has not been registered! - Added error "DPI-1061: edition is not supported when a new password is specified" to clarify the fact that specifying an edition and a new password at the same time is not supported when creating a connection. Previously the edition value was simply ignored. - Improved error message when older OCI client libraries are being used that don't have the method OCIClientVersion(). - Fixed the handling of ANSI types REAL and DOUBLE PRECISION as implemented by Oracle. These types are just subtypes of NUMBER and are different from BINARY_FLOAT and BINARY_DOUBLE (`issue 163 `__). - Fixed support for true heterogeneous session pools that use different user/password combinations for each session acquired from the pool. - Added error message indicating that setting either of the parameters arraydmlrowcounts and batcherrors to True in :meth:`Cursor.executemany()` is only supported with insert, update, delete and merge statements. #) Fixed support for getting the OUT values of bind variables bound to a DML Returning statement when calling the function :meth:`Cursor.executemany()`. Note that the attribute dml_ret_array_val in :attr:`cx_Oracle.__future__` must be set to True first. #) Added support for binding integers and floats as cx_Oracle.NATIVE_FLOAT. #) A :attr:`cx_Oracle._Error` object is now the value of all cx_Oracle exceptions raised by cx_Oracle. (`issue 51 `__). #) Added support for building cx_Oracle with a pre-compiled version of ODPI-C, as requested (`issue 103 `__). #) Default values are now provided for all parameters to :meth:`cx_Oracle.SessionPool`. #) Improved error message when an unsupported Oracle type is encountered. #) The Python GIL is now prevented from being held while performing a round trip for the call to get the attribute :attr:`Connection.version` (`issue 158 `__). #) Added check for the validity of the year for Python 2.x since it doesn't do that itself like Python 3.x does (`issue 166 `__). #) Adjusted documentation to provide additional information on the use of :meth:`Cursor.executemany()` as requested (`issue 153 `__). #) Adjusted documentation to state that batch errors and array DML row counts can only be used with insert, update, delete and merge statements (`issue 31 `__). #) Updated tutorial to import common connection information from files in order to make setup a bit more generic. Version 6.2.1 (March 2018) -------------------------- #) Make sure cxoModule.h is included in the source archive (`issue 155 `__). Version 6.2 (March 2018) ------------------------ #) Update to `ODPI-C 2.2.1 `__. - eliminate error "DPI-1054: connection cannot be closed when open statements or LOBs exist" (`issue 138 `__). - avoid a round trip to the database when a connection is released back to the pool by preventing a rollback from being called when no transaction is in progress. - improve error message when the use of bind variables is attempted with DLL statements, which is not supported by Oracle. - if an Oracle object is retrieved from an attribute of another Oracle object or a collection, prevent the "owner" from being destroyed until the object that was retrieved has itself been destroyed. - correct handling of boundary numbers 1e126 and -1e126 - eliminate memory leak when calling :meth:`Connection.enq()` and :meth:`Connection.deq()` - eliminate memory leak when setting NCHAR and NVARCHAR attributes of objects. - eliminate memory leak when fetching collection objects from the database. #) Added support for creating a temporary CLOB, BLOB or NCLOB via the method :meth:`Connection.createlob()`. #) Added support for binding a LOB value directly to a cursor. #) Added support for closing the connection when reaching the end of a ``with`` code block controlled by the connection as a context manager, but in a backwards compatible way (`issue 113 `__). See :data:`cx_Oracle.__future__` for more information. #) Reorganized code to simplify continued maintenance and consolidate transformations to/from Python objects. #) Ensure that the number of elements in the array is not lost when the buffer size is increased to accommodate larger strings. #) Corrected support in Python 3.x for cursor.parse() by permitting a string to be passed, instead of incorrectly requiring a bytes object. #) Eliminate reference leak with LOBs acquired from attributes of objects or elements of collections. #) Eliminate reference leak when extending an Oracle collection. #) Documentation improvements. #) Added test cases to the test suite. Version 6.1 (December 2017) --------------------------- #) Update to `ODPI-C 2.1 `__. - Support was added for accessing sharded databases via sharding keys (new in Oracle 12.2). NOTE: the underlying OCI library has a bug when using standalone connections. There is a small memory leak proportional to the number of connections created/dropped. There is no memory leak when using session pools, which is recommended. - Added options for authentication with SYSBACKUP, SYSDG, SYSKM and SYSRAC, as requested (`issue 101 `__). - Attempts to release statements or free LOBs after the connection has been closed (by, for example, killing the session) are now prevented. - An error message was added when specifying an edition and a connection class since this combination is not supported. - Attempts to close the session for connections created with an external handle are now prevented. - Attempting to ping a database earlier than 10g results in ORA-1010: invalid OCI operation, but that implies a response from the database and therefore a successful ping, so treat it that way! (see ``__ for more information). - Support was added for converting numeric values in an object type attribute to integer and text, as requested (`ODPI-C issue 35 `__). - Setting attributes :attr:`DeqOptions.msgId` and :attr:`MessageProperties.msgId` now works as expected. - The overflow check when using double values (Python floats) as input to float attributes of objects or elements of collections was removed as it didn't work anyway and is a well-known issue that cannot be prevented without removing desired functionality. The developer should ensure that the source value falls within the limits of floats, understand the consequent precision loss or use a different data type. - Variables of string/raw types are restricted to 2 bytes less than 1 GB (1,073,741,822 bytes), since OCI cannot handle more than that currently. - Support was added for identifying the id of the transaction which spawned a CQN subscription message, as requested (`ODPI-C issue 32 `__). - Corrected use of subscription port number (`issue 115 `__). - Problems reported with the usage of FormatMessage() on Windows were addressed (`ODPI-C issue 47 `__). - On Windows, if oci.dll cannot be loaded because it is the wrong architecture (32-bit vs 64-bit), attempt to find the offending DLL and include the full path of the DLL in the message, as suggested. (`ODPI-C issue 49 `__). - Force OCI prefetch to always use the value 2; the OCI default is 1 but setting the ODPI-C default to 2 ensures that single row fetches don't require an extra round trip to determine if there are more rows to fetch; this change also reduces the potential memory consumption when fetchArraySize was set to a large value and also avoids performance issues discovered with larger values of prefetch. #) Fix build with PyPy 5.9.0-alpha0 in libpython mode (`PR 54 `__). #) Ensure that the edition is passed through to the database when a session pool is created. #) Corrected handling of Python object references when an invalid keyword parameter is passed to :meth:`cx_Oracle.SessionPool`. #) Corrected handling of :attr:`Connection.handle` and the handle parameter to :meth:`cx_Oracle.connect` on Windows. #) Documentation improvements. #) Added test cases to the test suite. Version 6.0.3 (November 2017) ----------------------------- #) Update to `ODPI-C 2.0.3 `__. - Prevent use of uninitialized data in certain cases (`issue 77 `__). - Attempting to ping a database earlier than 10g results in error "ORA-1010: invalid OCI operation", but that implies a response from the database and therefore a successful ping, so treat it that way! - Correct handling of conversion of some numbers to NATIVE_FLOAT. - Prevent use of NaN with Oracle numbers since it produces corrupt data (`issue 91 `__). - Verify that Oracle objects bound to cursors, fetched from cursors, set in object attributes or appended to collection objects are of the correct type. - Correct handling of NVARCHAR2 when used as attributes of Oracle objects or as elements of collections. #) Ensure that a call to setinputsizes() with an invalid type prior to a call to executemany() does not result in a type error, but instead gracefully ignores the call to setinputsizes() as required by the DB API (`issue 75 `__). #) Check variable array size when setting variable values and raise IndexError, as is already done for getting variable values. Version 6.0.2 (August 2017) --------------------------- #) Update to `ODPI-C 2.0.2 `__. - Don't prevent connection from being explicitly closed when a fatal error has taken place (`issue 67 `__). - Correct handling of objects when dynamic binding is performed. - Process deregistration events without an error. - Eliminate memory leak when creating objects. #) Added missing type check to prevent coercion of decimal to float (`issue 68 `__). #) On Windows, sizeof(long) = 4, not 8, which meant that integers between 10 and 18 digits were not converted to Python correctly (`issue 70 `__). #) Eliminate memory leak when repeatedly executing the same query. #) Eliminate segfault when attempting to reuse a REF cursor that has been closed. #) Updated documentation. Version 6.0.1 (August 2017) --------------------------- #) Update to `ODPI-C 2.0.1 `__. - Ensure that queries registered via :meth:`Subscription.registerquery()` do not prevent the associated connection from being closed (`ODPI-C issue 27 `__). - Deprecated attribute :attr:`Subscription.id` as it was never intended to be exposed (`ODPI-C issue 28 `__). It will be dropped in version 6.1. - Add support for DML Returning statements that require dynamically allocated variable data (such as CLOBs being returned as strings). #) Correct packaging of Python 2.7 UCS4 wheels on Linux (`issue 64 `__). #) Updated documentation. Version 6.0 (August 2017) ------------------------- #) Update to `ODPI-C 2.0 `__. - Prevent closing the connection when there are any open statements or LOBs and add new error "DPI-1054: connection cannot be closed when open statements or LOBs exist" when this situation is detected; this is needed to prevent crashes under certain conditions when statements or LOBs are being acted upon while at the same time (in another thread) a connection is being closed; it also prevents leaks of statements and LOBs when a connection is returned to a session pool. - On platforms other than Windows, if the regular method for loading the Oracle Client libraries fails, try using $ORACLE_HOME/lib/libclntsh.so (`ODPI-C issue 20 `__). - Use the environment variable DPI_DEBUG_LEVEL at runtime, not compile time. - Added support for DPI_DEBUG_LEVEL_ERRORS (reports errors and has the value 8) and DPI_DEBUG_LEVEL_SQL (reports prepared SQL statement text and has the value 16) in order to further improve the ability to debug issues. - Correct processing of :meth:`Cursor.scroll()` in some circumstances. #) Delay initialization of the ODPI-C library until the first standalone connection or session pool is created so that manipulation of the environment variable NLS_LANG can be performed after the module has been imported; this also has the added benefit of reducing the number of errors that can take place when the module is imported. #) Prevent binding of null values from generating the exception "ORA-24816: Expanded non LONG bind data supplied after actual LONG or LOB column" in certain circumstances (`issue 50 `__). #) Added information on how to run the test suite (`issue 33 `__). #) Documentation improvements. Version 6.0 rc 2 (July 2017) ---------------------------- #) Update to `ODPI-C rc 2 `__. - Provide improved error message when OCI environment cannot be created, such as when the oraaccess.xml file cannot be processed properly. - On Windows, convert system message to Unicode first, then to UTF-8; otherwise, the error message returned could be in a mix of encodings (`issue 40 `__). - Corrected support for binding decimal values in object attribute values and collection element values. - Corrected support for binding PL/SQL boolean values to PL/SQL procedures with Oracle client 11.2. #) Define exception classes on the connection object in addition to at module scope in order to simplify error handling in multi-connection environments, as specified in the Python DB API. #) Ensure the correct encoding is used for setting variable values. #) Corrected handling of CLOB/NCLOB when using different encodings. #) Corrected handling of TIMESTAMP WITH TIME ZONE attributes on objects. #) Ensure that the array position passed to var.getvalue() does not exceed the number of elements allocated in the array. #) Reworked test suite and samples so that they are independent of each other and so that the SQL scripts used to create/drop schemas are easily adjusted to use different schema names, if desired. #) Updated DB API test suite stub to support Python 3. #) Added additional test cases and samples. #) Documentation improvements. Version 6.0 rc 1 (June 2017) ---------------------------- #) Update to `ODPI-C rc 1 `__. #) The method :meth:`Cursor.setoutputsize` no longer needs to do anything, since ODPI-C automatically manages buffer sizes of LONG and LONG RAW columns. #) Handle case when both precision and scale are zero, as occurs when retrieving numeric expressions (`issue 34 `__). #) OCI requires that both encoding and nencoding have values or that both encoding and encoding do not have values. These parameters are used in functions :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool`. The missing value is set to its default value if one of the values is set and the other is not (`issue 36 `__). #) Permit use of both string and unicode for Python 2.7 for creating session pools and for changing passwords (`issue 23 `__). #) Corrected handling of BFILE LOBs. #) Add script for dropping test schemas. #) Documentation improvements. Version 6.0 beta 2 (May 2017) ----------------------------- #) Added support for getting/setting attributes of objects or element values in collections that contain LOBs, BINARY_FLOAT values, BINARY_DOUBLE values and NCHAR and NVARCHAR2 values. The error message for any types that are not supported has been improved as well. #) Enable temporary LOB caching in order to avoid disk I/O as `suggested `__. #) Added support for setting the debug level in ODPI-C, if desirable, by setting environment variable DPI_DEBUG_LEVEL prior to building cx_Oracle. #) Correct processing of strings in :meth:`Cursor.executemany` when a larger string is found after a shorter string in the list of data bound to the statement. #) Correct handling of long Python integers that cannot fit inside a 64-bit C integer (`issue 18 `__). #) Correct creation of pool using external authentication. #) Handle edge case when an odd number of zeroes trail the decimal point in a value that is effectively zero (`issue 22 `__). #) Prevent segfault under load when the attempt to create an error fails. #) Eliminate resource leak when a standalone connection or pool is freed. #) Correct `typo `__. #) Correct handling of REF cursors when the array size is manipulated. #) Prevent attempts from binding the cursor being executed to itself. #) Correct reference count handling of parameters when creating a cursor. #) Correct determination of the names of the bind variables in prepared SQL statements (which behaves a little differently from PL/SQL statements). Version 6.0 beta 1 (April 2017) ------------------------------- #) Simplify building cx_Oracle considerably by use of `ODPI-C `__. This means that cx_Oracle can now be built without Oracle Client header files or libraries and that at runtime cx_Oracle can adapt to Oracle Client 11.2, 12.1 or 12.2 libraries without needing to be rebuilt. This also means that wheels can now be produced and installed via pip. #) Added attribute :attr:`SessionPool.stmtcachesize` to support getting and setting the default statement cache size for connections in the pool. #) Added attribute :attr:`Connection.dbop` to support setting the database operation that is to be monitored. #) Added attribute :attr:`Connection.handle` to facilitate testing the creation of a connection using a OCI service context handle. #) Added parameters tag and matchanytag to the :meth:`cx_Oracle.connect` and :meth:`SessionPool.acquire` methods and added parameters tag and retag to the :meth:`SessionPool.release` method in order to support session tagging. #) Added parameter edition to the :meth:`cx_Oracle.SessionPool` method. #) Added support for `universal rowids `__. #) Added support for `DML Returning of multiple rows `__. #) Added attributes :attr:`Variable.actualElements` and :attr:`Variable.values` to variables. #) Added parameters region, sharding_key and super_sharding_key to the :meth:`cx_Oracle.makedsn()` method to support connecting to a sharded database (new in Oracle Database 12.2). #) Added support for smallint and float data types in Oracle objects, as `requested `__. #) An exception is no longer raised when a collection is empty for methods :meth:`Object.first()` and :meth:`Object.last()`. Instead, the value None is returned to be consistent with the methods :meth:`Object.next()` and :meth:`Object.prev()`. #) If the environment variables NLS_LANG and NLS_NCHAR are being used, they must be set before the module is imported. Using the encoding and nencoding parameters to the :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool` methods is a simpler alternative to setting these environment variables. #) Removed restriction on fetching LOBs across round trips to the database (eliminates error "LOB variable no longer valid after subsequent fetch"). #) Removed requirement for specifying a maximum size when fetching LONG or LONG raw columns. This also allows CLOB, NCLOB, BLOB and BFILE columns to be fetched as strings or bytes without needing to specify a maximum size. #) Dropped deprecated parameter twophase from the :meth:`cx_Oracle.connect` method. Applications should set the :attr:`Connection.internal_name` and :attr:`Connection.external_name` attributes instead to a value appropriate to the application. #) Dropped deprecated parameters action, module and clientinfo from the :meth:`cx_Oracle.connect` method. The appcontext parameter should be used instead as shown in this `sample `__. #) Dropped deprecated attribute numbersAsString from :ref:`cursor objects `. Use an output type handler instead as shown in this `sample `__. #) Dropped deprecated attributes cqqos and rowids from :ref:`subscription objects `. Use the qos attribute instead as shown in this `sample `__. #) Dropped deprecated parameters cqqos and rowids from the :meth:`Connection.subscribe()` method. Use the qos parameter instead as shown in this `sample `__. Version 5.3 (March 2017) ------------------------ #) Added support for Python 3.6. #) Dropped support for Python versions earlier than 2.6. #) Dropped support for Oracle clients earlier than 11.2. #) Added support for :meth:`fetching implicit results` (available in Oracle 12.1) #) Added support for :attr:`Transaction Guard ` (available in Oracle 12.1). #) Added support for setting the :attr:`maximum lifetime ` of pool connections (available in Oracle 12.1). #) Added support for large row counts (larger than 2 ** 32, available in Oracle 12.1) #) Added support for :meth:`advanced queuing `. #) Added support for :meth:`scrollable cursors `. #) Added support for :attr:`edition based redefinition `. #) Added support for :meth:`creating `, modifying and binding user defined types and collections. #) Added support for creating, modifying and binding PL/SQL records and collections (available in Oracle 12.1). #) Added support for binding :data:`native integers `. #) Enabled statement caching. #) Removed deprecated variable attributes maxlength and allocelems. #) Corrected support for setting the encoding and nencoding parameters when :meth:`creating a connection ` and added support for setting these when creating a session pool. These can now be used instead of setting the environment variables NLS_LANG and NLS_NCHAR. #) Use None instead of 0 for items in the :attr:`Cursor.description` attribute that do not have any validity. #) Changed driver name to match informal driver name standard used by Oracle for other drivers. #) Add check for maximum of 10,000 parameters when calling a stored procedure or function in order to prevent a possible improper memory access from taking place. #) Removed -mno-cygwin compile flag since it is no longer used in newer versions of the gcc compiler for Cygwin. #) Simplified test suite by combining Python 2 and 3 scripts into one script and separated out 12.1 features into a single script. #) Updated samples to use code that works on both Python 2 and 3 #) Added support for pickling/unpickling error objects (`Issue #23 `__) #) Dropped support for callbacks on OCI functions. #) Removed deprecated types UNICODE, FIXED_UNICODE and LONG_UNICODE (use NCHAR, FIXED_NCHAR and LONG_NCHAR instead). #) Increased default array size to 100 (from 50) to match other drivers. #) Added support for setting the :attr:`~Connection.internal_name` and :attr:`~Connection.external_name` on the connection directly. The use of the twophase parameter is now deprecated. Applications should set the internal_name and external_name attributes directly to a value appropriate to the application. #) Added support for using application context when :meth:`creating a connection `. This should be used in preference to the module, action and clientinfo parameters which are now deprecated. #) Reworked database change notification and continuous query notification to more closely align with the PL/SQL implementation and prepare for sending notifications for AQ messages. The following changes were made: - added constant :data:`~cx_Oracle.SUBSCR_QOS_BEST_EFFORT` to replace deprecated constant SUBSCR_CQ_QOS_BEST_EFFORT - added constant :data:`~cx_Oracle.SUBSCR_QOS_QUERY` to replace deprecated constant SUBSCR_CQ_QOS_QUERY - added constant :data:`~cx_Oracle.SUBSCR_QOS_DEREG_NFY` to replace deprecated constant SUBSCR_QOS_PURGE_ON_NTFN - added constant :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS` to replace parameter rowids for method :meth:`Connection.subscribe()` - deprecated parameter cqqos for method :meth:`Connection.subscribe()`. The qos parameter should be used instead. - dropped constants SUBSCR_CQ_QOS_CLQRYCACHE, SUBSCR_QOS_HAREG, SUBSCR_QOS_MULTICBK, SUBSCR_QOS_PAYLOAD, SUBSCR_QOS_REPLICATE, and SUBSCR_QOS_SECURE since they were never actually used #) Deprecated use of the numbersAsStrings attribute on cursors. An output type handler should be used instead. Version 5.2.1 (January 2016) ---------------------------- #) Added support for Python 3.5. #) Removed password attribute from connection and session pool objects in order to promote best security practices (if stored in RAM in cleartext it can be read in process dumps, for example). For those who would like to retain this feature, a subclass of Connection could be used to store the password. #) Added optional parameter externalauth to SessionPool() which enables wallet based or other external authentication mechanisms to be used. #) Use the national character set encoding when required (when char set form is SQLCS_NCHAR); otherwise, the wrong encoding would be used if the environment variable NLS_NCHAR is set. #) Added support for binding boolean values to PL/SQL blocks and stored procedures (available in Oracle 12.1). Version 5.2 (June 2015) ----------------------- #) Added support for strings up to 32k characters (new in Oracle 12c). #) Added support for getting array DML row counts (new in Oracle 12c). #) Added support for fetching batch errors. #) Added support for LOB values larger than 4 GB. #) Added support for connections as SYSASM. #) Added support for building without any configuration changes to the machine when using instant client RPMs on Linux. #) Added types NCHAR, FIXED_NCHAR and LONG_NCHAR to replace the types UNICODE, FIXED_UNICODE and LONG_UNICODE (which are now deprecated). These types are available in Python 3 as well so they can be used to specify the use of NCHAR type fields when binding or using setinputsizes(). #) Fixed binding of booleans in Python 3.x. #) Test suite now sets NLS_LANG if not already set. #) Enhanced documentation for connection.action attribute and added note on cursor.parse() method to make clear that DDL statements are executed when parsed. #) Removed remaining remnants of support Oracle 9i. #) Added __version__ attribute to conform with PEP 396. #) Ensure that sessions are released to the pool when calling connection.close() (`Issue #2 `__) #) Fixed handling of datetime intervals (`Issue #7 `__) Version 5.1.3 (May 2014) ------------------------ #) Added support for Oracle 12c. #) Added support for Python 3.4. #) Added support for query result set change notification. Thanks to Glen Walker for the patch. #) Ensure that in Python 3.x that NCHAR and NVARCHAR2 and NCLOB columns are retrieved properly without conversion issues. Thanks to Joakim Andersson for pointing out the issue and the possible solution. #) Fix bug when an exception is caught and then another exception is raised while handling that exception in Python 3.x. Thanks to Boris Dzuba for pointing out the issue and providing a test case. #) Enhance performance returning integers between 10 and 18 digits on 64-bit platforms that support it. Thanks for Shai Berger for the initial patch. #) Fixed two memory leaks. #) Fix to stop current_schema from throwing a MemoryError on 64-bit platforms on occasion. Thanks to Andrew Horton for the fix. #) Class name of cursors changed to real name cx_Oracle.Cursor. Version 5.1.2 (July 2012) ------------------------- #) Added support for LONG_UNICODE which is a type used to handle long unicode strings. These are not explicitly supported in Oracle but can be used to bind to NCLOB, for example, without getting the error "unimplemented or unreasonable conversion requested". #) Set the row number in a cursor when executing PL/SQL blocks as requested by Robert Ritchie. #) Added support for setting the module, action and client_info attributes during connection so that logon triggers will see the supplied values, as requested by Rodney Barnett. Version 5.1.1 (October 2011) ---------------------------- #) Simplify management of threads for callbacks performed by database change notification and eliminate a crash that occurred under high load in certain situations. Thanks to Calvin S. for noting the issue and suggesting a solution and testing the patch. #) Force server detach on close so that the connection is completely closed and not just the session as before. #) Force use of OCI_UTF16ID for NCLOBs as using the default character set would result in ORA-03127 with Oracle 11.2.0.2 and UTF8 character set. #) Avoid attempting to clear temporary LOBs a second time when destroying the variable as in certain situations this results in spurious errors. #) Added additional parameter service_name to makedsn() which can be used to use the service_name rather than the SID in the DSN string that is generated. #) Fix cursor description in test suite to take into account the number of bytes per character. #) Added tests for NCLOBS to the test suite. #) Removed redundant code in setup.py for calculating the library path. Version 5.1 (March 2011) ------------------------ #) Remove support for UNICODE mode and permit Unicode to be passed through in everywhere a string may be passed in. This means that strings will be passed through to Oracle using the value of the NLS_LANG environment variable in Python 3.x as well. Doing this eliminated a bunch of problems that were discovered by using UNICODE mode and also removed an unnecessary restriction in Python 2.x that Unicode could not be used in connect strings or SQL statements, for example. #) Added support for creating an empty object variable via a named type, the first step to adding full object support. #) Added support for Python 3.2. #) Account for lib64 used on x86_64 systems. Thanks to Alex Wood for supplying the patch. #) Clear up potential problems when calling cursor.close() ahead of the cursor being freed by going out of scope. #) Avoid compilation difficulties on AIX5 as OCIPing does not appear to be available on that platform under Oracle 10g Release 2. Thanks to Pierre-Yves Fontaniere for the patch. #) Free temporary LOBs prior to each fetch in order to avoid leaking them. Thanks to Uwe Hoffmann for the initial patch. Version 5.0.4 (July 2010) ------------------------- #) Added support for Python 2.7. #) Added support for new parameter (port) for subscription() call which allows the client to specify the listening port for callback notifications from the database server. Thanks to Geoffrey Weber for the initial patch. #) Fixed compilation under Oracle 9i. #) Fixed a few error messages. Version 5.0.3 (February 2010) ----------------------------- #) Added support for 64-bit Windows. #) Added support for Python 3.1 and dropped support for Python 3.0. #) Added support for keyword parameters in cursor.callproc() and cursor.callfunc(). #) Added documentation for the UNICODE and FIXED_UNICODE variable types. #) Added extra link arguments required for Mac OS X as suggested by Jason Woodward. #) Added additional error codes to the list of error codes that raise OperationalError rather than DatabaseError. #) Fixed calculation of display size for strings with national database character sets that are not the default AL16UTF16. #) Moved the resetting of the setinputsizes flag before the binding takes place so that if an error takes place and a new statement is prepared subsequently, spurious errors will not occur. #) Fixed compilation with Oracle 10g Release 1. #) Tweaked documentation based on feedback from a number of people. #) Added support for running the test suite using "python setup.py test" #) Added support for setting the CLIENT_IDENTIFIER value in the v$session table for connections. #) Added exception when attempting to call executemany() with arrays which is not supported by the OCI. #) Fixed bug when converting from decimal would result in OCI-22062 because the locale decimal point was not a period. Thanks to Amaury Forgeot d'Arc for the solution to this problem. Version 5.0.2 (May 2009) ------------------------ #) Fix creation of temporary NCLOB values and the writing of NCLOB values in non Unicode mode. #) Re-enabled parsing of non select statements as requested by Roy Terrill. #) Implemented a parse error offset as requested by Catherine Devlin. #) Removed lib subdirectory when forcing RPATH now that the library directory is being calculated exactly in setup.py. #) Added an additional cast in order to support compiling by Microsoft Visual C++ 2008 as requested by Marco de Paoli. #) Added additional include directory to setup.py in order to support compiling by Microsoft Visual Studio was requested by Jason Coombs. #) Fixed a few documentation issues. Version 5.0.1 (February 2009) ----------------------------- #) Added support for database change notification available in Oracle 10g Release 2 and higher. #) Fix bug where NCLOB data would be corrupted upon retrieval (non Unicode mode) or would generate exception ORA-24806 (LOB form mismatch). Oracle insists upon differentiating between CLOB and NCLOB no matter which character set is being used for retrieval. #) Add new attributes size, bufferSize and numElements to variable objects, deprecating allocelems (replaced by numElements) and maxlength (replaced by bufferSize) #) Avoid increasing memory allocation for strings when using variable width character sets and increasing the number of elements in a variable during executemany(). #) Tweaked code in order to ensure that cx_Oracle can compile with Python 3.0.1. Version 5.0 (December 2008) --------------------------- #) Added support for Python 3.0 with much help from Amaury Forgeot d'Arc. #) Removed support for Python 2.3 and Oracle 8i. #) Added support for full unicode mode in Python 2.x where all strings are passed in and returned as unicode (module must be built in this mode) rather than encoded strings #) nchar and nvarchar columns now return unicode instead of encoded strings #) Added support for an output type handler and/or an input type handler to be specified at the connection and cursor levels. #) Added support for specifying both input and output converters for variables #) Added support for specifying the array size of variables that are created using the cursor.var() method #) Added support for events mode and database resident connection pooling (DRCP) in Oracle 11g. #) Added support for changing the password during construction of a new connection object as well as after the connection object has been created #) Added support for the interval day to second data type in Oracle, represented as datetime.timedelta objects in Python. #) Added support for getting and setting the current_schema attribute for a session #) Added support for proxy authentication in session pools as requested by Michael Wegrzynek (and thanks for the initial patch as well). #) Modified connection.prepare() to return a boolean indicating if a transaction was actually prepared in order to avoid the error ORA-24756 (transaction does not exist). #) Raise a cx_Oracle.Error instance rather than a string for column truncation errors as requested by Helge Tesdal. #) Fixed handling of environment handles in session pools in order to allow session pools to fetch objects without exceptions taking place. Version 4.4.1 (October 2008) ---------------------------- #) Make the bind variables and fetch variables accessible although they need to be treated carefully since they are used internally; support added for forward compatibility with version 5.x. #) Include the "cannot insert null value" in the list of errors that are treated as integrity errors as requested by Matt Boersma. #) Use a cx_Oracle.Error instance rather than a string to hold the error when truncation (ORA-1406) takes place as requested by Helge Tesdal. #) Added support for fixed char, old style varchar and timestamp attribute values in objects. #) Tweaked setup.py to check for the Oracle version up front rather than during the build in order to produce more meaningful errors and simplify the code. #) In setup.py added proper detection for the instant client on Mac OS X as recommended by Martijn Pieters. #) In setup.py, avoided resetting the extraLinkArgs on Mac OS X as doing so prevents simple modification where desired as expressed by Christian Zagrodnick. #) Added documentation on exception handling as requested by Andreas Mock, who also graciously provided an initial patch. #) Modified documentation indicating that the password attribute on connection objects can be written. #) Added documentation warning that parameters not passed in during subsequent executions of a statement will retain their original values as requested by Harald Armin Massa. #) Added comments indicating that an Oracle client is required since so many people find this surprising. #) Removed all references to Oracle 8i from the documentation and version 5.x will eliminate all vestiges of support for this version of the Oracle client. #) Added additional link arguments for Cygwin as requested by Rob Gillen. Version 4.4 (June 2008) ----------------------- #) Fix setup.py to handle the Oracle instant client and Oracle XE on both Linux and Windows as pointed out by many. Thanks also to the many people who also provided patches. #) Set the default array size to 50 instead of 1 as the DB API suggests because the performance difference is so drastic and many people have recommended that the default be changed. #) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS around each blocking call for LOBs as requested by Jason Conroy who also provided an initial patch and performed a number of tests that demonstrate the new code is much more responsive. #) Add support for acquiring cursor.description after a parse. #) Defer type assignment when performing executemany() until the last possible moment if the value being bound in is null as suggested by Dragos Dociu. #) When dropping a connection from the pool, ignore any errors that occur during the rollback; unfortunately, Oracle decides to commit data even when dropping a connection from the pool instead of rolling it back so the attempt still has to be made. #) Added support for setting CLIENT_DRIVER in V$SESSION_CONNECT_INFO in Oracle 11g and higher. #) Use cx_Oracle.InterfaceError rather than the builtin RuntimeError when unable to create the Oracle environment object as requested by Luke Mewburn since the error is specific to Oracle and someone attempting to catch any exception cannot simply use cx_Oracle.Error. #) Translated some error codes to OperationalError as requested by Matthew Harriger; translated if/elseif/else logic to switch statement to make it more readable and to allow for additional translation if desired. #) Transformed documentation to new format using restructured text. Thanks to Waldemar Osuch for contributing the initial draft of the new documentation. #) Allow the password to be overwritten by a new value as requested by Alex VanderWoude; this value is retained as a convenience to the user and not used by anything in the module; if changed externally it may be convenient to keep this copy up to date. #) Cygwin is on Windows so should be treated in the same way as noted by Matthew Cahn. #) Add support for using setuptools if so desired as requested by Shreya Bhatt. #) Specify that the version of Oracle 10 that is now primarily used is 10.2, not 10.1. Version 4.3.3 (October 2007) ---------------------------- #) Added method ping() on connections which can be used to test whether or not a connection is still active (available in Oracle 10g R2). #) Added method cx_Oracle.clientversion() which returns a 5-tuple giving the version of the client that is in use (available in Oracle 10g R2). #) Added methods startup() and shutdown() on connections which can be used to startup and shutdown databases (available in Oracle 10g R2). #) Added support for Oracle 11g. #) Added samples directory which contains a handful of scripts containing sample code for more advanced techniques. More will follow in future releases. #) Prevent error "ORA-24333: zero iteration count" when calling executemany() with zero rows as requested by Andreas Mock. #) Added methods __enter__() and __exit__() on connections to support using connections as context managers in Python 2.5 and higher. The context managed is the transaction state. Upon exit the transaction is either rolled back or committed depending on whether an exception took place or not. #) Make the search for the lib32 and lib64 directories automatic for all platforms. #) Tweak the setup configuration script to include all of the metadata and allow for building the module within another setup configuration script #) Include the Oracle version in addition to the Python version in the build directories that are created and in the names of the binary packages that are created. #) Remove unnecessary dependency on win32api to build module on Windows. Version 4.3.2 (August 2007) --------------------------- #) Added methods open(), close(), isopen() and getchunksize() in order to improve performance of reading/writing LOB values in chunks. #) Fixed support for native doubles and floats in Oracle 10g; added new type NATIVE_FLOAT to allow specification of a variable of that specific type where desired. Thanks to D.R. Boxhoorn for pointing out the fact that this was not working properly when the arraysize was anything other than 1. #) When calling connection.begin(), only create a new transaction handle if one is not already associated with the connection. Thanks to Andreas Mock for discovering this and for Amaury Forgeot d'Arc for diagnosing the problem and pointing the way to a solution. #) Added attribute cursor.rowfactory which allows a method to be called for each row that is returned; this is about 20% faster than calling the method in Python using the idiom [method(\*r) for r in cursor]. #) Attempt to locate an Oracle installation by looking at the PATH if the environment variable ORACLE_HOME is not set; this is of primary use on Windows where this variable should not normally be set. #) Added support for autocommit mode as requested by Ian Kelly. #) Added support for connection.stmtcachesize which allows for both reading and writing the size of the statement cache size. This parameter can make a huge difference with the length of time taken to prepare statements. Added support for setting the statement tag when preparing a statement. Both of these were requested by Bjorn Sandberg who also provided an initial patch. #) When copying the value of a variable, copy the return code as well. Version 4.3.1 (April 2007) -------------------------- #) Ensure that if the client buffer size exceeds 4000 bytes that the server buffer size does not as strings may only contain 4000 bytes; this allows handling of multibyte character sets on the server as well as the client. #) Added support for using buffer objects to populate binary data and made the Binary() constructor the buffer type as requested by Ken Mason. #) Fix potential crash when using full optimization with some compilers. Thanks to Aris Motas for noticing this and providing the initial patch and to Amaury Forgeot d'Arc for providing an even simpler solution. #) Pass the correct charset form in to the write call in order to support writing to national character set LOB values properly. Thanks to Ian Kelly for noticing this discrepancy. Version 4.3 (March 2007) ------------------------ #) Added preliminary support for fetching Oracle objects (SQL types) as requested by Kristof Beyls (who kindly provided an initial patch). Additional work needs to be done to support binding and updating objects but the basic structure is now in place. #) Added connection.maxBytesPerCharacter which indicates the maximum number of bytes each character can use; use this value to also determine the size of local buffers in order to handle discrepancies between the client character set and the server character set. Thanks to Andreas Mock for providing the initial patch and working with me to resolve this issue. #) Added support for querying native floats in Oracle 10g as requested by Danny Boxhoorn. #) Add support for temporary LOB variables created via PL/SQL instead of only directly by cx_Oracle; thanks to Henning von Bargen for discovering this problem. #) Added support for specifying variable types using the builtin types int, float, str and datetime.date which allows for finer control of what type of Python object is returned from cursor.callfunc() for example. #) Added support for passing booleans to callproc() and callfunc() as requested by Anana Aiyer. #) Fixed support for 64-bit environments in Python 2.5. #) Thanks to Filip Ballegeer and a number of his co-workers, an intermittent crash was tracked down; specifically, if a connection is closed, then the call to OCIStmtRelease() will free memory twice. Preventing the call when the connection is closed solves the problem. Version 4.2.1 (September 2006) ------------------------------ #) Added additional type (NCLOB) to handle CLOBs that use the national character set as requested by Chris Dunscombe. #) Added support for returning cursors from functions as requested by Daniel Steinmann. #) Added support for getting/setting the "get" mode on session pools as requested by Anand Aiyer. #) Added support for binding subclassed cursors. #) Fixed binding of decimal objects with absolute values less than 0.1. Version 4.2 (July 2006) ----------------------- #) Added support for parsing an Oracle statement as requested by Patrick Blackwill. #) Added support for BFILEs at the request of Matthew Cahn. #) Added support for binding decimal.Decimal objects to cursors. #) Added support for reading from NCLOBs as requested by Chris Dunscombe. #) Added connection attributes encoding and nencoding which return the IANA character set name for the character set and national character set in use by the client. #) Rework module initialization to use the techniques recommended by the Python documentation as one user was experiencing random segfaults due to the use of the module dictionary after the initialization was complete. #) Removed support for the OPT_Threading attribute. Use the threaded keyword when creating connections and session pools instead. #) Removed support for the OPT_NumbersAsStrings attribute. Use the numbersAsStrings attribute on cursors instead. #) Use type long rather than type int in order to support long integers on 64-bit machines as reported by Uwe Hoffmann. #) Add cursor attribute "bindarraysize" which is defaulted to 1 and is used to determine the size of the arrays created for bind variables. #) Added repr() methods to provide something a little more useful than the standard type name and memory address. #) Added keyword parameter support to the functions that imply such in the documentation as requested by Harald Armin Massa. #) Treat an empty dictionary passed through to cursor.execute() as keyword parameters the same as if no keyword parameters were specified at all, as requested by Fabien Grumelard. #) Fixed memory leak when a LOB read would fail. #) Set the LDFLAGS value in the environment rather than directly in the setup.py file in order to satisfy those who wish to enable the use of debugging symbols. #) Use __DATE__ and __TIME__ to determine the date and time of the build rather than passing it through directly. #) Use Oracle types and add casts to reduce warnings as requested by Amaury Forgeot d'Arc. #) Fixed typo in error message. Version 4.1.2 (December 2005) ----------------------------- #) Restore support of Oracle 9i features when using the Oracle 10g client. Version 4.1.1 (December 2005) ----------------------------- #) Add support for dropping a connection from a session pool. #) Add support for write only attributes "module", "action" and "clientinfo" which work only in Oracle 10g as requested by Egor Starostin. #) Add support for pickling database errors. #) Use the previously created bind variable as a template if available when creating a new variable of a larger size. Thanks to Ted Skolnick for the initial patch. #) Fixed tests to work properly in the Python 2.4 environment where dates and timestamps are different Python types. Thanks to Henning von Bargen for pointing this out. #) Added additional directories to search for include files and libraries in order to better support the Oracle 10g instant client. #) Set the internal fetch number to 0 in order to satisfy very picky source analysis tools as requested by Amaury Fogeot d'Arc. #) Improve the documentation for building and installing the module from source as some people are unaware of the standard methods for building Python modules using distutils. #) Added note in the documentation indicating that the arraysize attribute can drastically affect performance of queries since this seems to be a common misunderstanding of first time users of cx_Oracle. #) Add a comment indicating that on HP-UX Itanium with Oracle 10g the library ttsh10 must also be linked against. Thanks to Bernard Delmee for the information. Version 4.1 (January 2005) -------------------------- #) Fixed bug where subclasses of Cursor do not pass the connection in the constructor causing a segfault. #) DDL statements must be reparsed before execution as noted by Mihai Ibanescu. #) Add support for setting input sizes by position. #) Fixed problem with catching an exception during execute and then still attempting to perform a fetch afterwards as noted by Leith Parkin. #) Rename the types so that they can be pickled and unpickled. Thanks to Harri Pasanen for pointing out the problem. #) Handle invalid NLS_LANG setting properly (Oracle seems to like to provide a handle back even though it is invalid) and determine the number of bytes per character in order to allow for proper support in the future of multibyte and variable width character sets. #) Remove date checking from the native case since Python already checks that dates are valid; enhance error message when invalid dates are encountered so that additional processing can be done. #) Fix bug executing SQL using numeric parameter names with predefined variables (such as what takes place when calling stored procedures with out parameters). #) Add support for reading CLOB values using multibyte or variable length character sets. Version 4.1 beta 1 (September 2004) ----------------------------------- #) Added support for Python 2.4. In Python 2.4, the datetime module is used for both binding and fetching of date and timestamp data. In Python 2.3, objects from the datetime module can be bound but the internal datetime objects will be returned from queries. #) Added pickling support for LOB and datetime data. #) Fully qualified the table name that was missing in an alter table statement in the setup test script as noted by Marc Gehling. #) Added a section allowing for the setting of the RPATH linker directive in setup.py as requested by Iustin Pop. #) Added code to raise a programming error exception when an attempt is made to access a LOB locator variable in a subsequent fetch. #) The username, password and dsn (tnsentry) are stored on the connection object when specified, regardless of whether or not a standard connection takes place. #) Added additional module level constant called "LOB" as requested by Joseph Canedo. #) Changed exception type to IntegrityError for constraint violations as requested by Joseph Canedo. #) If scale and precision are not specified, an attempt is made to return a long integer as requested by Joseph Canedo. #) Added workaround for Oracle bug which returns an invalid handle when the prepare call fails. Thanks to alantam@hsbc.com for providing the code that demonstrated the problem. #) The cursor method arrayvar() will now accept the actual list so that it is not necessary to call cursor.arrayvar() followed immediately by var.setvalue(). #) Fixed bug where attempts to execute the statement "None" with bind variables would cause a segmentation fault. #) Added support for binding by position (paramstyle = "numeric"). #) Removed memory leak created by calls to OCIParamGet() which were not mirrored by calls to OCIDescriptorFree(). Thanks to Mihai Ibanescu for pointing this out and providing a patch. #) Added support for calling cursor.executemany() with statement None implying that the previously prepared statement ought to be executed. Thanks to Mihai Ibanescu for providing a patch. #) Added support for rebinding variables when a subsequent call to cursor.executemany() uses a different number of rows. Thanks to Mihai Ibanescu for supplying a patch. #) The microseconds are now displayed in datetime variables when nonzero similar to method used in the datetime module. #) Added support for binary_float and binary_double columns in Oracle 10g. Version 4.0.1 (February 2004) ----------------------------- #) Fixed bugs on 64-bit platforms that caused segmentation faults and bus errors in session pooling and determining the bind variables associated with a statement. #) Modified test suite so that 64-bit platforms are tested properly. #) Added missing commit statements in the test setup scripts. Thanks to Keith Lyon for pointing this out. #) Fix setup.py for Cygwin environments. Thanks to Doug Henderson for providing the necessary fix. #) Added support for compiling cx_Oracle without thread support. Thanks to Andre Reitz for pointing this out. #) Added support for a new keyword parameter called threaded on connections and session pools. This parameter defaults to False and indicates whether threaded mode ought to be used. It replaces the module level attribute OPT_Threading although examining the attribute will be retained until the next release at least. #) Added support for a new keyword parameter called twophase on connections. This parameter defaults to False and indicates whether support for two phase (distributed or global) transactions ought to be present. Note that support for distributed transactions is buggy when crossing major version boundaries (Oracle 8i to Oracle 9i for example). #) Ensure that the rowcount attribute is set properly when an exception is raised during execution. Thanks to Gary Aviv for pointing out this problem and its solution. Version 4.0 (December 2003) --------------------------- #) Added support for subclassing connections, cursors and session pools. The changes involved made it necessary to drop support for Python 2.1 and earlier although a branch exists in CVS to allow for support of Python 2.1 and earlier if needed. #) Connections and session pools can now be created with keyword parameters, not just sequential parameters. #) Queries now return integers whenever possible and long integers if the number will overflow a simple integer. Floats are only returned when it is known that the number is a floating point number or the integer conversion fails. #) Added initial support for user callbacks on OCI functions. See the documentation for more details. #) Add support for retrieving the bind variable names associated with a cursor with a new method bindnames(). #) Add support for temporary LOB variables. This means that setinputsizes() can be used with the values CLOB and BLOB to create these temporary LOB variables and allow for the equivalent of empty_clob() and empty_blob() since otherwise Oracle will treat empty strings as NULL values. #) Automatically switch to long strings when the data size exceeds the maximum string size that Oracle allows (4000 characters) and raise an error if an attempt is made to set a string variable to a size that it does not support. This avoids truncation errors as reported by Jon Franz. #) Add support for global (distributed) transactions and two phase commit. #) Force the NLS settings for the session so that test tables are populated correctly in all circumstances; problems were noted by Ralf Braun and Allan Poulsen. #) Display error messages using the environment handle when the error handle has not yet been created; this provides better error messages during this rather rare situation. #) Removed memory leak in callproc() that was reported by Todd Whiteman. #) Make consistent the calls to manipulate memory; otherwise segfaults can occur when the pymalloc option is used, as reported by Matt Hoskins. #) Force a rollback when a session is released back to the session pool. Apparently the connections are not as stateless as Oracle's documentation suggests and this makes the logic consistent with normal connections. #) Removed module method attach(). This can be replaced with a call to Connection(handle=) if needed. Version 3.1 (August 2003) ------------------------- #) Added support for connecting with SYSDBA and SYSOPER access which is needed for connecting as sys in Oracle 9i. #) Only check the dictionary size if the variable is not NULL; otherwise, an error takes place which is not caught or cleared; this eliminates a spurious "Objects/dictobject.c:1258: bad argument to internal function" in Python 2.3. #) Add support for session pooling. This is only support for Oracle 9i but is amazingly fast -- about 100 times faster than connecting. #) Add support for statement caching when pooling sessions, this reduces the parse time considerably. Unfortunately, the Oracle OCI does not allow this to be easily turned on for normal sessions. #) Add method trim() on CLOB and BLOB variables for trimming the size. #) Add support for externally identified users; to use this feature leave the username and password fields empty when connecting. #) Add method cancel() on connection objects to cancel long running queries. Note that this only works on non-Windows platforms. #) Add method callfunc() on cursor objects to allow calling a function without using an anonymous PL/SQL block. #) Added documentation on objects that were not documented. At this point all objects, methods and constants in cx_Oracle have been documented. #) Added support for timestamp columns in Oracle 9i. #) Added module level method makedsn() which creates a data source name given the host, port and SID. #) Added constant "buildtime" which is the time when the module was built as an additional means of identifying the build that is in use. #) Binding a value that is incompatible to the previous value that was bound (data types do not match or array size is larger) will now result in a new bind taking place. This is more consistent with the DB API although it does imply a performance penalty when used. Version 3.0a (June 2003) ------------------------ #) Fixed bug where zero length PL/SQL arrays were being mishandled #) Fixed support for the data type "float" in Oracle; added one to the display size to allow for the sign of the number, if necessary; changed the display size of unconstrained numbers to 127, which is the largest number that Oracle can handle #) Added support for retrieving the description of a bound cursor before fetching it #) Fixed a couple of build issues on Mac OS X, AIX and Solaris (64-bit) #) Modified documentation slightly based on comments from several people #) Included files in MANIFEST that are needed to generate the binaries #) Modified test suite to work within the test environment at Computronix as well as within the packages that are distributed Version 3.0 (March 2003) ------------------------ #) Removed support for connection to Oracle7 databases; it is entirely possible that it will still work but I no longer have any way of testing and Oracle has dropped any meaningful support for Oracle7 anyway #) Fetching of strings is now done with predefined memory areas rather than dynamic memory areas; dynamic fetching of strings was causing problems with Oracle 9i in some instances and databases using a different character set other than US ASCII #) Fixed bug where segfault would occur if the '/' character preceded the '@' character in a connect string #) Added two new cursor methods var() and arrayvar() in order to eliminate the need for setinputsizes() when defining PL/SQL arrays and as a generic method of acquiring bind variables directly when needed #) Fixed support for binding cursors and added support for fetching cursors (these are known as ref cursors in PL/SQL). #) Eliminated discrepancy between the array size used internally and the array size specified by the interface user; this was done earlier to avoid bus errors on 64-bit platforms but another way has been found to get around that issue and a number of people were getting confused because of the discrepancy #) Added support for the attribute "connection" on cursors, an optional DB API extension #) Added support for passing a dictionary as the second parameter for the cursor.execute() method in order to comply with the DB API more closely; the method of passing parameters with keyword parameters is still supported and is in fact preferred #) Added support for the attribute "statement" on cursors which is a reference to the last SQL statement prepared or executed #) Added support for passing any sequence to callproc() rather than just lists as before #) Fixed bug where segfault would occur if the array size was changed after the cursor was executed but before it was fetched #) Ignore array size when performing executemany() and use the length of the list of parameters instead #) Rollback when connection is closed or destroyed to follow DB API rather than use the Oracle default (which is commit) #) Added check for array size too large causing an integer overflow #) Added support for iterators for Python 2.2 and above #) Added test suite based on PyUnitTest #) Added documentation in HTML format similar to the documentation for the core Python library Version 2.5a (August 2002) -------------------------- #) Fix problem with Oracle 9i and retrieving strings; it seems that Oracle 9i uses the correct method for dynamic callback but Oracle 8i will not work with that method so an #ifdef was added to check for the existence of an Oracle 9i feature; thanks to Paul Denize for discovering this problem Version 2.5 (July 2002) ----------------------- #) Added flag OPT_NoOracle7 which, if set, assumes that connections are being made to Oracle8 or higher databases; this allows for eliminating the overhead in performing this check at connect time #) Added flag OPT_NumbersAsStrings which, if set, returns all numbers as strings rather than integers or floats; this flag is used when defined variables are created (during select statements only) #) Added flag OPT_Threading which, if set, uses OCI threading mode; there is a significant performance degradation in this mode (about 15-20%) but it does allow threads to share connections (threadsafety level 2 according to the Python Database API 2.0); note that in order to support this, Oracle 8i or higher is now required #) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS pairs where applicable to support threading during blocking OCI calls #) Added global method attach() to cx_Oracle to support attaching to an existing database handle (as provided by PowerBuilder, for example) #) Eliminated the cursor method fetchbinds() which was used for returning the list of bind variables after execution to get the values of out variables; the cursor method setinputsizes() was modified to return the list of bind variables and the cursor method execute() was modified to return the list of defined variables in the case of a select statement being executed; these variables have three methods available to them: getvalue([]) to get the value of a variable, setvalue(, ) to set its value and copy(, , ) to copy the value from a variable in a more efficient manner than setvalue(getvalue()) #) Implemented cursor method executemany() which expects a list of dictionaries for the parameters #) Implemented cursor method callproc() #) Added cursor method prepare() which parses (prepares) the statement for execution; subsequent execute() or executemany() calls can pass None as the statement which will imply use of the previously prepared statement; used for high performance only #) Added cursor method fetchraw() which will perform a raw fetch of the cursor returning the number of rows thus fetched; this is used to avoid the overhead of generating result sets; used for high performance only #) Added cursor method executemanyprepared() which is identical to the method executemany() except that it takes a single parameter which is the number of times to execute a previously prepared statement and it assumes that the bind variables already have their values set; used for high performance only #) Added support for rowid being returned in a select statement #) Added support for comparing dates returned by cx_Oracle #) Integrated patch from Andre Reitz to set the null ok flag in the description attribute of the cursor #) Integrated patch from Andre Reitz to setup.py to support compilation with Python 1.5 #) Integrated patch from Benjamin Kearns to setup.py to support compilation on Cygwin Version 2.4 (January 2002) -------------------------- #) String variables can now be made any length (previously restricted to the 64K limit imposed by Oracle for default binding); use the type cx_Oracle.LONG_STRING as the parameter to setinputsizes() for binding in string values larger than 4000 bytes. #) Raw and long raw columns are now supported; use the types cx_Oracle.BINARY and cx_Oracle.LONG_BINARY as the parameter to setinputsizes() for binding in values of these types. #) Functions DateFromTicks(), TimeFromTicks() and TimestampFromTicks() are now implemented. #) Function cursor.setoutputsize() implemented #) Added the ability to bind arrays as out parameters to procedures; use the format [cx_Oracle., ] as the input to the function setinputsizes() for binding arrays #) Discovered from the Oracle 8.1.6 version of the documentation of the OCI libraries, that the size of the memory location required for the precision variable is larger than the printed documentation says; this was causing a problem with the code on the Sun platform. #) Now support building RPMs for Linux. Version 2.3 (October 2001) -------------------------- #) Incremental performance enhancements (dealing with reusing cursors and bind handles) #) Ensured that arrays of integers with a single float in them are all treated as floats, as suggested by Martin Koch. #) Fixed code dealing with scale and precision for both defining a numeric variable and for providing the cursor description; this eliminates the problem of an underflow error (OCI-22054) when retrieving data with non-zero scale. Version 2.2 (July 2001) ----------------------- #) Upgraded thread safety to level 1 (according to the Python DB API 2.0) as an internal project required the ability to share the module between threads. #) Added ability to bind ref cursors to PL/SQL blocks as requested by Brad Powell. #) Added function write(Value, [Offset]) to LOB variables as requested by Matthias Kirst. #) Procedure execute() on Cursor objects now permits a value None for the statement which means that the previously prepared statement will be executed and any input sizes set earlier will be retained. This was done to improve the performance of scripts that execute one statement many times. #) Modified module global constants BINARY and DATETIME to point to the external representations of those types so that the expression type(var) == cx_Oracle.DATETIME will work as expected. #) Added global constant version to provide means of determining the current version of the module. #) Modified error checking routine to distinguish between an Oracle error and invalid handles. #) Added error checking to avoid setting the value of a bind variable to a value that it cannot support and raised an exception to indicate this fact. #) Added extra compile arguments for the AIX platform as suggested by Jehwan Ryu. #) Added section to the README to indicate the method for a binary installation as suggested by Steve Holden. #) Added simple usage example as requested by many people. #) Added HISTORY file to the distribution. python-cx_Oracle-8.3.0/doc/src/user_guide/000077500000000000000000000000001414105416400204275ustar00rootroot00000000000000python-cx_Oracle-8.3.0/doc/src/user_guide/aq.rst000066400000000000000000000135101414105416400215620ustar00rootroot00000000000000.. _aqusermanual: **************************** Oracle Advanced Queuing (AQ) **************************** `Oracle Advanced Queuing `__ is a highly configurable and scalable messaging feature of Oracle Database. It has interfaces in various languages, letting you integrate multiple tools in your architecture. cx_Oracle 7.2 introduced an updated interface for Oracle Advanced Queuing. There are Advanced Queuing examples in the `GitHub examples `__ directory. Creating a Queue ================ Before being used, queues need to be created in the database, for example in SQL*Plus: .. code-block:: sql begin dbms_aqadm.create_queue_table('MY_QUEUE_TABLE', 'RAW'); dbms_aqadm.create_queue('DEMO_RAW_QUEUE', 'MY_QUEUE_TABLE'); dbms_aqadm.start_queue('DEMO_RAW_QUEUE'); end; / This examples creates a RAW queue suitable for sending string or raw bytes messages. Enqueuing Messages ================== To send messages in Python you connect and get a :ref:`queue `. The queue can be used for enqueuing, dequeuing, or both as needed. .. code-block:: python queue = connection.queue("DEMO_RAW_QUEUE") Now messages can be queued using :meth:`~Queue.enqone()`. To send three messages: .. code-block:: python PAYLOAD_DATA = [ "The first message", "The second message", "The third message" ] for data in PAYLOAD_DATA: queue.enqone(connection.msgproperties(payload=data)) connection.commit() Since the queue sending the messages is a RAW queue, the strings in this example will be internally encoded to bytes using :attr:`Connection.encoding` before being enqueued. Dequeuing Messages ================== Dequeuing is performed similarly. To dequeue a message call the method :meth:`~Queue.deqone()` as shown. Note that if the message is expected to be a string, the bytes must be decoded using :attr:`Connection.encoding`. .. code-block:: python queue = connection.queue("DEMO_RAW_QUEUE") msg = queue.deqOne() connection.commit() print(msg.payload.decode(connection.encoding)) Using Object Queues =================== Named Oracle objects can be enqueued and dequeued as well. Given an object type called ``UDT_BOOK``: .. code-block:: sql CREATE OR REPLACE TYPE udt_book AS OBJECT ( Title VARCHAR2(100), Authors VARCHAR2(100), Price NUMBER(5,2) ); / And a queue that accepts this type: .. code-block:: sql begin dbms_aqadm.create_queue_table('BOOK_QUEUE_TAB', 'UDT_BOOK'); dbms_aqadm.create_queue('DEMO_BOOK_QUEUE', 'BOOK_QUEUE_TAB'); dbms_aqadm.start_queue('DEMO_BOOK_QUEUE'); end; / You can queue messages: .. code-block:: python book_type = connection.gettype("UDT_BOOK") queue = connection.queue("DEMO_BOOK_QUEUE", book_type) book = book_type.newobject() book.TITLE = "Quick Brown Fox" book.AUTHORS = "The Dog" book.PRICE = 123 queue.enqone(connection.msgproperties(payload=book)) connection.commit() Dequeuing is done like this: .. code-block:: python book_type = connection.gettype("UDT_BOOK") queue = connection.queue("DEMO_BOOK_QUEUE", book_type) msg = queue.deqone() connection.commit() print(msg.payload.TITLE) # will print Quick Brown Fox Changing Queue and Message Options ================================== Refer to the :ref:`cx_Oracle AQ API ` and `Oracle Advanced Queuing documentation `__ for details on all of the enqueue and dequeue options available. Enqueue options can be set. For example, to make it so that an explicit call to :meth:`~Connection.commit()` on the connection is not needed to commit messages: .. code-block:: python queue = connection.queue("DEMO_RAW_QUEUE") queue.enqoptions.visibility = cx_Oracle.ENQ_IMMEDIATE Dequeue options can also be set. For example, to specify not to block on dequeuing if no messages are available: .. code-block:: python queue = connection.queue("DEMO_RAW_QUEUE") queue.deqoptions.wait = cx_Oracle.DEQ_NO_WAIT Message properties can be set when enqueuing. For example, to set an expiration of 60 seconds on a message: .. code-block:: python queue.enqone(connection.msgproperties(payload="Message", expiration=60)) This means that if no dequeue operation occurs within 60 seconds that the message will be dropped from the queue. Bulk Enqueue and Dequeue ======================== The :meth:`~Queue.enqmany()` and :meth:`~Queue.deqmany()` methods can be used for efficient bulk message handling. :meth:`~Queue.enqmany()` is similar to :meth:`~Queue.enqone()` but accepts an array of messages: .. code-block:: python messages = [ "The first message", "The second message", "The third message", ] queue = connection.queue("DEMO_RAW_QUEUE") queue.enqmany(connection.msgproperties(payload=m) for m in messages) connection.commit() .. warning:: Calling :meth:`~Queue.enqmany()` in parallel on different connections acquired from the same pool may fail due to Oracle bug 29928074. Ensure that this function is not run in parallel, use standalone connections or connections from different pools, or make multiple calls to :meth:`~Queue.enqone()` instead. The function :meth:`~Queue.deqmany()` call is not affected. To dequeue multiple messages at one time, use :meth:`~Queue.deqmany()`. This takes an argument specifying the maximum number of messages to dequeue at one time: .. code-block:: python for m in queue.deqmany(10): print(m.payload.decode(connection.encoding)) Depending on the queue properties and the number of messages available to dequeue, this code will print out from zero to ten messages. python-cx_Oracle-8.3.0/doc/src/user_guide/batch_statement.rst000066400000000000000000000255331414105416400243360ustar00rootroot00000000000000.. _batchstmnt: ****************************************** Batch Statement Execution and Bulk Loading ****************************************** Inserting or updating multiple rows can be performed efficiently with :meth:`Cursor.executemany()`, making it easy to work with large data sets with cx_Oracle. This method can significantly outperform repeated calls to :meth:`Cursor.execute()` by reducing network transfer costs and database overheads. The :meth:`~Cursor.executemany()` method can also be used to execute PL/SQL statements multiple times at once. There are examples in the `GitHub examples `__ directory. The following tables will be used in the samples that follow: .. code-block:: sql create table ParentTable ( ParentId number(9) not null, Description varchar2(60) not null, constraint ParentTable_pk primary key (ParentId) ); create table ChildTable ( ChildId number(9) not null, ParentId number(9) not null, Description varchar2(60) not null, constraint ChildTable_pk primary key (ChildId), constraint ChildTable_fk foreign key (ParentId) references ParentTable ); Batch Execution of SQL ====================== The following example inserts five rows into the table ``ParentTable``: .. code-block:: python data = [ (10, 'Parent 10'), (20, 'Parent 20'), (30, 'Parent 30'), (40, 'Parent 40'), (50, 'Parent 50') ] cursor.executemany("insert into ParentTable values (:1, :2)", data) This code requires only one :ref:`round-trip ` from the client to the database instead of the five round-trips that would be required for repeated calls to :meth:`~Cursor.execute()`. For very large data sets there may be an external buffer or network limits to how many rows can be processed, so repeated calls to ``executemany()`` may be required. The limits are based on both the number of rows being processed as well as the "size" of each row that is being processed. Repeated calls to :meth:`~Cursor.executemany()` are still better than repeated calls to :meth:`~Cursor.execute()`. Batch Execution of PL/SQL ========================= PL/SQL functions and procedures and anonymous PL/SQL blocks can also be called using :meth:`~Cursor.executemany()` in order to improve performance. For example: .. code-block:: python data = [ (10, 'Parent 10'), (20, 'Parent 20'), (30, 'Parent 30'), (40, 'Parent 40'), (50, 'Parent 50') ] cursor.executemany("begin mypkg.create_parent(:1, :2); end;", data) Note that the ``batcherrors`` parameter (discussed below) cannot be used with PL/SQL block execution. Handling Data Errors ==================== Large datasets may contain some invalid data. When using batch execution as discussed above, the entire batch will be discarded if a single error is detected, potentially eliminating the performance benefits of batch execution and increasing the complexity of the code required to handle those errors. If the parameter ``batchErrors`` is set to the value ``True`` when calling :meth:`~Cursor.executemany()`, however, processing will continue even if there are data errors in some rows, and the rows containing errors can be examined afterwards to determine what course the application should take. Note that if any errors are detected, a transaction will be started but not committed, even if :attr:`Connection.autocommit` is set to ``True``. After examining the errors and deciding what to do with them, the application needs to explicitly commit or roll back the transaction with :meth:`Connection.commit()` or :meth:`Connection.rollback()`, as needed. This example shows how data errors can be identified: .. code-block:: python data = [ (60, 'Parent 60'), (70, 'Parent 70'), (70, 'Parent 70 (duplicate)'), (80, 'Parent 80'), (80, 'Parent 80 (duplicate)'), (90, 'Parent 90') ] cursor.executemany("insert into ParentTable values (:1, :2)", data, batcherrors=True) for error in cursor.getbatcherrors(): print("Error", error.message, "at row offset", error.offset) The output is:: Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 2 Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 4 The row offset is the index into the array of the data that could not be inserted due to errors. The application could choose to commit or rollback the other rows that were successfully inserted. Alternatively, it could correct the data for the two invalid rows and attempt to insert them again before committing. Identifying Affected Rows ========================= When executing a DML statement using :meth:`~Cursor.execute()`, the number of rows affected can be examined by looking at the attribute :attr:`~Cursor.rowcount`. When performing batch executing with :meth:`Cursor.executemany()`, however, the row count will return the *total* number of rows that were affected. If you want to know the total number of rows affected by each row of data that is bound you must set the parameter ``arraydmlrowcounts`` to ``True``, as shown: .. code-block:: python parent_ids_to_delete = [20, 30, 50] cursor.executemany("delete from ChildTable where ParentId = :1", [(i,) for i in parent_ids_to_delete], arraydmlrowcounts=True) row_counts = cursor.getarraydmlrowcounts() for parent_id, count in zip(parent_ids_to_delete, row_counts): print("Parent ID:", parent_id, "deleted", count, "rows.") Using the data found in the `GitHub samples `__ the output is as follows:: Parent ID: 20 deleted 3 rows. Parent ID: 30 deleted 2 rows. Parent ID: 50 deleted 4 rows. DML RETURNING ============= DML statements like INSERT, UPDATE, DELETE and MERGE can return values by using the DML RETURNING syntax. A bind variable can be created to accept this data. See :ref:`bind` for more information. If, instead of merely deleting the rows as shown in the previous example, you also wanted to know some information about each of the rows that were deleted, you could use the following code: .. code-block:: python parent_ids_to_delete = [20, 30, 50] child_id_var = cursor.var(int, arraysize=len(parent_ids_to_delete)) cursor.setinputsizes(None, child_id_var) cursor.executemany(""" delete from ChildTable where ParentId = :1 returning ChildId into :2""", [(i,) for i in parent_ids_to_delete]) for ix, parent_id in enumerate(parent_ids_to_delete): print("Child IDs deleted for parent ID", parent_id, "are", child_id_var.getvalue(ix)) The output would then be:: Child IDs deleted for parent ID 20 are [1002, 1003, 1004] Child IDs deleted for parent ID 30 are [1005, 1006] Child IDs deleted for parent ID 50 are [1012, 1013, 1014, 1015] Note that the bind variable created to accept the returned data must have an arraysize large enough to hold data for each row that is processed. Also, the call to :meth:`Cursor.setinputsizes()` binds this variable immediately so that it does not have to be passed in each row of data. Predefining Memory Areas ======================== When multiple rows of data are being processed there is the possibility that the data is not uniform in type and size. In such cases, cx_Oracle makes some effort to accommodate such differences. Type determination for each column is deferred until a value that is not ``None`` is found in the column's data. If all values in a particular column are ``None``, then cx_Oracle assumes the type is a string and has a length of 1. cx_Oracle will also adjust the size of the buffers used to store strings and bytes when a longer value is encountered in the data. These sorts of operations incur overhead as memory has to be reallocated and data copied. To eliminate this overhead, using :meth:`~Cursor.setinputsizes()` tells cx_Oracle about the type and size of the data that is going to be used. Consider the following code: .. code-block:: python data = [ (110, "Parent 110"), (2000, "Parent 2000"), (30000, "Parent 30000"), (400000, "Parent 400000"), (5000000, "Parent 5000000") ] cursor.setinputsizes(None, 20) cursor.executemany(""" insert into ParentTable (ParentId, Description) values (:1, :2)""", data) In this example, without the call to :meth:`~Cursor.setinputsizes()`, cx_Oracle would perform five allocations of increasing size as it discovered each new, longer string. However ``cursor.setinputsizes(None, 20)`` tells cx_Oracle that the maximum size of the strings that will be processed is 20 characters. Since cx_Oracle allocates memory for each row based on this value, it is best not to oversize it. The first parameter of ``None`` tells cx_Oracle that its default processing will be sufficient. Loading CSV Files into Oracle Database ====================================== The :meth:`Cursor.executemany()` method and Python's `csv module `__ can be used to efficiently load CSV (Comma Separated Values) files. For example, consider the file ``data.csv``:: 101,Abel 154,Baker 132,Charlie 199,Delta . . . And the schema: .. code-block:: sql create table test (id number, name varchar2(25)); Data loading can be done in batches of records since the number of records may prevent all data being inserted at once: .. code-block:: python import cx_Oracle import csv # Predefine the memory areas to match the table definition. # This can improve performance by avoiding memory reallocations. # Here, one parameter is passed for each of the columns. # "None" is used for the ID column, since the size of NUMBER isn't # variable. The "25" matches the maximum expected data size for the # NAME column cursor.setinputsizes(None, 25) # Adjust the number of rows to be inserted in each iteration # to meet your memory and performance requirements batch_size = 10000 with open('testsp.csv', 'r') as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') sql = "insert into test (id,name) values (:1, :2)" data = [] for line in csv_reader: data.append((line[0], line[1])) if len(data) % batch_size == 0: cursor.executemany(sql, data) data = [] if data: cursor.executemany(sql, data) con.commit() Depending on data sizes and business requirements, database changes such as temporarily disabling redo logging on the table, or disabling indexes may also be beneficial. python-cx_Oracle-8.3.0/doc/src/user_guide/bind.rst000066400000000000000000000661671414105416400221150ustar00rootroot00000000000000.. _bind: ******************** Using Bind Variables ******************** SQL and PL/SQL statements that pass data to and from Oracle Database should use placeholders in SQL and PL/SQL statements that mark where data is supplied or returned. These placeholders are referred to as bind variables or bind parameters A bind variable is a colon-prefixed identifier or numeral. For example, there are two bind variables (``dept_id`` and ``dept_name``) in this SQL statement: .. code-block:: python sql = """insert into departments (department_id, department_name) values (:dept_id, :dept_name)""" cursor.execute(sql, [280, "Facility"]) Using bind variables is important for scalability and security. They help avoid SQL Injection security problems because data is never treated as part of an executable statement. Never concatenate or interpolate user data into SQL statements: .. code-block:: python did = 280 dnm = "Facility" # !! Never do this !! sql = f"""insert into departments (department_id, department_name) values ({did}, '{dnm}')""" cursor.execute(sql) Bind variables reduce parsing and execution costs when statements are executed more than once with different data values. If you do not use bind variables, Oracle must reparse and cache multiple statements. When using bind variables, Oracle Database may be able to reuse the statement execution plan and context. Bind variables can be used to substitute data, but cannot be used to substitute the text of the statement. You cannot, for example, use a bind variable where a column name or a table name is required. Bind variables also cannot be used in Data Definition Language (DDL) statements, such as CREATE TABLE or ALTER statements. Binding By Name or Position =========================== Binding can be done by name or by position. A named bind is performed when the bind variables in a statement are associated with a name. For example: .. code-block:: python cursor.execute(""" insert into departments (department_id, department_name) values (:dept_id, :dept_name)""", dept_id=280, dept_name="Facility") # alternatively, the parameters can be passed as a dictionary instead of as # keyword parameters data = dict(dept_id=280, dept_name="Facility") cursor.execute(""" insert into departments (department_id, department_name) values (:dept_id, :dept_name)""", data) In the above example, the keyword parameter names or the keys of the dictionary must match the bind variable names. The advantages of this approach are that the location of the bind variables in the statement is not important, the names can be meaningful and the names can be repeated while still only supplying the value once. A positional bind is performed when a list of bind values are passed to the execute() call. For example: .. code-block:: python cursor.execute(""" insert into departments (department_id, department_name) values (:dept_id, :dept_name)""", [280, "Facility"]) Note that for SQL statements, the order of the bind values must exactly match the order of each bind variable and duplicated names must have their values repeated. For PL/SQL statements, however, the order of the bind values must exactly match the order of each **unique** bind variable found in the PL/SQL block and values should not be repeated. In order to avoid this difference, binding by name is recommended when bind variable names are repeated. Bind Direction ============== The caller can supply data to the database (IN), the database can return data to the caller (OUT) or the caller can supply initial data to the database and the database can supply the modified data back to the caller (IN/OUT). This is known as the bind direction. The examples shown above have all supplied data to the database and are therefore classified as IN bind variables. In order to have the database return data to the caller, a variable must be created. This is done by calling the method :func:`Cursor.var()`, which identifies the type of data that will be found in that bind variable and its maximum size among other things. Here is an example showing how to use OUT binds. It calculates the sum of the integers 8 and 7 and stores the result in an OUT bind variable of type integer: .. code-block:: python out_val = cursor.var(int) cursor.execute(""" begin :out_val := :in_bind_var1 + :in_bind_var2; end;""", out_val=out_val, in_bind_var1=8, in_bind_var2=7) print(out_val.getvalue()) # will print 15 If instead of simply getting data back you wish to supply an initial value to the database, you can set the variable's initial value. This example is the same as the previous one but it sets the initial value first: .. code-block:: python in_out_var = cursor.var(int) in_out_var.setvalue(0, 25) cursor.execute(""" begin :in_out_bind_var := :in_out_bind_var + :in_bind_var1 + :in_bind_var2; end;""", in_out_bind_var=in_out_var, in_bind_var1=8, in_bind_var2=7) print(in_out_var.getvalue()) # will print 40 When binding data to parameters of PL/SQL procedures that are declared as OUT parameters, it is worth noting that any value that is set in the bind variable will be ignored. In addition, any parameters declared as IN/OUT that do not have a value set will start out with a value of ``null``. Binding Null Values =================== In cx_Oracle, null values are represented by the Python singleton ``None``. For example: .. code-block:: python cursor.execute(""" insert into departments (department_id, department_name) values (:dept_id, :dept_name)""", dept_id=280, dept_name=None) In this specific case, because the ``DEPARTMENT_NAME`` column is defined as a ``NOT NULL`` column, an error will occur:: cx_Oracle.IntegrityError: ORA-01400: cannot insert NULL into ("HR"."DEPARTMENTS"."DEPARTMENT_NAME") If this value is bound directly, cx_Oracle assumes it to be a string (equivalent to a VARCHAR2 column). If you need to use a different Oracle type you will need to make a call to :func:`Cursor.setinputsizes()` or create a bind variable with the correct type by calling :func:`Cursor.var()`. Binding ROWID Values ==================== The pseudo-column ``ROWID`` uniquely identifies a row within a table. In cx_Oracle, ROWID values are represented as strings. The example below shows fetching a row and then updating that row by binding its rowid: .. code-block:: python # fetch the row cursor.execute(""" select rowid, manager_id from departments where department_id = :dept_id""", dept_id=280) rowid, manager_id = cursor.fetchone() # update the row by binding ROWID cursor.execute(""" update departments set manager_id = :manager_id where rowid = :rid""", manager_id=205, rid=rowid) DML RETURNING Bind Variables ============================ When a RETURNING clause is used with a DML statement like UPDATE, INSERT, or DELETE, the values are returned to the application through the use of OUT bind variables. Consider the following example: .. code-block:: python # The RETURNING INTO bind variable is a string dept_name = cursor.var(str) cursor.execute(""" update departments set location_id = :loc_id where department_id = :dept_id returning department_name into :dept_name""", loc_id=1700, dept_id=50, dept_name=dept_name) print(dept_name.getvalue()) # will print ['Shipping'] In the above example, since the WHERE clause matches only one row, the output contains a single item in the list. If the WHERE clause matched multiple rows, however, the output would contain as many items as there were rows that were updated. No duplicate binds are allowed in a DML statement with a RETURNING clause, and no duplication is allowed between bind variables in the DML section and the RETURNING section of the statement. LOB Bind Variables ================== Database CLOBs, NCLOBS, BLOBs and BFILEs can be bound with types :attr:`cx_Oracle.DB_TYPE_CLOB`, :attr:`cx_Oracle.DB_TYPE_NCLOB`, :attr:`cx_Oracle.DB_TYPE_BLOB` and :attr:`cx_Oracle.DB_TYPE_BFILE` respectively. LOBs fetched from the database or created with :meth:`Connection.createlob()` can also be bound. LOBs may represent Oracle Database persistent LOBs (those stored in tables) or temporary LOBs (such as those created with :meth:`Connection.createlob()` or returned by some SQL and PL/SQL operations). LOBs can be used as IN, OUT or IN/OUT bind variables. See :ref:`lobdata` for examples. .. _refcur: REF CURSOR Bind Variables ========================= cx_Oracle provides the ability to bind and define PL/SQL REF cursors. As an example, consider the PL/SQL procedure: .. code-block:: sql CREATE OR REPLACE PROCEDURE find_employees ( p_query IN VARCHAR2, p_results OUT SYS_REFCURSOR ) AS BEGIN OPEN p_results FOR SELECT employee_id, first_name, last_name FROM employees WHERE UPPER(first_name || ' ' || last_name || ' ' || email) LIKE '%' || UPPER(p_query) || '%'; END; / A newly opened cursor can be bound to the REF CURSOR parameter, as shown in the following Python code. After the PL/SQL procedure has been called with :meth:`Cursor.callproc()`, the cursor can then be fetched just like any other cursor which had executed a SQL query: .. code-block:: python ref_cursor = connection.cursor() cursor.callproc("find_employees", ['Smith', ref_cursor]) for row in ref_cursor: print(row) With Oracle's `sample HR schema `__ there are two employees with the last name 'Smith' so the result is:: (159, 'Lindsey', 'Smith') (171, 'William', 'Smith') To return a REF CURSOR from a PL/SQL function, use ``cx_Oracle.DB_TYPE_CURSOR`` for the return type of :meth:`Cursor.callfunc()`: .. code-block:: python ref_cursor = cursor.callfunc('example_package.f_get_cursor', cx_Oracle.DB_TYPE_CURSOR) for row in ref_cursor: print(row) See :ref:`tuning` for information on how to tune REF CURSORS. Binding PL/SQL Collections ========================== PL/SQL Collections like Associative Arrays can be bound as IN, OUT, and IN/OUT variables. When binding IN values, an array can be passed directly as shown in this example, which sums up the lengths of all of the strings in the provided array. First the PL/SQL package definition: .. code-block:: sql create or replace package mypkg as type udt_StringList is table of varchar2(100) index by binary_integer; function DemoCollectionIn ( a_Values udt_StringList ) return number; end; / create or replace package body mypkg as function DemoCollectionIn ( a_Values udt_StringList ) return number is t_ReturnValue number := 0; begin for i in 1..a_Values.count loop t_ReturnValue := t_ReturnValue + length(a_Values(i)); end loop; return t_ReturnValue; end; end; / Then the Python code: .. code-block:: python values = ["String One", "String Two", "String Three"] return_val = cursor.callfunc("mypkg.DemoCollectionIn", int, [values]) print(return_val) # will print 32 In order get values back from the database, a bind variable must be created using :meth:`Cursor.arrayvar()`. The first parameter to this method is a Python type that cx_Oracle knows how to handle or one of the cx_Oracle :ref:`types`. The second parameter is the maximum number of elements that the array can hold or an array providing the value (and indirectly the maximum length). The final parameter is optional and only used for strings and bytes. It identifies the maximum length of the strings and bytes that can be stored in the array. If not specified, the length defaults to 4000 bytes. Consider the following PL/SQL package: .. code-block:: sql create or replace package mypkg as type udt_StringList is table of varchar2(100) index by binary_integer; procedure DemoCollectionOut ( a_NumElements number, a_Values out nocopy udt_StringList ); procedure DemoCollectionInOut ( a_Values in out nocopy udt_StringList ); end; / create or replace package body mypkg as procedure DemoCollectionOut ( a_NumElements number, a_Values out nocopy udt_StringList ) is begin for i in 1..a_NumElements loop a_Values(i) := 'Demo out element #' || to_char(i); end loop; end; procedure DemoCollectionInOut ( a_Values in out nocopy udt_StringList ) is begin for i in 1..a_Values.count loop a_Values(i) := 'Converted element #' || to_char(i) || ' originally had length ' || length(a_Values(i)); end loop; end; end; / The Python code to process an OUT collection would look as follows. Note the call to :meth:`Cursor.arrayvar()` which creates space for an array of strings. Each string would permit up to 100 bytes and only 10 strings would be permitted. If the PL/SQL block exceeds the maximum number of strings allowed the error ``ORA-06513: PL/SQL: index for PL/SQL table out of range for host language array`` would be raised. .. code-block:: python out_array_var = cursor.arrayvar(str, 10, 100) cursor.callproc("mypkg.DemoCollectionOut", [5, out_array_var]) for val in out_array_var.getvalue(): print(val) This would produce the following output:: Demo out element #1 Demo out element #2 Demo out element #3 Demo out element #4 Demo out element #5 The Python code to process an IN/OUT collections is similar. Note the different call to :meth:`Cursor.arrayvar()` which creates space for an array of strings, but uses an array to determine both the maximum length of the array and its initial value. .. code-block:: python in_values = ["String One", "String Two", "String Three", "String Four"] in_out_array_var = cursor.arrayvar(str, in_values) cursor.callproc("mypkg.DemoCollectionInOut", [in_out_array_var]) for val in in_out_array_var.getvalue(): print(val) This would produce the following output:: Converted element #1 originally had length 10 Converted element #2 originally had length 10 Converted element #3 originally had length 12 Converted element #4 originally had length 11 If an array variable needs to have an initial value but also needs to allow for more elements than the initial value contains, the following code can be used instead: .. code-block:: python in_out_array_var = cursor.arrayvar(str, 10, 100) in_out_array_var.setvalue(0, ["String One", "String Two"]) All of the collections that have been bound in preceding examples have used contiguous array elements. If an associative array with sparse array elements is needed, a different approach is required. Consider the following PL/SQL code: .. code-block:: sql create or replace package mypkg as type udt_StringList is table of varchar2(100) index by binary_integer; procedure DemoCollectionOut ( a_Value out nocopy udt_StringList ); end; / create or replace package body mypkg as procedure DemoCollectionOut ( a_Value out nocopy udt_StringList ) is begin a_Value(-1048576) := 'First element'; a_Value(-576) := 'Second element'; a_Value(284) := 'Third element'; a_Value(8388608) := 'Fourth element'; end; end; / Note that the collection element indices are separated by large values. The technique used above would fail with the exception ``ORA-06513: PL/SQL: index for PL/SQL table out of range for host language array``. The code required to process this collection looks like this instead: .. code-block:: python collection_type = connection.gettype("MYPKG.UDT_STRINGLIST") collection = collection_type.newobject() cursor.callproc("mypkg.DemoCollectionOut", [collection]) print(collection.aslist()) This produces the output:: ['First element', 'Second element', 'Third element', 'Fourth element'] Note the use of :meth:`Object.aslist()` which returns the collection element values in index order as a simple Python list. The indices themselves are lost in this approach. Starting from cx_Oracle 7.0, the associative array can be turned into a Python dictionary using :meth:`Object.asdict()`. If that value was printed in the previous example instead, the output would be:: {-1048576: 'First element', -576: 'Second element', 284: 'Third element', 8388608: 'Fourth element'} If the elements need to be traversed in index order, the methods :meth:`Object.first()` and :meth:`Object.next()` can be used. The method :meth:`Object.getelement()` can be used to acquire the element at a particular index. This is shown in the following code: .. code-block:: python ix = collection.first() while ix is not None: print(ix, "->", collection.getelement(ix)) ix = collection.next(ix) This produces the output:: -1048576 -> First element -576 -> Second element 284 -> Third element 8388608 -> Fourth element Similarly, the elements can be traversed in reverse index order using the methods :meth:`Object.last()` and :meth:`Object.prev()` as shown in the following code: .. code-block:: python ix = collection.last() while ix is not None: print(ix, "->", collection.getelement(ix)) ix = collection.prev(ix) This produces the output:: 8388608 -> Fourth element 284 -> Third element -576 -> Second element -1048576 -> First element Binding PL/SQL Records ====================== PL/SQL record type objects can also be bound for IN, OUT and IN/OUT bind variables. For example: .. code-block:: sql create or replace package mypkg as type udt_DemoRecord is record ( NumberValue number, StringValue varchar2(30), DateValue date, BooleanValue boolean ); procedure DemoRecordsInOut ( a_Value in out nocopy udt_DemoRecord ); end; / create or replace package body mypkg as procedure DemoRecordsInOut ( a_Value in out nocopy udt_DemoRecord ) is begin a_Value.NumberValue := a_Value.NumberValue * 2; a_Value.StringValue := a_Value.StringValue || ' (Modified)'; a_Value.DateValue := a_Value.DateValue + 5; a_Value.BooleanValue := not a_Value.BooleanValue; end; end; / Then this Python code can be used to call the stored procedure which will update the record: .. code-block:: python # create and populate a record record_type = connection.gettype("MYPKG.UDT_DEMORECORD") record = record_type.newobject() record.NUMBERVALUE = 6 record.STRINGVALUE = "Test String" record.DATEVALUE = datetime.datetime(2016, 5, 28) record.BOOLEANVALUE = False # show the original values print("NUMBERVALUE ->", record.NUMBERVALUE) print("STRINGVALUE ->", record.STRINGVALUE) print("DATEVALUE ->", record.DATEVALUE) print("BOOLEANVALUE ->", record.BOOLEANVALUE) print() # call the stored procedure which will modify the record cursor.callproc("mypkg.DemoRecordsInOut", [record]) # show the modified values print("NUMBERVALUE ->", record.NUMBERVALUE) print("STRINGVALUE ->", record.STRINGVALUE) print("DATEVALUE ->", record.DATEVALUE) print("BOOLEANVALUE ->", record.BOOLEANVALUE) This will produce the following output:: NUMBERVALUE -> 6 STRINGVALUE -> Test String DATEVALUE -> 2016-05-28 00:00:00 BOOLEANVALUE -> False NUMBERVALUE -> 12 STRINGVALUE -> Test String (Modified) DATEVALUE -> 2016-06-02 00:00:00 BOOLEANVALUE -> True Note that when manipulating records, all of the attributes must be set by the Python program in order to avoid an Oracle Client bug which will result in unexpected values or the Python application segfaulting. .. _spatial: Binding Spatial Datatypes ========================= Oracle Spatial datatypes objects can be represented by Python objects and their attribute values can be read and updated. The objects can further be bound and committed to database. This is similar to the examples above. An example of fetching SDO_GEOMETRY is in :ref:`Oracle Database Objects and Collections `. .. _inputtypehandlers: Changing Bind Data Types using an Input Type Handler ==================================================== Input Type Handlers allow applications to change how data is bound to statements, or even to enable new types to be bound directly. An input type handler is enabled by setting the attribute :attr:`Cursor.inputtypehandler` or :attr:`Connection.inputtypehandler`. Input type handlers can be combined with variable converters to bind Python objects seamlessly: .. code-block:: python # A standard Python object class Building: def __init__(self, build_id, description, num_floors, date_built): self.building_id = build_id self.description = description self.num_floors = num_floors self.date_built = date_built building = Building(1, "Skyscraper 1", 5, datetime.date(2001, 5, 24)) # Get Python representation of the Oracle user defined type UDT_BUILDING obj_type = con.gettype("UDT_BUILDING") # convert a Python Building object to the Oracle user defined type # UDT_BUILDING def building_in_converter(value): obj = obj_type.newobject() obj.BUILDINGID = value.building_id obj.DESCRIPTION = value.description obj.NUMFLOORS = value.num_floors obj.DATEBUILT = value.date_built return obj def input_type_handler(cursor, value, num_elements): if isinstance(value, Building): return cursor.var(obj_type, arraysize=num_elements, inconverter=building_in_converter) # With the input type handler, the bound Python object is converted # to the required Oracle object before being inserted cur.inputtypehandler = input_type_handler cur.execute("insert into myTable values (:1, :2)", (1, building)) Binding Multiple Values to a SQL WHERE IN Clause ================================================ To use a SQL IN clause with multiple values, use one bind variable per value. You cannot directly bind a Python list or dictionary to a single bind variable. For example, to use two values in an IN clause: .. code-block:: python cursor.execute(""" select employee_id, first_name, last_name from employees where last_name in (:name1, :name2)""", name1="Smith", name2="Taylor") for row in cursor: print(row) This gives the output:: (159, 'Lindsey', 'Smith') (171, 'William', 'Smith') (176, 'Jonathon', 'Taylor') (180, 'Winston', 'Taylor') If the query is executed multiple times with differing numbers of values, a bind variable should be included for each possible value. When the statement is executed but the maximum number of values has not been supplied, the value ``None`` can be bound for missing values. For example, if the query above is used for up to 5 values, the code would be: .. code-block:: python cursor.execute(""" select employee_id, first_name, last_name from employees where last_name in (:name1, :name2, :name3, :name4, :name5)""", name1="Smith", name2="Taylor", name3=None, name4=None, name5=None) for row in cursor: print(row) This will produce the same output as the original example. Reusing the same SQL statement like this for a variable number of values, instead of constructing a unique statement per set of values, allows best reuse of Oracle Database resources. However, if the statement is not going to be re-executed, or the number of values is only going to be known at runtime, then a SQL statement can be built up as follows: .. code-block:: python bind_values = ["Gates", "Marvin", "Fay"] bind_names = [":" + str(i + 1) for i in range(len(bind_values))] sql = "select employee_id, first_name, last_name from employees " + \ "where last_name in (%s)" % (",".join(bind_names)) cursor.execute(sql, bind_values) for row in cursor: print(row) A general solution for a larger number of values is to construct a SQL statement like:: SELECT ... WHERE col IN ( ) The best way to do the '' will depend on how the data is initially represented and the number of items. You might look at using CONNECT BY or at using a global temporary table. One method is to use an Oracle collection with the ``TABLE()`` clause. For example, if the following type was created:: SQL> CREATE OR REPLACE TYPE name_array AS TABLE OF VARCHAR2(25); 2 / then the application could do: .. code-block:: python type_obj = connection.gettype("NAME_ARRAY") obj = type_obj.newobject() obj.extend(["Smith", "Taylor"]) cursor.execute("""select employee_id, first_name, last_name from employees where last_name in (select * from table(:1))""", [obj]) for row in cursor: print(row) For efficiency, retain the return value of ``gettype()`` for reuse instead of making repeated calls to get the type information. Binding Column and Table Names ============================== Column and table names cannot be bound in SQL queries. You can concatenate text to build up a SQL statement, but make sure you use an Allow List or other means to validate the data in order to avoid SQL Injection security issues: .. code-block:: python table_allow_list = ['employees', 'departments'] table_name = get_table_name() # get the table name from user input if table_name.lower() not in table_allow_list: raise Exception('Invalid table name') sql = f'select * from {table_name}' Binding column names can be done either by using the above method or by using a CASE statement. The example below demonstrates binding a column name in an ORDER BY clause: .. code-block:: python sql = """ SELECT * FROM departments ORDER BY CASE :bindvar WHEN 'department_id' THEN DEPARTMENT_ID ELSE MANAGER_ID END""" col_name = get_column_name() # Obtain a column name from the user cursor.execute(sql, [col_name]) Depending on the name provided by the user, the query results will be ordered either by the column ``DEPARTMENT_ID`` or the column ``MANAGER_ID``. python-cx_Oracle-8.3.0/doc/src/user_guide/connection_handling.rst000066400000000000000000001762221414105416400251760ustar00rootroot00000000000000.. _connhandling: ***************************** Connecting to Oracle Database ***************************** Connections between cx_Oracle and Oracle Database are used for executing :ref:`SQL `, :ref:`PL/SQL `, and :ref:`SODA `. Establishing Database Connections ================================= There are two ways to connect to Oracle Database using cx_Oracle: * **Standalone connections** These are useful when the application maintains a single user session to a database. Connections are created by :meth:`cx_Oracle.connect()` or its alias :meth:`cx_Oracle.Connection()`. * **Pooled connections** :ref:`Connection pooling ` is important for performance when applications frequently connect and disconnect from the database. Pools support Oracle's :ref:`high availability ` features and are recommended for applications that must be reliable. Small pools can also be useful for applications that want a few connections available for infrequent use. Pools are created with :meth:`cx_Oracle.SessionPool()` at application initialization time, and then :meth:`SessionPool.acquire()` can be called to obtain a connection from a pool. Many connection behaviors can be controlled by cx_Oracle options. Other settings can be configured in :ref:`optnetfiles` or in :ref:`optclientfiles`. These include limiting the amount of time that opening a connection can take, or enabling :ref:`network encryption `. **Example: Standalone Connection to Oracle Database** .. code-block:: python import cx_Oracle userpwd = ". . ." # Obtain password string from a user prompt or environment variable connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8") cx_Oracle also supports :ref:`external authentication ` so passwords do not need to be in the application. Closing Connections =================== Connections should be released when they are no longer needed by calling :meth:`Connection.close()`. Alternatively, you may prefer to let connections be automatically cleaned up when references to them go out of scope. This lets cx_Oracle close dependent resources in the correct order. One other approach is the use of a "with" block, for example: .. code-block:: python with cx_Oracle.connect(user=user, password=password, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8") as connection: cursor = connection.cursor() cursor.execute("insert into SomeTable values (:1, :2)", (1, "Some string")) connection.commit() This code ensures that, once the block is completed, the connection is closed and resources have been reclaimed by the database. In addition, any attempt to use the variable ``connection`` outside of the block will simply fail. Prompt closing of connections is important when using connection pools so connections are available for reuse by other pool users. .. _connstr: Connection Strings ================== The data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()` is the Oracle Database connection string identifying which database service to connect to. The ``dsn`` string can be one of: * An Oracle Easy Connect string * An Oracle Net Connect Descriptor string * A Net Service Name mapping to a connect descriptor For more information about naming methods, see `Oracle Net Service Reference `__. .. _easyconnect: Easy Connect Syntax for Connection Strings ------------------------------------------ An Easy Connect string is often the simplest connection string to use for the data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()`. This method does not need configuration files such as ``tnsnames.ora``. For example, to connect to the Oracle Database service ``orclpdb1`` that is running on the host ``dbhost.example.com`` with the default Oracle Database port 1521, use: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8") If the database is using a non-default port, it must be specified: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com:1984/orclpdb1", encoding="UTF-8") The Easy Connect syntax supports Oracle Database service names. It cannot be used with the older System Identifiers (SID). The Easy Connect syntax has been extended in recent versions of Oracle Database client since its introduction in 10g. Check the Easy Connect Naming method in `Oracle Net Service Administrator's Guide `__ for the syntax to use in your version of the Oracle Client libraries. If you are using Oracle Client 19c, the latest `Easy Connect Plus `__ syntax allows the use of multiple hosts or ports, along with optional entries for the wallet location, the distinguished name of the database server, and even lets some network configuration options be set. This means that a :ref:`sqlnet.ora ` file is not needed for some common connection scenarios. Oracle Net Connect Descriptor Strings ------------------------------------- The :meth:`cx_Oracle.makedsn()` function can be used to construct a connect descriptor string for the data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()`. The :meth:`~cx_Oracle.makedsn()` function accepts the database hostname, the port number, and the service name. It also supports :ref:`sharding ` syntax. For example, to connect to the Oracle Database service ``orclpdb1`` that is running on the host ``dbhost.example.com`` with the default Oracle Database port 1521, use: .. code-block:: python dsn = cx_Oracle.makedsn("dbhost.example.com", 1521, service_name="orclpdb1") connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, encoding="UTF-8") Note the use of the named argument ``service_name``. By default, the third parameter of :meth:`~cx_Oracle.makedsn()` is a database System Identifier (SID), not a service name. However, almost all current databases use service names. The value of ``dsn`` in this example is the connect descriptor string:: (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=dbhost.example.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orclpdb1))) You can manually create similar connect descriptor strings. This lets you extend the syntax, for example to support failover. These strings can be embedded directly in the application: .. code-block:: python dsn = """(DESCRIPTION= (FAILOVER=on) (ADDRESS_LIST= (ADDRESS=(PROTOCOL=tcp)(HOST=sales1-svr)(PORT=1521)) (ADDRESS=(PROTOCOL=tcp)(HOST=sales2-svr)(PORT=1521))) (CONNECT_DATA=(SERVICE_NAME=sales.example.com)))""" connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, encoding="UTF-8") .. _netservice: Net Service Names for Connection Strings ---------------------------------------- Connect Descriptor Strings are commonly stored in a :ref:`tnsnames.ora ` file and associated with a Net Service Name. This name can be used directly for the data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()`. For example, given a ``tnsnames.ora`` file with the following contents:: ORCLPDB1 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orclpdb1) ) ) then you could connect using the following code: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="orclpdb1", encoding="UTF-8") For more information about Net Service Names, see `Database Net Services Reference `__. JDBC and Oracle SQL Developer Connection Strings ------------------------------------------------ The cx_Oracle connection string syntax is different to Java JDBC and the common Oracle SQL Developer syntax. If these JDBC connection strings reference a service name like:: jdbc:oracle:thin:@hostname:port/service_name for example:: jdbc:oracle:thin:@dbhost.example.com:1521/orclpdb1 then use Oracle's Easy Connect syntax in cx_Oracle: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com:1521/orclpdb1", encoding="UTF-8") Alternatively, if a JDBC connection string uses an old-style Oracle SID "system identifier", and the database does not have a service name:: jdbc:oracle:thin:@hostname:port:sid for example:: jdbc:oracle:thin:@dbhost.example.com:1521:orcl then a connect descriptor string from ``makedsn()`` can be used in the application: .. code-block:: python dsn = cx_Oracle.makedsn("dbhost.example.com", 1521, sid="orcl") connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, encoding="UTF-8") Alternatively, create a ``tnsnames.ora`` (see :ref:`optnetfiles`) entry, for example:: finance = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521)) (CONNECT_DATA = (SID = ORCL) ) ) This can be referenced in cx_Oracle: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="finance", encoding="UTF-8") .. _connpool: Connection Pooling ================== cx_Oracle's connection pooling lets applications create and maintain a pool of connections to the database. Connection pooling is important for performance when applications frequently connect and disconnect from the database. The pool implementation uses Oracle's `session pool technology `__ which supports Oracle's :ref:`high availability ` features and is recommended for applications that must be reliable. This also means that small pools can be useful for applications that want a few connections available for infrequent use. A connection pool is created by calling :meth:`~cx_Oracle.SessionPool()`. This is generally called during application initialization. The initial pool size and the maximum pool size are provided at the time of pool creation. When the pool needs to grow, new connections are created automatically. The pool can shrink back to the minimum size when connections are no longer in use. For pools created with :ref:`external authentication `, with :ref:`homogeneous ` set to False, or when using :ref:`drcp`, then the number of connections initially created is zero even if a larger value is specified for ``min``. Also in these cases the pool increment is always 1, regardless of the value of ``increment``. After a pool has been created, connections can be obtained from it by calling :meth:`~SessionPool.acquire()`. These connections can be used in the same way that standalone connections are used. Connections acquired from the pool should be released back to the pool using :meth:`SessionPool.release()` or :meth:`Connection.close()` when they are no longer required. Otherwise, they will be released back to the pool automatically when all of the variables referencing the connection go out of scope. This make connections available for other users of the pool. The session pool can be completely closed using :meth:`SessionPool.close()`. The example below shows how to connect to Oracle Database using a connection pool: .. code-block:: python # Create the session pool pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", min=2, max=5, increment=1, encoding="UTF-8") # Acquire a connection from the pool connection = pool.acquire() # Use the pooled connection cursor = connection.cursor() for result in cursor.execute("select * from mytab"): print(result) # Release the connection to the pool pool.release(connection) # Close the pool pool.close() Other :meth:`cx_Oracle.SessionPool()` options can be used at pool creation. For example the ``getmode`` value can be set so that any ``aquire()`` call will wait for a connection to become available if all are currently in use, for example: .. code-block:: python # Create the session pool pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", min=2, max=5, increment=1, getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT, encoding="UTF-8") See `ConnectionPool.py `__ for an example. Before :meth:`SessionPool.acquire()` returns, cx_Oracle does a lightweight check to see if the network transport for the selected connection is still open. If it is not, then :meth:`~SessionPool.acquire()` will clean up the connection and return a different one. This check will not detect cases such as where the database session has been killed by the DBA, or reached a database resource manager quota limit. To help in those cases, :meth:`~SessionPool.acquire()` will also do a full :ref:`round-trip ` ping to the database when it is about to return a connection that was unused in the pool for :data:`SessionPool.ping_interval` seconds. If the ping fails, the connection will be discarded and another one obtained before :meth:`~SessionPool.acquire()` returns to the application. Because this full ping is time based, it won't catch every failure. Also network timeouts and session kills may occur after :meth:`~SessionPool.acquire()` and before :meth:`Cursor.execute()`. To handle these cases, applications need to check for errors after each :meth:`~Cursor.execute()` and make application-specific decisions about retrying work if there was a connection failure. Oracle's :ref:`Application Continuity ` can do this automatically in some cases. Note both the lightweight and full ping connection checks can mask performance-impacting configuration issues, for example firewalls killing connections, so monitor the connection rate in `AWR `__ for an unexpected value. You can explicitly initiate a full ping to check connection liveness with :meth:`Connection.ping()` but overuse will impact performance and scalability. Connection Pool Sizing ---------------------- The Oracle Real-World Performance Group's recommendation is to use fixed size connection pools. The values of ``min`` and ``max`` should be the same (and the ``increment`` equal to zero). This avoids connection storms which can decrease throughput. See `Guideline for Preventing Connection Storms: Use Static Pools `__, which contains more details about sizing of pools. Having a fixed size will guarantee that the database can handle the upper pool size. For example, if a pool needs to grow but the database resources are limited, then :meth:`SessionPool.acquire()` may return errors such as ORA-28547. With a fixed pool size, this class of error will occur when the pool is created, allowing you to change the size before users access the application. With a dynamically growing pool, the error may occur much later after the pool has been in use for some time. The Real-World Performance Group also recommends keeping pool sizes small, as they may perform better than larger pools. The pool attributes should be adjusted to handle the desired workload within the bounds of available resources in cx_Oracle and the database. Make sure the :ref:`firewall `, `resource manager `__ or user profile `IDLE_TIME `__ do not expire idle sessions, since this will require connections be recreated, which will impact performance and scalability. .. _poolreconfiguration: Connection Pool Reconfiguration ------------------------------- Some pool settings can be changed dynamically with :meth:`SessionPool.reconfigure()`. This allows the pool size and other attributes to be changed during application runtime without needing to restart the pool or application. For example a pool's size can be changed like: .. code-block:: python pool.reconfigure(min=10, max=10, increment=0) After any size change has been processed, reconfiguration on the other parameters is done sequentially. If an error such as an invalid value occurs when changing one attribute, then an exception will be generated but any already changed attributes will retain their new values. During reconfiguration of a pool's size, the behavior of :meth:`SessionPool.acquire()` depends on the ``getmode`` in effect when ``acquire()`` is called, see :meth:`SessionPool.reconfigure()`. Closing connections or closing the pool will wait until after pool reconfiguration is complete. Calling ``reconfigure()`` is the only way to change a pool's ``min``, ``max`` and ``increment`` values. Other attributes such as :data:`~SessionPool.wait_timeout` can also be passed to ``reconfigure()`` or they can be set directly: .. code-block:: python pool.wait_timeout = 1000 .. _sessioncallback: Session CallBacks for Setting Pooled Connection State ----------------------------------------------------- Applications can set "session" state in each connection. Examples of session state are NLS settings from ``ALTER SESSION`` statements. Pooled connections will retain their session state after they have been released back to the pool. However, because pools can grow, or connections in the pool can be recreated, there is no guarantee a subsequent :meth:`~SessionPool.acquire()` call will return a database connection that has any particular state. The :meth:`~cx_Oracle.SessionPool()` parameter ``session_callback`` enables efficient setting of session state so that connections have a known session state, without requiring that state to be explicitly set after each :meth:`~SessionPool.acquire()` call. Connections can also be tagged when they are released back to the pool. The tag is a user-defined string that represents the session state of the connection. When acquiring connections, a particular tag can be requested. If a connection with that tag is available, it will be returned. If not, then another session will be returned. By comparing the actual and requested tags, applications can determine what exact state a session has, and make any necessary changes. The session callback can be a Python function or a PL/SQL procedure. There are three common scenarios for ``session_callback``: - When all connections in the pool should have the same state, use a Python callback without tagging. - When connections in the pool require different state for different users, use a Python callback with tagging. - When using :ref:`drcp`: use a PL/SQL callback with tagging. **Python Callback** If the ``session_callback`` parameter is a Python procedure, it will be called whenever :meth:`~SessionPool.acquire()` will return a newly created database connection that has not been used before. It is also called when connection tagging is being used and the requested tag is not identical to the tag in the connection returned by the pool. An example is: .. code-block:: python # Set the NLS_DATE_FORMAT for a session def init_session(connection, requested_tag): cursor = connection.cursor() cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI'") # Create the pool with session callback defined pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="orclpdb1", session_callback=init_session, encoding="UTF-8") # Acquire a connection from the pool (will always have the new date format) connection = pool.acquire() If needed, the ``init_session()`` procedure is called internally before ``acquire()`` returns. It will not be called when previously used connections are returned from the pool. This means that the ALTER SESSION does not need to be executed after every ``acquire()`` call. This improves performance and scalability. In this example tagging was not being used, so the ``requested_tag`` parameter is ignored. Note: if you need to execute multiple SQL statements in the callback, use an anonymous PL/SQL block to save :ref:`round-trips ` of repeated ``execute()`` calls. With ALTER SESSION, pass multiple settings in the one statement: .. code-block:: python cursor.execute(""" begin execute immediate 'alter session set nls_date_format = ''YYYY-MM-DD'' nls_language = AMERICAN'; -- other SQL statements could be put here end;""") **Connection Tagging** Connection tagging is used when connections in a pool should have differing session states. In order to retrieve a connection with a desired state, the ``tag`` attribute in :meth:`~SessionPool.acquire()` needs to be set. When cx_Oracle is using Oracle Client libraries 12.2 or later, then cx_Oracle uses 'multi-property tags' and the tag string must be of the form of one or more "name=value" pairs separated by a semi-colon, for example ``"loc=uk;lang=cy"``. When a connection is requested with a given tag, and a connection with that tag is not present in the pool, then a new connection, or an existing connection with cleaned session state, will be chosen by the pool and the session callback procedure will be invoked. The callback can then set desired session state and update the connection's tag. However if the ``matchanytag`` parameter of :meth:`~SessionPool.acquire()` is *True*, then any other tagged connection may be chosen by the pool and the callback procedure should parse the actual and requested tags to determine which bits of session state should be reset. The example below demonstrates connection tagging: .. code-block:: python def init_session(connection, requested_tag): if requested_tag == "NLS_DATE_FORMAT=SIMPLE": sql = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'" elif requested_tag == "NLS_DATE_FORMAT=FULL": sql = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI'" cursor = connection.cursor() cursor.execute(sql) connection.tag = requested_tag pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="orclpdb1", session_callback=init_session, encoding="UTF-8") # Two connections with different session state: connection1 = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") connection2 = pool.acquire(tag="NLS_DATE_FORMAT=FULL") See `session_callback.py `__ for an example. **PL/SQL Callback** When cx_Oracle uses Oracle Client 12.2 or later, the session callback can also be the name of a PL/SQL procedure. A PL/SQL callback will be initiated only when the tag currently associated with a connection does not match the tag that is requested. A PL/SQL callback is most useful when using :ref:`drcp` because DRCP does not require a :ref:`round-trip ` to invoke a PL/SQL session callback procedure. The PL/SQL session callback should accept two VARCHAR2 arguments: .. code-block:: sql PROCEDURE myPlsqlCallback ( requestedTag IN VARCHAR2, actualTag IN VARCHAR2 ); The logic in this procedure can parse the actual tag in the session that has been selected by the pool and compare it with the tag requested by the application. The procedure can then change any state required before the connection is returned to the application from :meth:`~SessionPool.acquire()`. If the ``matchanytag`` attribute of :meth:`~SessionPool.acquire()` is *True*, then a connection with any state may be chosen by the pool. Oracle 'multi-property tags' must be used. The tag string must be of the form of one or more "name=value" pairs separated by a semi-colon, for example ``"loc=uk;lang=cy"``. In cx_Oracle set ``session_callback`` to the name of the PL/SQL procedure. For example: .. code-block:: python pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1:pooled", session_callback="MyPlsqlCallback", encoding="UTF-8") connection = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE", # DRCP options, if you are using DRCP cclass='MYCLASS', purity=cx_Oracle.ATTR_PURITY_SELF) See `session_callback_plsql.py `__ for an example. .. _connpooltypes: Heterogeneous and Homogeneous Connection Pools ---------------------------------------------- By default, connection pools are ‘homogeneous’, meaning that all connections use the same database credentials. However, if the pool option ``homogeneous`` is False at the time of pool creation, then a ‘heterogeneous’ pool will be created. This allows different credentials to be used each time a connection is acquired from the pool with :meth:`~SessionPool.acquire()`. **Heterogeneous Pools** When a heterogeneous pool is created by setting ``homogeneous`` to False and no credentials are supplied during pool creation, then a user name and password may be passed to :meth:`~SessionPool.acquire()` as shown in this example: .. code-block:: python pool = cx_Oracle.SessionPool(dsn="dbhost.example.com/orclpdb1", homogeneous=False, encoding="UTF-8") connection = pool.acquire(user="hr", password=userpwd) .. _drcp: Database Resident Connection Pooling (DRCP) =========================================== `Database Resident Connection Pooling (DRCP) `__ enables database resource sharing for applications that run in multiple client processes, or run on multiple middle-tier application servers. By default each connection from Python will use one database server process. DRCP allows pooling of these server processes. This reduces the amount of memory required on the database host. The DRCP pool can be shared by multiple applications. DRCP is useful for applications which share the same database credentials, have similar session settings (for example date format settings or PL/SQL package state), and where the application gets a database connection, works on it for a relatively short duration, and then releases it. Applications can choose whether or not to use pooled connections at runtime. For efficiency, it is recommended that DRCP connections should be used in conjunction with cx_Oracle’s local :ref:`connection pool `. **Using DRCP in Python** Using DRCP with cx_Oracle applications involves the following steps: 1. Configuring and enabling DRCP in the database 2. Configuring the application to use a DRCP connection 3. Deploying the application **Configuring and enabling DRCP** Every instance of Oracle Database uses a single, default connection pool. The pool can be configured and administered by a DBA using the ``DBMS_CONNECTION_POOL`` package: .. code-block:: sql EXECUTE DBMS_CONNECTION_POOL.CONFIGURE_POOL( pool_name => 'SYS_DEFAULT_CONNECTION_POOL', minsize => 4, maxsize => 40, incrsize => 2, session_cached_cursors => 20, inactivity_timeout => 300, max_think_time => 600, max_use_session => 500000, max_lifetime_session => 86400) Alternatively the method ``DBMS_CONNECTION_POOL.ALTER_PARAM()`` can set a single parameter: .. code-block:: sql EXECUTE DBMS_CONNECTION_POOL.ALTER_PARAM( pool_name => 'SYS_DEFAULT_CONNECTION_POOL', param_name => 'MAX_THINK_TIME', param_value => '1200') The ``inactivity_timeout`` setting terminates idle pooled servers, helping optimize database resources. To avoid pooled servers permanently being held onto by a selfish Python script, the ``max_think_time`` parameter can be set. The parameters ``num_cbrok`` and ``maxconn_cbrok`` can be used to distribute the persistent connections from the clients across multiple brokers. This may be needed in cases where the operating system per-process descriptor limit is small. Some customers have found that having several connection brokers improves performance. The ``max_use_session`` and ``max_lifetime_session`` parameters help protect against any unforeseen problems affecting server processes. The default values will be suitable for most users. See the `Oracle DRCP documentation `__ for details on parameters. In general, if pool parameters are changed, the pool should be restarted, otherwise server processes will continue to use old settings. There is a ``DBMS_CONNECTION_POOL.RESTORE_DEFAULTS()`` procedure to reset all values. When DRCP is used with RAC, each database instance has its own connection broker and pool of servers. Each pool has the identical configuration. For example, all pools start with ``minsize`` server processes. A single DBMS_CONNECTION_POOL command will alter the pool of each instance at the same time. The pool needs to be started before connection requests begin. The command below does this by bringing up the broker, which registers itself with the database listener: .. code-block:: sql EXECUTE DBMS_CONNECTION_POOL.START_POOL() Once enabled this way, the pool automatically restarts when the database instance restarts, unless explicitly stopped with the ``DBMS_CONNECTION_POOL.STOP_POOL()`` command: .. code-block:: sql EXECUTE DBMS_CONNECTION_POOL.STOP_POOL() The pool cannot be stopped while connections are open. **Application Deployment for DRCP** In order to use DRCP, the ``cclass`` and ``purity`` parameters should be passed to :meth:`cx_Oracle.connect()` or :meth:`SessionPool.acquire()`. If ``cclass`` is not set, the pooled server sessions will not be reused optimally, and the DRCP statistic views will record large values for NUM_MISSES. The DRCP ``purity`` can be one of ``ATTR_PURITY_NEW``, ``ATTR_PURITY_SELF``, or ``ATTR_PURITY_DEFAULT``. The value ``ATTR_PURITY_SELF`` allows reuse of both the pooled server process and session memory, giving maximum benefit from DRCP. See the Oracle documentation on `benefiting from scalability `__. The connection string used for :meth:`~cx_Oracle.connect()` or :meth:`~SessionPool.acquire()` must request a pooled server by following one of the syntaxes shown below: Using Oracle’s Easy Connect syntax, the connection would look like: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orcl:pooled", encoding="UTF-8") Or if you connect using a Net Service Name named ``customerpool``: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="customerpool", encoding="UTF-8") Then only the Oracle Network configuration file ``tnsnames.ora`` needs to be modified:: customerpool = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) (HOST=dbhost.example.com) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=CUSTOMER) (SERVER=POOLED))) If these changes are made and the database is not actually configured for DRCP, or the pool is not started, then connections will not succeed and an error will be returned to the Python application. Although applications can choose whether or not to use pooled connections at runtime, care must be taken to configure the database appropriately for the number of expected connections, and also to stop inadvertent use of non-DRCP connections leading to a database server resource shortage. Conversely, avoid using DRCP connections for long-running operations. The example below shows how to connect to Oracle Database using Database Resident Connection Pooling: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orcl:pooled", cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF, encoding="UTF-8") The example below shows connecting to Oracle Database using DRCP and cx_Oracle's connection pooling: .. code-block:: python mypool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="dbhost.example.com/orcl:pooled", encoding="UTF-8") connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF) For more information about DRCP see `Oracle Database Concepts Guide `__, and for DRCP Configuration see `Oracle Database Administrator's Guide `__. **Closing Connections** Python scripts where cx_Oracle connections do not go out of scope quickly (which releases them), or do not currently use :meth:`Connection.close()`, should be examined to see if :meth:`~Connection.close()` can be used, which then allows maximum use of DRCP pooled servers by the database: .. code-block:: python # Do some database operations connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF) . . . connection.close(); # Do lots of non-database work . . . # Do some more database operations connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF) . . . connection.close(); **Monitoring DRCP** Data dictionary views are available to monitor the performance of DRCP. Database administrators can check statistics such as the number of busy and free servers, and the number of hits and misses in the pool against the total number of requests from clients. The views are: * ``DBA_CPOOL_INFO`` * ``V$PROCESS`` * ``V$SESSION`` * ``V$CPOOL_STATS`` * ``V$CPOOL_CC_STATS`` * ``V$CPOOL_CONN_INFO`` **DBA_CPOOL_INFO View** ``DBA_CPOOL_INFO`` displays configuration information about the DRCP pool. The columns are equivalent to the ``dbms_connection_pool.configure_pool()`` settings described in the table of DRCP configuration options, with the addition of a ``STATUS`` column. The status is ``ACTIVE`` if the pool has been started and ``INACTIVE`` otherwise. Note the pool name column is called ``CONNECTION_POOL``. This example checks whether the pool has been started and finds the maximum number of pooled servers:: SQL> SELECT connection_pool, status, maxsize FROM dba_cpool_info; CONNECTION_POOL STATUS MAXSIZE ---------------------------- ---------- ---------- SYS_DEFAULT_CONNECTION_POOL ACTIVE 40 **V$PROCESS and V$SESSION Views** The ``V$SESSION`` view shows information about the currently active DRCP sessions. It can also be joined with ``V$PROCESS`` via ``V$SESSION.PADDR = V$PROCESS.ADDR`` to correlate the views. **V$CPOOL_STATS View** The ``V$CPOOL_STATS`` view displays information about the DRCP statistics for an instance. The V$CPOOL_STATS view can be used to assess how efficient the pool settings are. T his example query shows an application using the pool effectively. The low number of misses indicates that servers and sessions were reused. The wait count shows just over 1% of requests had to wait for a pooled server to become available:: NUM_REQUESTS NUM_HITS NUM_MISSES NUM_WAITS ------------ ---------- ---------- ---------- 10031 99990 40 1055 If ``cclass`` was set (allowing pooled servers and sessions to be reused) then NUM_MISSES will be low. If the pool maxsize is too small for the connection load, then NUM_WAITS will be high. **V$CPOOL_CC_STATS View** The view ``V$CPOOL_CC_STATS`` displays information about the connection class level statistics for the pool per instance:: SQL> SELECT cclass_name, num_requests, num_hits, num_misses FROM v$cpool_cc_stats; CCLASS_NAME NUM_REQUESTS NUM_HITS NUM_MISSES -------------------------------- ------------ ---------- ---------- HR.MYCLASS 100031 99993 38 **V$CPOOL_CONN_INFO View** The ``V$POOL_CONN_INFO`` view gives insight into client processes that are connected to the connection broker, making it easier to monitor and trace applications that are currently using pooled servers or are idle. This view was introduced in Oracle 11gR2. You can monitor the view ``V$CPOOL_CONN_INFO`` to, for example, identify misconfigured machines that do not have the connection class set correctly. This view maps the machine name to the class name:: SQL> SELECT cclass_name, machine FROM v$cpool_conn_info; CCLASS_NAME MACHINE --------------------------------------- ------------ CJ.OCI:SP:wshbIFDtb7rgQwMyuYvodA cjlinux . . . In this example you would examine applications on ``cjlinux`` and make sure ``cclass`` is set. .. _proxyauth: Connecting Using Proxy Authentication ===================================== Proxy authentication allows a user (the "session user") to connect to Oracle Database using the credentials of a 'proxy user'. Statements will run as the session user. Proxy authentication is generally used in three-tier applications where one user owns the schema while multiple end-users access the data. For more information about proxy authentication, see the `Oracle documentation `__. An alternative to using proxy users is to set :attr:`Connection.client_identifier` after connecting and use its value in statements and in the database, for example for :ref:`monitoring `. The following proxy examples use these schemas. The ``mysessionuser`` schema is granted access to use the password of ``myproxyuser``: .. code-block:: sql CREATE USER myproxyuser IDENTIFIED BY myproxyuserpw; GRANT CREATE SESSION TO myproxyuser; CREATE USER mysessionuser IDENTIFIED BY itdoesntmatter; GRANT CREATE SESSION TO mysessionuser; ALTER USER mysessionuser GRANT CONNECT THROUGH myproxyuser; After connecting to the database, the following query can be used to show the session and proxy users: .. code-block:: sql SELECT SYS_CONTEXT('USERENV', 'PROXY_USER'), SYS_CONTEXT('USERENV', 'SESSION_USER') FROM DUAL; Standalone connection examples: .. code-block:: python # Basic Authentication without a proxy connection = cx_Oracle.connect(user="myproxyuser", password="myproxyuserpw", dsn="dbhost.example.com/orclpdb1", encoding="UTF-8") # PROXY_USER: None # SESSION_USER: MYPROXYUSER # Basic Authentication with a proxy connection = cx_Oracle.connect(user="myproxyuser[mysessionuser]", password="myproxyuserpw", dsn="dbhost.example.com/orclpdb1", encoding="UTF-8") # PROXY_USER: MYPROXYUSER # SESSION_USER: MYSESSIONUSER Pooled connection examples: .. code-block:: python # Basic Authentication without a proxy pool = cx_Oracle.SessionPool(user="myproxyuser", password="myproxyuser", dsn="dbhost.example.com/orclpdb1", encoding="UTF-8") connection = pool.acquire() # PROXY_USER: None # SESSION_USER: MYPROXYUSER # Basic Authentication with proxy pool = cx_Oracle.SessionPool(user="myproxyuser[mysessionuser]", password="myproxyuser", dsn="dbhost.example.com/orclpdb1", homogeneous=False, encoding="UTF-8") connection = pool.acquire() # PROXY_USER: MYPROXYUSER # SESSION_USER: MYSESSIONUSER Note the use of a :ref:`heterogeneous ` pool in the example above. This is required in this scenario. .. _extauth: Connecting Using External Authentication ======================================== Instead of storing the database username and password in Python scripts or environment variables, database access can be authenticated by an outside system. External Authentication allows applications to validate user access by an external password store (such as an Oracle Wallet), by the operating system, or with an external authentication service. Using an Oracle Wallet for External Authentication -------------------------------------------------- The following steps give an overview of using an Oracle Wallet. Wallets should be kept securely. Wallets can be managed with `Oracle Wallet Manager `__. In this example the wallet is created for the ``myuser`` schema in the directory ``/home/oracle/wallet_dir``. The ``mkstore`` command is available from a full Oracle client or Oracle Database installation. If you have been given wallet by your DBA, skip to step 3. 1. First create a new wallet as the ``oracle`` user:: mkstore -wrl "/home/oracle/wallet_dir" -create This will prompt for a new password for the wallet. 2. Create the entry for the database user name and password that are currently hardcoded in your Python scripts. Use either of the methods shown below. They will prompt for the wallet password that was set in the first step. **Method 1 - Using an Easy Connect string**:: mkstore -wrl "/home/oracle/wallet_dir" -createCredential dbhost.example.com/orclpdb1 myuser myuserpw **Method 2 - Using a connect name identifier**:: mkstore -wrl "/home/oracle/wallet_dir" -createCredential mynetalias myuser myuserpw The alias key ``mynetalias`` immediately following the ``-createCredential`` option will be the connect name to be used in Python scripts. If your application connects with multiple different database users, you could create a wallet entry with different connect names for each. You can see the newly created credential with:: mkstore -wrl "/home/oracle/wallet_dir" -listCredential 3. Skip this step if the wallet was created using an Easy Connect String. Otherwise, add an entry in :ref:`tnsnames.ora ` for the connect name as follows:: mynetalias = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orclpdb1) ) ) The file uses the description for your existing database and sets the connect name alias to ``mynetalias``, which is the identifier used when adding the wallet entry. 4. Add the following wallet location entry in the :ref:`sqlnet.ora ` file, using the ``DIRECTORY`` you created the wallet in:: WALLET_LOCATION = (SOURCE = (METHOD = FILE) (METHOD_DATA = (DIRECTORY = /home/oracle/wallet_dir) ) ) SQLNET.WALLET_OVERRIDE = TRUE Examine the Oracle documentation for full settings and values. 5. Ensure the configuration files are in a default location or set TNS_ADMIN is set to the directory containing them. See :ref:`optnetfiles`. With an Oracle wallet configured, and readable by you, your scripts can connect using: .. code-block:: python connection = cx_Oracle.connect(dsn="mynetalias", encoding="UTF-8") or: .. code-block:: python pool = cx_Oracle.SessionPool(externalauth=True, homogeneous=False, dsn="mynetalias", encoding="UTF-8") pool.acquire() The ``dsn`` must match the one used in the wallet. After connecting, the query:: SELECT SYS_CONTEXT('USERENV', 'SESSION_USER') FROM DUAL; will show:: MYUSER .. note:: Wallets are also used to configure TLS connections. If you are using a wallet like this, you may need a database username and password in :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()` calls. **External Authentication and Proxy Authentication** The following examples show external wallet authentication combined with :ref:`proxy authentication `. These examples use the wallet configuration from above, with the addition of a grant to another user:: ALTER USER mysessionuser GRANT CONNECT THROUGH myuser; After connection, you can check who the session user is with: .. code-block:: sql SELECT SYS_CONTEXT('USERENV', 'PROXY_USER'), SYS_CONTEXT('USERENV', 'SESSION_USER') FROM DUAL; Standalone connection example: .. code-block:: python # External Authentication with proxy connection = cx_Oracle.connect(user="[mysessionuser]", dsn="mynetalias", encoding="UTF-8") # PROXY_USER: MYUSER # SESSION_USER: MYSESSIONUSER Pooled connection example: .. code-block:: python # External Authentication with proxy pool = cx_Oracle.SessionPool(externalauth=True, homogeneous=False, dsn="mynetalias", encoding="UTF-8") pool.acquire(user="[mysessionuser]") # PROXY_USER: MYUSER # SESSION_USER: MYSESSIONUSER The following usage is not supported: .. code-block:: python pool = cx_Oracle.SessionPool(user="[mysessionuser]", externalauth=True, homogeneous=False, dsn="mynetalias", encoding="UTF-8") pool.acquire() Operating System Authentication ------------------------------- With Operating System authentication, Oracle allows user authentication to be performed by the operating system. The following steps give an overview of how to implement OS Authentication on Linux. 1. Login to your computer. The commands used in these steps assume the operating system user name is "oracle". 2. Login to SQL*Plus as the SYSTEM user and verify the value for the ``OS_AUTHENT_PREFIX`` parameter:: SQL> SHOW PARAMETER os_authent_prefix NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ os_authent_prefix string ops$ 3. Create an Oracle database user using the ``os_authent_prefix`` determined in step 2, and the operating system user name: .. code-block:: sql CREATE USER ops$oracle IDENTIFIED EXTERNALLY; GRANT CONNECT, RESOURCE TO ops$oracle; In Python, connect using the following code: .. code-block:: python connection = cx_Oracle.connect(dsn="mynetalias", encoding="UTF-8") Your session user will be ``OPS$ORACLE``. If your database is not on the same computer as python, you can perform testing by setting the database configuration parameter ``remote_os_authent=true``. Beware this is insecure. See `Oracle Database Security Guide `__ for more information about Operating System Authentication. Privileged Connections ====================== The ``mode`` parameter of the function :meth:`cx_Oracle.connect()` specifies the database privilege that you want to associate with the user. The example below shows how to connect to Oracle Database as SYSDBA: .. code-block:: python connection = cx_Oracle.connect(user="sys", password=syspwd, dsn="dbhost.example.com/orclpdb1", mode=cx_Oracle.SYSDBA, encoding="UTF-8") cursor = con.cursor() sql = "GRANT SYSOPER TO hr" cursor.execute(sql) This is equivalent to executing the following in SQL*Plus: .. code-block:: sql CONNECT sys/syspwd AS SYSDBA GRANT SYSOPER TO hr; .. _netencrypt: Securely Encrypting Network Traffic to Oracle Database ====================================================== You can encrypt data transferred between the Oracle Database and the Oracle Client libraries used by cx_Oracle so that unauthorized parties are not able to view plain text values as the data passes over the network. The easiest configuration is Oracle’s native network encryption. The standard SSL protocol can also be used if you have a PKI, but setup is necessarily more involved. With native network encryption, the client and database server negotiate a key using Diffie-Hellman key exchange. This provides protection against man-in-the-middle attacks. Native network encryption can be configured by editing Oracle Net’s optional :ref:`sqlnet.ora ` configuration file, on either the database server and/or on each cx_Oracle 'client' machine. Parameters control whether data integrity checking and encryption is required or just allowed, and which algorithms the client and server should consider for use. As an example, to ensure all connections to the database are checked for integrity and are also encrypted, create or edit the Oracle Database ``$ORACLE_HOME/network/admin/sqlnet.ora`` file. Set the checksum negotiation to always validate a checksum and set the checksum type to your desired value. The network encryption settings can similarly be set. For example, to use the SHA512 checksum and AES256 encryption use:: SQLNET.CRYPTO_CHECKSUM_SERVER = required SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA512) SQLNET.ENCRYPTION_SERVER = required SQLNET.ENCRYPTION_TYPES_SERVER = (AES256) If you definitely know that the database server enforces integrity and encryption, then you do not need to configure cx_Oracle separately. However you can also, or alternatively, do so depending on your business needs. Create a ``sqlnet.ora`` on your client machine and locate it with other :ref:`optnetfiles`:: SQLNET.CRYPTO_CHECKSUM_CLIENT = required SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT = (SHA512) SQLNET.ENCRYPTION_CLIENT = required SQLNET.ENCRYPTION_TYPES_CLIENT = (AES256) The client and server sides can negotiate the protocols used if the settings indicate more than one value is accepted. Note that these are example settings only. You must review your security requirements and read the documentation for your Oracle version. In particular review the available algorithms for security and performance. The ``NETWORK_SERVICE_BANNER`` column of the database view `V$SESSION_CONNECT_INFO `__ can be used to verify the encryption status of a connection. For more information on Oracle Data Network Encryption and Integrity, configuring SSL network encryption and Transparent Data Encryption of data-at-rest in the database, see `Oracle Database Security Guide `__. Resetting Passwords =================== After connecting, passwords can be changed by calling :meth:`Connection.changepassword()`: .. code-block:: python # Get the passwords from somewhere, such as prompting the user oldpwd = getpass.getpass(f"Old Password for {username}: ") newpwd = getpass.getpass(f"New Password for {username}: ") connection.changepassword(oldpwd, newpwd) When a password has expired and you cannot connect directly, you can connect and change the password in one operation by using the ``newpassword`` parameter of the function :meth:`cx_Oracle.connect()` constructor: .. code-block:: python # Get the passwords from somewhere, such as prompting the user oldpwd = getpass.getpass(f"Old Password for {username}: ") newpwd = getpass.getpass(f"New Password for {username}: ") connection = cx_Oracle.connect(user=username, password=oldpwd, dsn="dbhost.example.com/orclpdb1", newpassword=newpwd, encoding="UTF-8") .. _autononmousdb: Connecting to Oracle Cloud Autononmous Databases ================================================ To enable connection to Oracle Autonomous Database in Oracle Cloud, a wallet needs be downloaded from the cloud, and cx_Oracle needs to be configured to use it. The wallet gives mutual TLS which provides enhanced security for authentication and encryption. A database username and password is still required for your application connections. Install the Wallet and Network Configuration Files -------------------------------------------------- From the Oracle Cloud console for the database, download the wallet zip file. It contains the wallet and network configuration files. Note: keep wallet files in a secure location and share them only with authorized users. Unzip the wallet zip file. For cx_Oracle, only these files from the zip are needed: - ``tnsnames.ora`` - Maps net service names used for application connection strings to your database services - ``sqlnet.ora`` - Configures Oracle Network settings - ``cwallet.sso`` - Enables SSL/TLS connections There are now two options: - Move the three files to the ``network/admin`` directory of the client libraries used by your application. For example if you are using Instant Client 19c and it is in ``$HOME/instantclient_19_11``, then you would put the wallet files in ``$HOME/instantclient_19_11/network/admin/``. - Alternatively, move them to any accessible directory, for example ``/opt/OracleCloud/MYDB``. Then edit ``sqlnet.ora`` and change the wallet location directory to the directory containing the ``cwallet.sso`` file. For example:: WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/opt/OracleCloud/MYDB"))) SSL_SERVER_DN_MATCH=yes Since the ``tnsnames.ora`` and ``sqlnet.ora`` files are not in the default location, your application needs to indicate where they are, either with the ``config_dir`` parameter to :meth:`cx_Oracle.init_oracle_client()`, or using the ``TNS_ADMIN`` environment variable. See :ref:`Optional Oracle Net Configuration Files `. Neither of these settings are needed, and you don't need to edit ``sqlnet.ora``, if you have put all the files in the ``network/admin`` directory. Run Your Application -------------------- The ``tnsnames.ora`` file contains net service names for various levels of database service. For example, if you create a database called CJDB1 with the Always Free services from the `Oracle Cloud Free Tier `__, then you might decide to use the connection string in ``tnsnames.ora`` called ``cjdb1_high``. Update your application to use your schema username, its database password, and a net service name, for example: .. code-block:: python connection = cx_Oracle.connect(user="scott", password=userpwd, dsn="cjdb1_high", encoding="UTF-8") Once you have set optional Oracle environment variables used by your application, such as ``TNS_ADMIN``, you can start your application. If you need to create a new database schema so you do not login as the privileged ADMIN user, refer to the relevant Oracle Cloud documentation, for example see `Create Database Users `__ in the Oracle Autonomous Database manual. Access Through a Proxy ---------------------- If you are behind a firewall, you can tunnel TLS/SSL connections via a proxy using `HTTPS_PROXY `__ in the connect descriptor. Successful connection depends on specific proxy configurations. Oracle does not recommend doing this when performance is critical. Edit ``sqlnet.ora`` and add a line:: SQLNET.USE_HTTPS_PROXY=on Edit ``tnsnames.ora`` and add an ``HTTPS_PROXY`` proxy name and ``HTTPS_PROXY_PORT`` port to the connect descriptor address list of any service name you plan to use, for example:: cjdb1_high = (description= (address= (https_proxy=myproxy.example.com)(https_proxy_port=80) (protocol=tcps)(port=1522)(host= . . . ) Using the Easy Connect Syntax with Autonomous Database ------------------------------------------------------ When cx_Oracle is using Oracle Client libraries 19c or later, you can optionally use the :ref:`Easy Connect ` syntax to connect to Oracle Autonomous Database. The mapping from the cloud ``tnsnames.ora`` entries to an Easy Connect Plus string is:: protocol://host:port/service_name?wallet_location=/my/dir&retry_count=N&retry_delay=N For example, if your ``tnsnames.ora`` file had an entry:: cjjson_high = (description=(retry_count=20)(retry_delay=3) (address=(protocol=tcps)(port=1522) (host=adb.ap-sydney-1.oraclecloud.com)) (connect_data=(service_name=abc_cjjson_high.adb.oraclecloud.com)) (security=(ssl_server_cert_dn="CN=adb.ap-sydney-1.oraclecloud.com,OU=Oracle ADB SYDNEY,O=Oracle Corporation,L=Redwood City,ST=California,C=US"))) Then your applications can connect using the connection string: .. code-block:: python dsn = "tcps://adb.ap-sydney-1.oraclecloud.com:1522/abc_cjjson_high.adb.oraclecloud.com?wallet_location=/Users/cjones/Cloud/CJJSON&retry_count=20&retry_delay=3" connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, encoding="UTF-8") The ``wallet_location`` parameter needs to be set to the directory containing the ``cwallet.sso`` file from the wallet ZIP. The other wallet files, including ``tnsnames.ora``, are not needed when you use the Easy Connect Plus syntax. You can add other Easy Connect parameters to the connection string, for example:: dsn = dsn + "&https_proxy=myproxy.example.com&https_proxy_port=80" .. _connsharding: Connecting to Sharded Databases =============================== `Oracle Sharding `__ can be used to horizontally partition data across independent databases. A database table can be split so each shard contains a table with the same columns but a different subset of rows. These tables are known as sharded tables. Sharding is configured in Oracle Database, see the `Oracle Sharding `__ manual. Sharding requires Oracle Database and Oracle Client libraries 12.2, or later. The :meth:`cx_Oracle.connect()` and :meth:`SessionPool.acquire()` functions accept ``shardingkey`` and ``supershardingkey`` parameters that are a sequence of values used to route the connection directly to a given shard. A sharding key is always required. A super sharding key is additionally required when using composite sharding, which is when data has been partitioned by a list or range (the super sharding key), and then further partitioned by a sharding key. When creating a connection pool, the :meth:`cx_Oracle.SessionPool()` attribute ``max_sessions_per_shard`` can be set. This is used to balance connections in the pool equally across shards. It requires Oracle Client libraries 18.3, or later. Shard key values may be of type string (mapping to VARCHAR2 shard keys), number (NUMBER), bytes (RAW), or date (DATE). Multiple types may be used in each array. Sharding keys of TIMESTAMP type are not supported. When connected to a shard, queries will only return data from that shard. For queries that need to access data from multiple shards, connections can be established to the coordinator shard catalog database. In this case, no shard key or super shard key is used. As an example of direct connection, if sharding had been configured on a single VARCHAR2 column like: .. code-block:: sql CREATE SHARDED TABLE customers ( cust_id NUMBER, cust_name VARCHAR2(30), class VARCHAR2(10) NOT NULL, signup_date DATE, cust_code RAW(20), CONSTRAINT cust_name_pk PRIMARY KEY(cust_name)) PARTITION BY CONSISTENT HASH (cust_name) PARTITIONS AUTO TABLESPACE SET ts1; then direct connection to a shard can be made by passing a single sharding key: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8", shardingkey=["SCOTT"]) Numbers keys can be used in a similar way: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8", shardingkey=[110]) When sharding by DATE, you can connect like: .. code-block:: python import datetime d = datetime.datetime(2014, 7, 3) connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8", shardingkey=[d]) When sharding by RAW, you can connect like: .. code-block:: python b = b'\x01\x04\x08'; connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8", shardingkey=[b]) Multiple keys can be specified, for example: .. code-block:: python key_list = [70, "SCOTT", "gold", b'\x00\x01\x02'] connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8", shardingkey=key_list) A super sharding key example is: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8", supershardingkey=["goldclass"], shardingkey=["SCOTT"]) python-cx_Oracle-8.3.0/doc/src/user_guide/cqn.rst000066400000000000000000000140131414105416400217410ustar00rootroot00000000000000.. _cqn: *********************************** Continuous Query Notification (CQN) *********************************** `Continuous Query Notification (CQN) `__ allows applications to receive notifications when a table changes, such as when rows have been updated, regardless of the user or the application that made the change. This can be useful in many circumstances, such as near real-time monitoring, auditing applications, or for such purposes as mid-tier cache invalidation. A cache might hold some values that depend on data in a table. If the data in the table changes, the cached values must then be updated with the new information. CQN notification behavior is widely configurable. Choices include specifying what types of SQL should trigger a notification, whether notifications should survive database loss, and control over unsubscription. You can also choose whether notification messages will include ROWIDs of affected rows. By default, object-level (previously known as Database Change Notification) occurs and the Python notification method is invoked whenever a database transaction is committed that changes an object that a registered query references, regardless of whether the actual query result changed. However if the :meth:`subscription ` option ``qos`` is :data:`cx_Oracle.SUBSCR_QOS_QUERY` then query-level notification occurs. In this mode, the database notifies the application whenever a transaction changing the result of the registered query is committed. CQN is best used to track infrequent data changes. Requirements ============ Before using CQN, users must have appropriate permissions: .. code-block:: sql GRANT CHANGE NOTIFICATION TO ; To use CQN, connections must have ``events`` mode set to ``True``, for example: .. code-block:: python connection = cx_Oracle.connect(user=user, password=password, dsn="dbhost.example.com/orclpdb1", events=True) The default CQN connection mode means the database must be able to connect back to the application using cx_Oracle in order to receive notification events. Alternatively, when using Oracle Database and Oracle client libraries 19.4, or later, subscriptions can set the optional ``client_initiated`` parameter to ``True``, see ``Connection.subscribe()`` below. The default CQN connection mode typically means that the machine running cx_Oracle needs a fixed IP address. Note :meth:`Connection.subscribe()` does not verify that this reverse connection is possible. If there is any problem sending a notification, then the callback method will not be invoked. Configuration options can include an IP address and port on which cx_Oracle will listen for notifications; otherwise, the database chooses values. Creating a Subscription ======================= Subscriptions allow Python to receives notifications for events that take place in the database that match the given parameters. For example, a basic CQN subscription might be created like: .. code-block:: python connection.subscribe(callback=my_callback) See :meth:`Connection.subscribe()` for details on all of the parameters. See :ref:`cqn-operation-codes` for the types of operations that are supported. See :ref:`subscr-qos` for the quality of service values that are supported. See :ref:`subscr-namespaces` and :ref:`subscr-protocols` for the namespaces and protocols that are supported. See :ref:`subscrobj` for more details on the subscription object that is created. When using Oracle Database and Oracle client libraries 19.4, or later, the optional subscription parameter ``client_initiated`` can be set: .. code-block:: python connection.subscribe(callback=my_callback, client_initiated=True) This enables CQN "client initiated" connections which internally use the same approach as normal cx_Oracle connections to the database, and do not require the database to be able to connect back to the application. Since client initiated connections do not need special network configuration they have ease-of-use and security advantages. Registering Queries =================== Once a subscription has been created, one or more queries must be registered by calling :meth:`Subscription.registerquery()`. Registering a query behaves similarly to :meth:`Cursor.execute()`, but only queries are permitted and the ``args`` parameter must be a sequence or dictionary. An example script to receive query notifications when the 'CUSTOMER' table data changes is: .. code-block:: python def cqn_callback(message): print("Notification:") for query in message.queries: for tab in query.tables: print("Table:", tab.name) print("Operation:", tab.operation) for row in tab.rows: if row.operation & cx_Oracle.OPCODE_INSERT: print("INSERT of rowid:", row.rowid) if row.operation & cx_Oracle.OPCODE_DELETE: print("DELETE of rowid:", row.rowid) subscr = connection.subscribe(callback=cqn_callback, operations=cx_Oracle.OPCODE_INSERT | cx_Oracle.OPCODE_DELETE, qos=cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS) subscr.registerquery("select * from regions") input("Hit enter to stop CQN demo\n") Running the above script, shows the initial output as:: Hit enter to stop CQN demo Use SQL*Plus or another tool to commit a change to the table: .. code-block:: sql insert into regions values(120, 'L'); commit; When the commit is executed, a notification will be received by the callback which should print something like the following:: Hit enter to stop CQN demo Notification: Table: HR.REGIONS Operation: 2 INSERT of rowid: AAA7EsAAHAAAFS/AAA See `GitHub Samples `__ for a runnable CQN example. python-cx_Oracle-8.3.0/doc/src/user_guide/exception_handling.rst000066400000000000000000000023671414105416400250330ustar00rootroot00000000000000.. _exception: ****************** Exception Handling ****************** All exceptions raised by cx_Oracle are inherited from :attr:`cx_Oracle.Error`. See :ref:`Exceptions ` for more details on the various exceptions defined by cx_Oracle. See the exception handling section in the :ref:`API manual ` for more details on the information available when an exception is raised. Applications can catch exceptions as needed. For example, when trying to add a customer that already exists in the database, the following could be used to catch the exception: .. code-block:: python try: cursor.execute("insert into customer values (101, 'Customer A')") except cx_Oracle.IntegrityError: print("Customer ID already exists") else: print("Customer added") If information about the exception needs to be processed instead, the following code can be used: .. code-block:: python try: cursor.execute("insert into customer values (101, 'Customer A')") except cx_Oracle.IntegrityError as e: error_obj, = e.args print("Customer ID already exists") print("Error Code:", error_obj.code) print("Error Message:", error_obj.message) else: print("Customer added") python-cx_Oracle-8.3.0/doc/src/user_guide/globalization.rst000066400000000000000000000134711414105416400240250ustar00rootroot00000000000000.. _globalization: ******************************** Character Sets and Globalization ******************************** Data fetched from, and sent to, Oracle Database will be mapped between the database character set and the "Oracle client" character set of the Oracle Client libraries used by cx_Oracle. If data cannot be correctly mapped between client and server character sets, then it may be corrupted or queries may fail with :ref:`"codec can't decode byte" `. cx_Oracle uses Oracle’s National Language Support (NLS) to assist in globalizing applications. As well as character set support, there are many other features that will be useful in applications. See the `Database Globalization Support Guide `__. Setting the Client Character Set ================================ In cx_Oracle 8 the default encoding used for all character data changed to "UTF-8". This universal encoding is suitable for most applications. If you have a special need, you can pass the ``encoding`` and ``nencoding`` parameters to the :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool` methods to specify different Oracle Client character sets. For example: .. code-block:: python import cx_Oracle connection = cx_Oracle.connect(dsn=connect_string, encoding="US-ASCII", nencoding="UTF-8") .. note:: In a future release of cx_Oracle, only UTF-8 will be supported. The ``encoding`` parameter affects character data such as VARCHAR2 and CLOB columns. The ``nencoding`` parameter affects "National Character" data such as NVARCHAR2 and NCLOB. If you are not using national character types, then you can omit ``nencoding``. Both the ``encoding`` and ``nencoding`` parameters are expected to be one of the `Python standard encodings `__ such as ``UTF-8``. Do not accidentally use ``UTF8``, which Oracle uses to specify the older Unicode 3.0 Universal character set, ``CESU-8``. Note that Oracle does not recognize all of the encodings that Python recognizes. You can see which encodings are usable in cx_Oracle by issuing this query: .. code-block:: sql select distinct utl_i18n.map_charset(value) from v$nls_valid_values where parameter = 'CHARACTERSET' and utl_i18n.map_charset(value) is not null order by 1 .. note:: From cx_Oracle 8, it is no longer possible to change the character set using the ``NLS_LANG`` environment variable. The character set component of that variable is ignored. The language and territory components of ``NLS_LANG`` are still respected by the Oracle Client libraries. Character Set Example --------------------- The script below tries to display data containing a Euro symbol from the database. .. code-block:: python connection = cx_Oracle.connect(user=user, password=password, dsn="dbhost.example.com/orclpdb1", encoding="US-ASCII") cursor = connection.cursor() for row in cursor.execute("select nvarchar2_column from nchar_test"): print(row) Because the '€' symbol is not supported by the ``US-ASCII`` character set, all '€' characters are replaced by '¿' in the cx_Oracle output:: ('¿',) When the ``encoding`` parameter is removed (or set to "UTF-8") during connection: .. code-block:: python connection = cx_Oracle.connect(user=user, password=password, dsn="dbhost.example.com/orclpdb1") Then the output displays the Euro symbol as desired:: ('€',) .. _findingcharset: Finding the Database and Client Character Set --------------------------------------------- To find the database character set, execute the query: .. code-block:: sql SELECT value AS db_charset FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET'; To find the database 'national character set' used for NCHAR and related types, execute the query: .. code-block:: sql SELECT value AS db_ncharset FROM nls_database_parameters WHERE parameter = 'NLS_NCHAR_CHARACTERSET'; To find the current "client" character set used by cx_Oracle, execute the query: .. code-block:: sql SELECT DISTINCT client_charset AS client_charset FROM v$session_connect_info WHERE sid = SYS_CONTEXT('USERENV', 'SID'); If these character sets do not match, characters transferred over Oracle Net will be mapped from one character set to another. This may impact performance and may result in invalid data. Setting the Oracle Client Locale ================================ You can use the ``NLS_LANG`` environment variable to set the language and territory used by the Oracle Client libraries. For example, on Linux you could set:: export NLS_LANG=JAPANESE_JAPAN The language ("JAPANESE" in this example) specifies conventions such as the language used for Oracle Database messages, sorting, day names, and month names. The territory ("JAPAN") specifies conventions such as the default date, monetary, and numeric formats. If the language is not specified, then the value defaults to AMERICAN. If the territory is not specified, then the value is derived from the language value. See `Choosing a Locale with the NLS_LANG Environment Variable `__ If the ``NLS_LANG`` environment variable is set in the application with ``os.environ['NLS_LANG']``, it must be set before any connection pool is created, or before any standalone connections are created. Other Oracle globalization variables, such as ``NLS_DATE_FORMAT`` can also be set to change the behavior of cx_Oracle, see `Setting NLS Parameters `__. python-cx_Oracle-8.3.0/doc/src/user_guide/ha.rst000066400000000000000000000232461414105416400215600ustar00rootroot00000000000000.. _highavailability: ******************************** High Availability with cx_Oracle ******************************** Applications can utilize many features for high availability (HA) during planned and unplanned outages in order to: * Reduce application downtime * Eliminate compromises between high availability and performance * Increase operational productivity .. _harecommend: General HA Recommendations -------------------------- General recommendations for creating highly available cx_Oracle programs are: * Tune operating system and Oracle Network parameters to avoid long TCP timeouts, to prevent firewalls killing connections, and to avoid connection storms. * Implement application error handling and recovery. * Use the most recent version of the Oracle client libraries. New versions have improvements to features such as dead database server detection, and make it easier to set connection options. * Use the most recent version of Oracle Database. New database versions introduce, and enhance, features such as Application Continuity (AC) and Transparent Application Continuity (TAC). * Utilize Oracle Database technologies such as `RAC `__ or standby databases. * Configure database services to emit :ref:`FAN ` events. * Use a :ref:`connection pool `, because pools can handle database events and take proactive and corrective action for draining, run time load balancing, and fail over. Set the minimum and maximum pool sizes to the same values to avoid connection storms. Remove resource manager or user profiles that prematurely close sessions. * Test all scenarios thoroughly. .. _hanetwork: Network Configuration --------------------- The operating system TCP and :ref:`Oracle Net configuration ` should be configured for performance and availability. Options such as `SQLNET.OUTBOUND_CONNECT_TIMEOUT `__, `SQLNET.RECV_TIMEOUT `__ and `SQLNET.SEND_TIMEOUT `__ can be explored. `Oracle Net Services `__ options may also be useful for high availability and performance tuning. For example the database's `listener.ora` file can have `RATE_LIMIT `__ and `QUEUESIZE `__ parameters that can help handle connection storms. With Oracle Client 19c, `EXPIRE_TIME `__ can be used in :ref:`tnsnames.ora ` connect descriptors to prevent firewalls from terminating idle connections and to adjust keepalive timeouts. The general recommendation for ``EXPIRE_TIME`` is to use a value that is slightly less than half of the termination period. In older versions of Oracle Client, a ``tnsnames.ora`` connect descriptor option `ENABLE=BROKEN `_ can be used instead of ``EXPIRE_TIME``. These settings can also aid detection of a terminated remote database server. When cx_Oracle uses :ref:`Oracle Client libraries 19c `, then the :ref:`Easy Connect Plus syntax ` syntax enables some options to be used without needing a ``sqlnet.ora`` file. For example, if your firewall times out every 4 minutes, and you cannot alter the firewall settings, then you may decide to use ``EXPIRE_TIME`` in your connect string to send a probe every 2 minutes to the database to keep connections 'alive':: connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1?expire_time=2") .. _fan: Fast Application Notification (FAN) ----------------------------------- Users of `Oracle Database FAN `__ must connect to a FAN-enabled database service. The application should have ``events`` set to True when connecting. This value can also be changed via :ref:`Oracle Client Configuration `. FAN support is useful for planned and unplanned outages. It provides immediate notification to cx_Oracle following outages related to the database, computers, and networks. Without FAN, cx_Oracle can hang until a TCP timeout occurs and an error is returned, which might be several minutes. FAN allows cx_Oracle to provide high availability features without the application being aware of an outage. Unused, idle connections in a :ref:`connection pool ` will be automatically cleaned up. A future :meth:`SessionPool.acquire()` call will establish a fresh connection to a surviving database instance without the application being aware of any service disruption. To handle errors that affect active connections, you can add application logic to re-connect (this will connect to a surviving database instance) and replay application logic without having to return an error to the application user. FAN benefits users of Oracle Database's clustering technology `Oracle RAC `__ because connections to surviving database instances can be immediately made. Users of Oracle's Data Guard with a broker will get FAN events generated when the standby database goes online. Standalone databases will send FAN events when the database restarts. For a more information on FAN see the `white paper on Fast Application Notification `__. .. _appcont: Application Continuity (AC) --------------------------- Oracle Application Continuity and Transparent Application Continuity are Oracle Database technologies that record application interaction with the database and, in the event of a database instance outage, attempt to replay the interaction on a surviving database instance. If successful, users will be unaware of any database issue. AC and TAC are best suited for OLTP applications. When AC or TAC are configured on the database service, they are transparently available to cx_Oracle applications. You must thoroughly test your application because not all lower level calls in the cx_Oracle implementation can be replayed. See `OCI and Application Continuity `__ for more information. .. _tg: Transaction Guard ----------------- cx_Oracle supports `Transaction Guard `__ which enables Python application to verify the success or failure of the last transaction in the event of an unplanned outage. This feature is available when both client and database are 12.1 or higher. Using Transaction Guard helps to: * Preserve the commit outcome * Ensure a known outcome for every transaction See `Oracle Database Development Guide `__ for more information about using Transaction Guard. When an error occurs during commit, the Python application can acquire the logical transaction id (``ltxid``) from the connection and then call a procedure to determine the outcome of the commit for this logical transaction id. Follow the steps below to use the Transaction Guard feature in Python: 1. Grant execute privileges to the database users who will be checking the outcome of the commit. Login as SYSDBA and run the following command: .. code-block:: sql GRANT EXECUTE ON DBMS_APP_CONT TO ; 2. Create a new service by executing the following PL/SQL block as SYSDBA. Replace the ````, ```` and ```` values with suitable values. It is important that the ``COMMIT_OUTCOME`` parameter be set to true for Transaction Guard to function properly. .. code-block:: sql DECLARE t_Params dbms_service.svc_parameter_array; BEGIN t_Params('COMMIT_OUTCOME') := 'true'; t_Params('RETENTION_TIMEOUT') := ; DBMS_SERVICE.CREATE_SERVICE('', '', t_Params); END; / 3. Start the service by executing the following PL/SQL block as SYSDBA: .. code-block:: sql BEGIN DBMS_SERVICE.start_service(''); END; / Ensure the service is running by examining the output of the following query: .. code-block:: sql SELECT name, network_name FROM V$ACTIVE_SERVICES ORDER BY 1; **Python Application code requirements to use Transaction Guard** In the Python application code: * Use the connection attribute :attr:`~Connection.ltxid` to determine the logical transaction id. * Call the ``DBMS_APP_CONT.GET_LTXID_OUTCOME`` PL/SQL procedure with the logical transaction id acquired from the connection attribute. This returns a boolean value indicating if the last transaction was committed and whether the last call was completed successfully or not. See the `Transaction Guard Sample `__ for further details. python-cx_Oracle-8.3.0/doc/src/user_guide/initialization.rst000066400000000000000000000456441414105416400242250ustar00rootroot00000000000000.. _initialization: ************************** cx_Oracle 8 Initialization ************************** The cx_Oracle module loads Oracle Client libraries which communicate over Oracle Net to an existing database. The Oracle Client libraries need to be installed separately. See :ref:`installation`. Oracle Net is not a separate product: it is how the Oracle Client and Oracle Database communicate. .. figure:: /images/cx_Oracle_arch.png cx_Oracle Architecture .. _libinit: Locating the Oracle Client Libraries ==================================== cx_Oracle dynamically loads the Oracle Client libraries using a search heuristic. Only the first set of libraries found are loaded. The libraries can be in an installation of Oracle Instant Client, in a full Oracle Client installation, or in an Oracle Database installation (if Python is running on the same machine as the database). The versions of Oracle Client and Oracle Database do not have to be the same. For certified configurations see Oracle Support's `Doc ID 207303.1 `__. .. _wininit: * On Windows, cx_Oracle looks for the Oracle Client libraries as follows: - In the ``lib_dir`` directory specified in a call to :meth:`cx_Oracle.init_oracle_client()`. This directory should contain the libraries from an unzipped Instant Client 'Basic' or 'Basic Light' package. If you pass the library directory from a full client or database installation, such as Oracle Database "XE" Express Edition, then you will need to have previously set your environment to use that software installation, otherwise files such as message files will not be located. On Windows when the path contains backslashes, use a 'raw' string like ``lib_dir=r"C:\instantclient_19_6"``. If the Oracle Client libraries cannot be loaded from ``lib_dir``, then an exception is raised. - If ``lib_dir`` was not specified, then Oracle Client libraries are looked for in the directory where the cx_Oracle binary module is installed. This directory should contain the libraries from an unzipped Instant Client 'Basic' or 'Basic Light' package. If the libraries are not found, no exception is raised and the search continues, see next bullet point. - In the directories on the system library search path, e.g. the ``PATH`` environment variable. If the Oracle Client libraries cannot be loaded, then an exception is raised. .. _macinit: * On macOS, cx_Oracle looks for the Oracle Client libraries as follows: - In the ``lib_dir`` directory specified in a call to :meth:`cx_Oracle.init_oracle_client()`. This directory should contain the libraries from an unzipped Instant Client 'Basic' or 'Basic Light' package. If the Oracle Client libraries cannot be loaded from ``lib_dir``, then an exception is raised. - If ``lib_dir`` was not specified, then Oracle Client libraries are looked for in the directory where the cx_Oracle binary module is. This directory should contain the libraries from an unzipped Instant Client 'Basic' or 'Basic Light' package. For example if ``/Users/your_username/Library/Python/3.8/lib/python/site-packages`` contains ``cx_Oracle.cpython-38-darwin.so``, then you could run ``ln -s ~/instantclient_19_3/libclntsh.dylib ~/Library/Python/3.8/lib/python/site-packages``. If the libraries are not found, no exception is raised and the search continues, see next bullet point. - In the directories on the system library search path, e.g. ``~/lib/`` and ``/usr/local/lib``, or in ``$DYLD_LIBRARY_PATH``. These paths will vary with macOS version and Python version. Any value in ``DYLD_LIBRARY_PATH`` will not propagate to a sub-shell. If the Oracle Client libraries cannot be loaded, then an exception is raised. .. _linuxinit: * On Linux and related platforms, cx_Oracle looks for the Oracle Client libraries as follows: - In the ``lib_dir`` directory specified in a call to :meth:`cx_Oracle.init_oracle_client()`. **Note this is only useful to force immediate loading of the libraries because on Linux and related platforms the libraries must always be in the system library search path**. The ``lib_dir`` directory should contain the libraries from an unzipped Instant Client 'Basic' or 'Basic Light' package. If you pass the library directory from a full client or database installation, such as Oracle Database "XE" Express Edition then you will need to have previously set the ``ORACLE_HOME`` environment variable. If the Oracle Client libraries cannot be loaded from ``lib_dir``, then an exception is raised. - If ``lib_dir`` was not specified, then Oracle Client libraries are looked for in the operating system library search path, such as configured with ``ldconfig`` or set in the environment variable ``LD_LIBRARY_PATH``. On some UNIX platforms an OS specific equivalent, such as ``LIBPATH`` or ``SHLIB_PATH`` is used instead of ``LD_LIBRARY_PATH``. If the libraries are not found, no exception is raised and the search continues, see next bullet point. - In ``$ORACLE_HOME/lib``. Note the environment variable ``ORACLE_HOME`` should only ever be set when you have a full database installation or full client installation. It should not be set if you are using Oracle Instant Client. The ``ORACLE_HOME`` variable, and other necessary variables, should be set before starting Python. See :ref:`envset`. If the Oracle Client libraries cannot be loaded, then an exception is raised. If you call :meth:`cx_Oracle.init_oracle_client()` with a ``lib_dir`` parameter, the Oracle Client libraries are loaded immediately from that directory. If you call :meth:`cx_Oracle.init_oracle_client()` but do *not* set the ``lib_dir`` parameter, the Oracle Client libraries are loaded immediately using the search heuristic above. If you do not call :meth:`cx_Oracle.init_oracle_client()`, then the libraries are loaded using the search heuristic when the first cx_Oracle function that depends on the libraries is called, for example when a connection pool is created. If there is a problem loading the libraries, then an exception is raised. Make sure the Python process has directory and file access permissions for the Oracle Client libraries. On Linux ensure a ``libclntsh.so`` file exists. On macOS ensure a ``libclntsh.dylib`` file exists. cx_Oracle will not directly load ``libclntsh.*.XX.1`` files in ``lib_dir`` or from the directory where the cx_Oracle binary module is. Note other libraries used by ``libclntsh*`` are also required. To trace the loading of Oracle Client libraries, the environment variable ``DPI_DEBUG_LEVEL`` can be set to 64 before starting Python. For example, on Linux, you might use:: $ export DPI_DEBUG_LEVEL=64 $ python myapp.py 2> log.txt .. _usinginitoracleclient: Using cx_Oracle.init_oracle_client() to set the Oracle Client directory ----------------------------------------------------------------------- Applications can call the function :meth:`cx_Oracle.init_oracle_client()` to specify the directory containing Oracle Instant Client libraries. The Oracle Client Libraries are loaded when ``init_oracle_client()`` is called. For example, if the Oracle Instant Client Libraries are in ``C:\oracle\instantclient_19_9`` on Windows or ``$HOME/Downloads/instantclient_19_8`` on macOS, then you can use: .. code-block:: python import cx_Oracle import sys import os try: if sys.platform.startswith("darwin"): lib_dir = os.path.join(os.environ.get("HOME"), "Downloads", "instantclient_19_8") cx_Oracle.init_oracle_client(lib_dir=lib_dir) elif sys.platform.startswith("win32"): lib_dir=r"C:\oracle\instantclient_19_9" cx_Oracle.init_oracle_client(lib_dir=lib_dir) except Exception as err: print("Whoops!") print(err); sys.exit(1); Note the use of a 'raw' string ``r"..."`` on Windows so that backslashes are treated as directory separators. The :meth:`~cx_Oracle.init_oracle_client()` function can only be called once. **Note if you set** ``lib_dir`` **on Linux and related platforms, you must still have configured the system library search path to include that directory before starting Python**. On any operating system, if you set ``lib_dir`` to the library directory of a full database or full client installation, you will need to have previously set the Oracle environment, for example by setting the ``ORACLE_HOME`` environment variable. Otherwise you will get errors like ORA-1804. You should set this, and other Oracle environment variables, before starting Python, as shown in :ref:`envset`. .. _optnetfiles: Optional Oracle Net Configuration Files ======================================= Optional Oracle Net configuration files are read when cx_Oracle is loaded. These files affect connections and applications. The common files are: * ``tnsnames.ora``: A configuration file that defines databases addresses for establishing connections. See :ref:`Net Service Name for Connection Strings `. * ``sqlnet.ora``: A profile configuration file that may contain information on features such as connection failover, network encryption, logging, and tracing. See `Oracle Net Services Reference `__ for more information. The files should be in a directory accessible to Python, not on the database server host. For example, if the file ``/etc/my-oracle-config/tnsnames.ora`` should be used, you can call :meth:`cx_Oracle.init_oracle_client()`: .. code-block:: python import cx_Oracle import sys try: cx_Oracle.init_oracle_client(config_dir="/etc/my-oracle-config") except Exception as err: print("Whoops!") print(err); sys.exit(1); This is equivalent to setting the environment variable `TNS_ADMIN `__ to ``/etc/my-oracle-config``. If :meth:`~cx_Oracle.init_oracle_client()` is not called, or it is called but ``config_dir`` is not specified, then default directories searched for the configuration files. They include: * ``$TNS_ADMIN`` * ``/opt/oracle/instantclient_19_6/network/admin`` if Instant Client is in ``/opt/oracle/instantclient_19_6``. * ``/usr/lib/oracle/19.6/client64/lib/network/admin`` if Oracle 19.6 Instant Client RPMs are used on Linux. * ``$ORACLE_HOME/network/admin`` if cx_Oracle is using libraries from a database installation. A wallet configuration file ``cwallet.sso`` for secure connection can be located with, or separately from, the ``tnsnames.ora`` and ``sqlnet.ora`` files. It should be securely stored. The ``sqlnet.ora`` file's ``WALLET_LOCATION`` path should be set to the directory containing ``cwallet.sso``. For Oracle Autonomous Database use of wallets, see :ref:`autononmousdb`. Note the :ref:`easyconnect` can set many common configuration options without needing ``tnsnames.ora`` or ``sqlnet.ora`` files. The section :ref:`Network Configuration ` has some discussion about Oracle Net configuration. .. _optclientfiles: Optional Oracle Client Configuration Files ========================================== When cx_Oracle uses Oracle Client libraries version 12.1, or later, an optional client parameter file called ``oraaccess.xml`` can be used to configure some behviors of those libraries, such as statement caching and prefetching. This can be useful if the application cannot be altered. The file is read from the same directory as the `Optional Oracle Net Configuration Files`_. A sample ``oraaccess.xml`` file that sets the Oracle client ‘prefetch’ value to 1000 rows. This value affects every SQL query in the application:: 1000 Prefetching is the number of additional rows the underlying Oracle client library fetches whenever cx_Oracle requests query data from the database. Prefetching is a tuning option to maximize data transfer efficiency and minimize :ref:`round-trips ` to the database. The prefetch size does not affect when, or how many, rows are returned by cx_Oracle to the application. The cache management is transparently handled by the Oracle client libraries. Note, standard cx_Oracle fetch tuning is via :attr:`Cursor.arraysize`, but changing the prefetch value can be useful in some cases such as when modifying the application is not feasible. The `oraaccess.xml` file has other uses including: - Changing the value of Fast Application Notification :ref:`FAN ` events which affects notifications and Runtime Load Balancing (RLB). - Configuring `Client Result Caching `__ parameters - Turning on `Client Statement Cache Auto-tuning `__ Refer to the documentation on `oraaccess.xml `__ for more details. .. _envset: Oracle Environment Variables ============================ Some common environment variables that influence cx_Oracle are shown below. The variables that may be needed depend on how Python is installed, how you connect to the database, and what optional settings are desired. It is recommended to set Oracle variables in the environment before invoking Python, however they may also be set in the application with ``os.putenv()`` before the first connection is established. System environment variables like ``LD_LIBRARY_PATH`` must be set before Python starts. .. list-table:: Common Oracle environment variables :header-rows: 1 :widths: 1 2 :align: left * - Oracle Environment Variables - Purpose * - LD_LIBRARY_PATH - The library search path for platforms like Linux should include the Oracle libraries, for example ``$ORACLE_HOME/lib`` or ``/opt/instantclient_19_3``. This variable is not needed if the libraries are located by an alternative method, such as with ``ldconfig``. On other UNIX platforms you may need to set an OS specific equivalent, such as ``LIBPATH`` or ``SHLIB_PATH``. * - PATH - The library search path for Windows should include the location where ``OCI.DLL`` is found. Not needed if you set ``lib_dir`` in a call to :meth:`cx_Oracle.init_oracle_client()` * - TNS_ADMIN - The directory of optional Oracle Client configuration files such as ``tnsnames.ora`` and ``sqlnet.ora``. Not needed if the configuration files are in a default location or if ``config_dir`` was not used in :meth:`cx_Oracle.init_oracle_client()`. See :ref:`optnetfiles`. * - ORA_SDTZ - The default session time zone. * - ORA_TZFILE - The name of the Oracle time zone file to use. See below. * - ORACLE_HOME - The directory containing the Oracle Database software. The directory and various configuration files must be readable by the Python process. This variable should not be set if you are using Oracle Instant Client. * - NLS_LANG - Determines the 'national language support' globalization options for cx_Oracle. Note: from cx_Oracle 8, the character set component is ignored and only the language and territory components of ``NLS_LANG`` are used. The character set can instead be specified during connection or connection pool creation. See :ref:`globalization`. * - NLS_DATE_FORMAT, NLS_TIMESTAMP_FORMAT - Often set in Python applications to force a consistent date format independent of the locale. The variables are ignored if the environment variable ``NLS_LANG`` is not set. Oracle Instant Client includes a small and big time zone file, for example ``timezone_32.dat`` and ``timezlrg_32.dat``. The versions can be shown by running the utility ``genezi -v`` located in the Instant Client directory. The small file contains only the most commonly used time zones. By default the larger ``timezlrg_n.dat`` file is used. If you want to use the smaller ``timezone_n.dat`` file, then set the ``ORA_TZFILE`` environment variable to the name of the file without any directory prefix, for example ``export ORA_TZFILE=timezone_32.dat``. With Oracle Instant Client 12.2 or later, you can also use an external time zone file. Create a subdirectory ``oracore/zoneinfo`` under the Instant Client directory, and move the file into it. Then set ``ORA_TZFILE`` to the file name, without any directory prefix. The ``genezi -v`` utility will show the time zone file in use. If cx_Oracle is using Oracle Client libraries from an Oracle Database or full Oracle Client software installation, and you want to use a non-default time zone file, then set ``ORA_TZFILE`` to the file name with a directory prefix, for example: ``export ORA_TZFILE=/opt/oracle/myconfig/timezone_31.dat``. The Oracle Database documentation contains more information about time zone files, see `Choosing a Time Zone File `__. .. _otherinit: Other cx_Oracle Initialization ============================== The :meth:`cx_Oracle.init_oracle_client()` function allows ``driver_name`` and ``error_url`` parameters to be set. These are useful for applications whose end-users are not aware cx_Oracle is being used. An example of setting the parameters is: .. code-block:: python import cx_Oracle import sys try: cx_Oracle.init_oracle_client(driver_name="My Great App : 3.1.4", error_url="https://example.com/MyInstallInstructions.html") except Exception as err: print("Whoops!") print(err); sys.exit(1); The convention for ``driver_name`` is to separate the product name from the product version by a colon and single blank characters. The value will be shown in Oracle Database views like ``V$SESSION_CONNECT_INFO``. If this parameter is not specified, then the value "cx_Oracle : *version*" is used. The ``error_url`` string will be shown in the exception raised if ``init_oracle_client()`` cannot load the Oracle Client libraries. This allows applications that use cx_Oracle to refer users to application-specific installation instructions. If this value is not specified, then the :ref:`installation` URL is used. python-cx_Oracle-8.3.0/doc/src/user_guide/installation.rst000066400000000000000000001076401414105416400236720ustar00rootroot00000000000000.. _installation: ************************ cx_Oracle 8 Installation ************************ Overview ======== To use cx_Oracle 8 with Python and Oracle Database you need: - Python 3.5 and higher. Older versions of cx_Oracle may work with older versions of Python. - Oracle Client libraries. These can be from the free `Oracle Instant Client `__, from a full Oracle Client installation, or from those included in Oracle Database if Python is on the same machine as the database. Oracle client libraries versions 21, 19, 18, 12, and 11.2 are supported where available on Linux, Windows and macOS (Intel x86). Users have also reported success with other platforms. Use the latest client possible: Oracle's standard client-server version interoperability allows connection to both older and newer databases. - An Oracle Database, either local or remote. The cx_Oracle module loads Oracle Client libraries which communicate over Oracle Net to an existing database. Oracle Net is not a separate product: it is how the Oracle Client and Oracle Database communicate. .. figure:: /images/cx_Oracle_arch.png cx_Oracle Architecture Quick Start cx_Oracle Installation ================================== The `Quick Start: Developing Python Applications for Oracle Database `__ and `Quick Start: Developing Python Applications for Oracle Autonomous Database `__ instructions have steps for Windows, Linux, and macOS. Alternatively you can: - Install `Python `__ 3, if not already available. On macOS you must always install your own Python. Python 3.5 and higher are supported by cx_Oracle 8. If you use Python 2, then the older cx_Oracle 7.3 will install. - Install cx_Oracle from `PyPI `__ with: .. code-block:: shell python -m pip install cx_Oracle --upgrade Note: if a binary wheel package is not available for your platform, the source package will be downloaded instead. This will be compiled and the resulting binary installed. The ``--user`` option may be useful, if you don't have permission to write to system directories: .. code-block:: shell python -m pip install cx_Oracle --upgrade --user If you are behind a proxy, add a proxy server to the command, for example add ``--proxy=http://proxy.example.com:80`` - Add Oracle 21, 19, 18, 12 or 11.2 client libraries to your operating system library search path such as ``PATH`` on Windows or ``LD_LIBRARY_PATH`` on Linux. On macOS use :meth:`~cx_Oracle.init_oracle_client()` in your application to pass the Oracle Client directory name, see :ref:`usinginitoracleclient`. This is also usable on Windows. To get the libraries: - If your database is on a remote computer, then download and unzip the client libraries from the free `Oracle Instant Client `__ "Basic" or "Basic Light" package for your operating system architecture. Instant Client on Windows requires an appropriate Microsoft Windows Redistributables, see :ref:`wininstall`. On Linux, the ``libaio`` (sometimes called ``libaio1``) package is needed. Oracle Linux 8 also needs the ``libnsl`` package. - Alternatively, use the client libraries already available in a locally installed database such as the free `Oracle Database Express Edition ("XE") `__ release. Version 21 client libraries can connect to Oracle Database 12.1 or greater. Version 19, 18 and 12.2 client libraries can connect to Oracle Database 11.2 or greater. Version 12.1 client libraries can connect to Oracle Database 10.2 or greater. Version 11.2 client libraries can connect to Oracle Database 9.2 or greater. - Create a script like the one below: .. code-block:: python # myscript.py import cx_Oracle # Connect as user "hr" with password "welcome" to the "orclpdb1" service running on this computer. connection = cx_Oracle.connect(user="hr", password="welcome", dsn="localhost/orclpdb1") cursor = connection.cursor() cursor.execute(""" SELECT first_name, last_name FROM employees WHERE department_id = :did AND employee_id > :eid""", did = 50, eid = 190) for fname, lname in cursor: print("Values:", fname, lname) Locate your Oracle Database username and password, and the database connection string. The connection string is commonly of the format ``hostname/servicename``, using the hostname where the database is running, and using the service name of the Oracle Database instance. Substitute your username, password and connection string in the code. Run the Python script, for example:: python myscript.py You can learn how to use cx_Oracle from the :ref:`API documentation ` and `samples `__. If you run into installation trouble, check out the section on `Troubleshooting`_. Oracle Client and Oracle Database Interoperability ================================================== cx_Oracle requires Oracle Client libraries. The libraries provide the necessary network connectivity to access an Oracle Database instance. They also provide basic and advanced connection management and data features to cx_Oracle. The simplest way to get Oracle Client libraries is to install the free `Oracle Instant Client `__ "Basic" or "Basic Light" package. The libraries are also available in any Oracle Database installation or full Oracle Client installation. Oracle's standard client-server network interoperability allows connections between different versions of Oracle Client libraries and Oracle Database. For certified configurations see Oracle Support's `Doc ID 207303.1 `__. In summary, Oracle Client 21 can connect to Oracle Database 12.1 or greater. Oracle Client 19, 18 and 12.2 can connect to Oracle Database 11.2 or greater. Oracle Client 12.1 can connect to Oracle Database 10.2 or greater. Oracle Client 11.2 can connect to Oracle Database 9.2 or greater. The technical restrictions on creating connections may be more flexible. For example Oracle Client 12.2 can successfully connect to Oracle Database 10.2. cx_Oracle uses the shared library loading mechanism available on each supported platform to load the Oracle Client libraries at runtime. It does not need to be rebuilt for different versions of the libraries. Since a single cx_Oracle binary can use different client versions and also access multiple database versions, it is important your application is tested in your intended release environments. Newer Oracle clients support new features, such as the `oraaccess.xml `__ external configuration file available with 12.1 or later clients, session pool improvements, improved high availability features, call timeouts, and `other enhancements `__. The cx_Oracle function :func:`~cx_Oracle.clientversion()` can be used to determine which Oracle Client version is in use. The attribute :attr:`Connection.version` can be used to determine which Oracle Database version a connection is accessing. These can then be used to adjust application behavior accordingly. Attempts to use Oracle features that are not supported by a particular client/server library combination will result in runtime errors. Installing cx_Oracle on Linux ============================= This section discusses the generic installation methods on Linux. To use Python and cx_Oracle RPM packages from yum on Oracle Linux, see :ref:`oraclelinux`. Install cx_Oracle ----------------- The generic way to install cx_Oracle on Linux is to use Python's `Pip `__ package to install cx_Oracle from `PyPI `__: .. code-block:: shell python -m pip install cx_Oracle --upgrade The ``--user`` option may be useful, if you don't have permission to write to system directories: .. code-block:: shell python -m pip install cx_Oracle --upgrade --user If you are behind a proxy, add a proxy server to the command, for example add ``--proxy=http://proxy.example.com:80`` This will download and install a pre-compiled binary `if one is available `__ for your architecture. If a pre-compiled binary is not available, the source will be downloaded, compiled, and the resulting binary installed. Compiling cx_Oracle requires the ``Python.h`` header file. If you are using the default ``python`` package, this file is in the ``python-devel`` package or equivalent. Install Oracle Client --------------------- Using cx_Oracle requires Oracle Client libraries to be installed. These provide the necessary network connectivity allowing cx_Oracle to access an Oracle Database instance. - If your database is on a remote computer, then download the free `Oracle Instant Client `__ "Basic" or "Basic Light" package for your operating system architecture. Use the RPM or ZIP packages, based on your preferences. - Alternatively, use the client libraries already available in a locally installed database such as the free `Oracle Database Express Edition ("XE") `__ release. Oracle Instant Client Zip Files +++++++++++++++++++++++++++++++ To use cx_Oracle with Oracle Instant Client zip files: 1. Download an Oracle 21, 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip file matching your Python 64-bit or 32-bit architecture: - `x86-64 64-bit `__ - `x86 32-bit `__ - `ARM (aarch64) 64-bit `__ The latest version is recommended. Oracle Instant Client 21 will connect to Oracle Database 12.1 or later. 2. Unzip the package into a single directory that is accessible to your application. For example: .. code-block:: shell mkdir -p /opt/oracle cd /opt/oracle unzip instantclient-basic-linux.x64-21.1.0.0.0.zip 3. Install the ``libaio`` package with sudo or as the root user. For example:: sudo yum install libaio On some Linux distributions this package is called ``libaio1`` instead. On recent Linux versions such as Oracle Linux 8, you may also need to install the ``libnsl`` package when using Oracle Instant Client 19. 4. If there is no other Oracle software on the machine that will be impacted, permanently add Instant Client to the runtime link path. For example, with sudo or as the root user: .. code-block:: shell sudo sh -c "echo /opt/oracle/instantclient_21_1 > /etc/ld.so.conf.d/oracle-instantclient.conf" sudo ldconfig Alternatively, set the environment variable ``LD_LIBRARY_PATH`` to the appropriate directory for the Instant Client version. For example:: export LD_LIBRARY_PATH=/opt/oracle/instantclient_21_1:$LD_LIBRARY_PATH 5. If you use optional Oracle configuration files such as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files in an accessible directory, for example in ``/opt/oracle/your_config_dir``. Then use: .. code-block:: python import cx_Oracle cx_Oracle.init_oracle_client(config_dir="/home/your_username/oracle/your_config_dir") Or set the environment variable ``TNS_ADMIN`` to that directory name. Alternatively, put the files in the ``network/admin`` subdirectory of Instant Client, for example in ``/opt/oracle/instantclient_21_1/network/admin``. This is the default Oracle configuration directory for executables linked with this Instant Client. Oracle Instant Client RPMs ++++++++++++++++++++++++++ To use cx_Oracle with Oracle Instant Client RPMs: 1. Download an Oracle 21,19, 18, 12, or 11.2 "Basic" or "Basic Light" RPM matching your Python architecture: - `x86-64 64-bit `__ - `x86 32-bit `__ - `ARM (aarch64) 64-bit `__ Oracle's yum server has convenient repositories: - `Instant Client 21 RPMs for Oracle Linux x86-64 8 `__, `Older Instant Client RPMs for Oracle Linux x86-64 8 `__ - `Instant Client 21 RPMs for Oracle Linux x86-64 7 `__, `Older Instant Client RPMs for Oracle Linux x86-64 7 `__ - `Instant Client RPMs for Oracle Linux x86-64 6 `__ - `Instant Client RPMs for Oracle Linux ARM (aarch64) 8 `__ - `Instant Client RPMs for Oracle Linux ARM (aarch64) 7 `__ The latest version is recommended. Oracle Instant Client 21 will connect to Oracle Database 12.1 or later. 2. Install the downloaded RPM with sudo or as the root user. For example: .. code-block:: shell sudo yum install oracle-instantclient-basic-21.1.0.0.0-1.x86_64.rpm Yum will automatically install required dependencies, such as ``libaio``. On recent Linux versions, such as Oracle Linux 8, you may need to manually install the ``libnsl`` package when using Oracle Instant Client 19. 3. For Instant Client 19, or later, the system library search path is automatically configured during installation. For older versions, if there is no other Oracle software on the machine that will be impacted, permanently add Instant Client to the runtime link path. For example, with sudo or as the root user: .. code-block:: shell sudo sh -c "echo /usr/lib/oracle/18.5/client64/lib > /etc/ld.so.conf.d/oracle-instantclient.conf" sudo ldconfig Alternatively, for version 18 and earlier, every shell running Python will need to have the environment variable ``LD_LIBRARY_PATH`` set to the appropriate directory for the Instant Client version. For example:: export LD_LIBRARY_PATH=/usr/lib/oracle/18.5/client64/lib:$LD_LIBRARY_PATH 4. If you use optional Oracle configuration files such as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files in an accessible directory, for example in ``/opt/oracle/your_config_dir``. Then use: .. code-block:: python import cx_Oracle cx_Oracle.init_oracle_client(config_dir="/opt/oracle/your_config_dir") Or set the environment variable ``TNS_ADMIN`` to that directory name. Alternatively, put the files in the ``network/admin`` subdirectory of Instant Client, for example in ``/usr/lib/oracle/21/client64/lib/network/admin``. This is the default Oracle configuration directory for executables linked with this Instant Client. Local Database or Full Oracle Client ++++++++++++++++++++++++++++++++++++ cx_Oracle applications can use Oracle Client 21, 19, 18, 12, or 11.2 libraries from a local Oracle Database or full Oracle Client installation. The libraries must be either 32-bit or 64-bit, matching your Python architecture. 1. Set required Oracle environment variables by running the Oracle environment script. For example: .. code-block:: shell source /usr/local/bin/oraenv For Oracle Database Express Edition ("XE") 11.2, run: .. code-block:: shell source /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh 2. Optional Oracle configuration files such as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` can be placed in ``$ORACLE_HOME/network/admin``. Alternatively, Oracle configuration files can be put in another, accessible directory. Then set the environment variable ``TNS_ADMIN`` to that directory name. .. _oraclelinux: Installing cx_Oracle RPMs on Oracle Linux ========================================= Python and cx_Oracle RPM packages are available from the `Oracle Linux yum server `__. Various versions of Python are easily installed. Using the yum server makes it easy to keep up to date. Installation instructions are at `Oracle Linux for Python Developers `__. .. _wininstall: Installing cx_Oracle on Windows =============================== Install cx_Oracle ----------------- Use Python's `Pip `__ package to install cx_Oracle from `PyPI `__:: python -m pip install cx_Oracle --upgrade If you are behind a proxy, specify your proxy server: .. code-block:: shell python -m pip install cx_Oracle --proxy=http://proxy.example.com:80 --upgrade This will download and install a pre-compiled binary `if one is available `__ for your architecture. If a pre-compiled binary is not available, the source will be downloaded, compiled, and the resulting binary installed. Install Oracle Client --------------------- Using cx_Oracle requires Oracle Client libraries to be installed. These provide the necessary network connectivity allowing cx_Oracle to access an Oracle Database instance. Oracle Client versions 19, 18, 12 and 11.2 are supported. - If your database is on a remote computer, then download the free `Oracle Instant Client `__ "Basic" or "Basic Light" package for your operating system architecture. - Alternatively, use the client libraries already available in a locally installed database such as the free `Oracle Database Express Edition ("XE") `__ release. Oracle Instant Client Zip Files +++++++++++++++++++++++++++++++ To use cx_Oracle with Oracle Instant Client zip files: 1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip file: `64-bit `__ or `32-bit `__, matching your Python architecture. The latest version is recommended. Oracle Instant Client 19 will connect to Oracle Database 11.2 or later. Windows 7 users: Note that Oracle 19c is not supported on Windows 7. 2. Unzip the package into a directory that is accessible to your application. For example unzip ``instantclient-basic-windows.x64-19.11.0.0.0dbru.zip`` to ``C:\oracle\instantclient_19_11``. 3. Oracle Instant Client libraries require a Visual Studio redistributable with a 64-bit or 32-bit architecture to match Instant Client's architecture. Each Instant Client version requires a different redistributable version: - For Instant Client 19 install `VS 2017 `__. - For Instant Client 18 or 12.2 install `VS 2013 `__ - For Instant Client 12.1 install `VS 2010 `__ - For Instant Client 11.2 install `VS 2005 64-bit `__ or `VS 2005 32-bit `__ Configure Oracle Instant Client ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. There are several alternative ways to tell cx_Oracle where your Oracle Client libraries are, see :ref:`initialization`. * With Oracle Instant Client you can use :meth:`~cx_Oracle.init_oracle_client()` in your application, for example: .. code-block:: python import cx_Oracle cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11") Note a 'raw' string is used because backslashes occur in the path. * Alternatively, add the Oracle Instant Client directory to the ``PATH`` environment variable. The directory must occur in ``PATH`` before any other Oracle directories. Restart any open command prompt windows. * Another way to set ``PATH`` is to use a batch file that sets it before Python is executed, for example:: REM mypy.bat SET PATH=C:\oracle\instantclient_19_9;%PATH% python %* Invoke this batch file every time you want to run Python. 2. If you use optional Oracle configuration files such as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files in an accessible directory, for example in ``C:\oracle\your_config_dir``. Then use: .. code-block:: python import cx_Oracle cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11", config_dir=r"C:\oracle\your_config_dir") Or set the environment variable ``TNS_ADMIN`` to that directory name. Alternatively, put the files in a ``network\admin`` subdirectory of Instant Client, for example in ``C:\oracle\instantclient_19_11\network\admin``. This is the default Oracle configuration directory for executables linked with this Instant Client. Local Database or Full Oracle Client ++++++++++++++++++++++++++++++++++++ cx_Oracle applications can use Oracle Client 19, 18, 12, or 11.2 libraries libraries from a local Oracle Database or full Oracle Client. The Oracle libraries must be either 32-bit or 64-bit, matching your Python architecture. 1. Set the environment variable ``PATH`` to include the path that contains ``OCI.DLL``, if it is not already set. Restart any open command prompt windows. 2. Optional Oracle configuration files such as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` can be placed in the ``network\admin`` subdirectory of the Oracle Database software installation. Alternatively, pass ``config_dir`` to :meth:`~cx_Oracle.init_oracle_client()` as shown in the previous section, or set ``TNS_ADMIN`` to the directory name. Installing cx_Oracle on macOS (Intel x86) ========================================= Install Python -------------- Make sure you are not using the bundled Python. This has restricted entitlements and will fail to load Oracle client libraries. Instead use `Homebrew `__ or `Python.org `__. A C compiler is needed, for example Xcode and its command line tools. Install cx_Oracle ----------------- Use Python's `Pip `__ package to install cx_Oracle from `PyPI `__: .. code-block:: shell export ARCHFLAGS="-arch x86_64" python -m pip install cx_Oracle --upgrade The ``--user`` option may be useful, if you don't have permission to write to system directories: .. code-block:: shell python -m pip install cx_Oracle --upgrade --user If you are behind a proxy, add a proxy server to the command, for example add ``--proxy=http://proxy.example.com:80`` The source will be downloaded, compiled, and the resulting binary installed. Install Oracle Instant Client ----------------------------- Oracle Instant Client provides the network connectivity for accessing Oracle Database. Manual Installation +++++++++++++++++++ * Download the **Basic** 64-bit DMG from `Oracle `__. * In Finder, double click on the DMG to mount it. * Open a terminal window and run the install script in the mounted package, for example: .. code-block:: shell /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh This copies the contents to ``$HOME/Downloads/instantclient_19_8``. Applications may not have access to the ``Downloads`` directory, so you should move Instant Client somewhere convenient. * In Finder, eject the mounted Instant Client package. If you have multiple Instant Client DMG packages mounted, you only need to run ``install_ic.sh`` once. It will copy all mounted Instant Client DMG packages at the same time. Scripted Installation +++++++++++++++++++++ Instant Client installation can alternatively be scripted, for example: .. code-block:: shell cd $HOME/Downloads curl -O https://download.oracle.com/otn_software/mac/instantclient/198000/instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg hdiutil mount instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh hdiutil unmount /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru The Instant Client directory will be ``$HOME/Downloads/instantclient_19_8``. Applications may not have access to the ``Downloads`` directory, so you should move Instant Client somewhere convenient. Configure Oracle Instant Client ------------------------------- 1. Call :meth:`~cx_Oracle.init_oracle_client()` once in your application: .. code-block:: python import cx_Oracle cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8") 2. If you use optional Oracle configuration files such as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with Oracle Instant Client, then put the files in an accessible directory, for example in ``/Users/your_username/oracle/your_config_dir``. Then use: .. code-block:: python import cx_Oracle cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8", config_dir="/Users/your_username/oracle/your_config_dir") Or set the environment variable ``TNS_ADMIN`` to that directory name. Alternatively, put the files in the ``network/admin`` subdirectory of Oracle Instant Client, for example in ``/Users/your_username/Downloads/instantclient_19_8/network/admin``. This is the default Oracle configuration directory for executables linked with this Instant Client. Linux Containers ================ Sample Dockerfiles are on `GitHub `__. Pre-built images for Python and cx_Oracle are in the `GitHub Container Registry `__. These are easily used. For example, to pull an Oracle Linux 8 image with Python 3.6 and cx_Oracle, execute:: docker pull ghcr.io/oracle/oraclelinux7-python:3.6-oracledb Installing cx_Oracle without Internet Access ============================================ To install cx_Oracle on a computer that is not connected to the internet, download the appropriate cx_Oracle file from `PyPI `__. Transfer this file to the offline computer and install it with:: python -m pip install "" Then follow the general cx_Oracle platform installation instructions to install Oracle client libraries. Install Using GitHub ==================== In order to install using the source on GitHub, use the following commands:: git clone https://github.com/oracle/python-cx_Oracle.git cx_Oracle cd cx_Oracle git submodule init git submodule update python setup.py install Note that if you download a source zip file directly from GitHub then you will also need to download an `ODPI-C `__ source zip file and extract it inside the directory called "odpi". cx_Oracle source code is also available from opensource.oracle.com. This can be cloned with:: git clone git://opensource.oracle.com/git/oracle/python-cx_Oracle.git cx_Oracle cd cx_Oracle git submodule init git submodule update Install Using Source from PyPI ============================== The source package can be downloaded manually from `PyPI `__ and extracted, after which the following commands should be run:: python setup.py build python setup.py install Upgrading from Older Versions ============================= Review the :ref:`release notes ` and :ref:`Deprecations ` for changes. Modify affected code. If you are upgrading from cx_Oracle 7 note these changes: - The default character set used by cx_Oracle 8 is now "UTF-8". Also, the character set component of the ``NLS_LANG`` environment variable is ignored. If you need to change the character set, then pass ``encoding`` and ``nendcoding`` parameters when creating a connection or connection pool. See :ref:`globalization`. - Any uses of ``type(var)`` need to be changed to ``var.type``. - Any uses of ``var.type is not None`` need to be changed to ``isinstance(var.type, cx_Oracle.ObjectType)`` - Note that ``TIMESTAMP WITH TIME ZONE`` columns will now be reported as :data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` instead of :data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`. - Note that ``TIMESTAMP WITH LOCAL TIME ZONE`` columns will now be reported as :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` instead of :data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`. - Note that ``BINARY_FLOAT`` columns will now be reported as :data:`cx_Oracle.DB_TYPE_BINARY_FLOAT` instead of :data:`cx_Oracle.NATIVE_DOUBLE` in :data:`Cursor.description`. If you are upgrading from cx_Oracle 5 note these installation changes: - When using Oracle Instant Client, you should not set ``ORACLE_HOME``. - On Linux, cx_Oracle 6 and higher no longer uses Instant Client RPMs automatically. You must set ``LD_LIBRARY_PATH`` or use ``ldconfig`` to locate the Oracle client library. - PyPI no longer allows Windows installers or Linux RPMs to be hosted. Use the supplied cx_Oracle Wheels instead, or use RPMs from Oracle, see :ref:`oraclelinux`. .. _python2: Installing cx_Oracle in Python 2 ================================ cx_Oracle 7.3 was the last version with support for Python 2. If you install cx_Oracle in Python 2 using the commands provided above, then cx_Oracle 7.3 will be installed. This is equivalent to using a command like:: python -m pip install cx_Oracle==7.3 --upgrade --user For other installation options such as installing through a proxy, see instructions above. Make sure the Oracle Client libraries are in the system library search path because cx_Oracle 7 does not support the :meth:`cx_Oracle.init_oracle_client()` method and does not support loading the Oracle Client libraries from the directory containing the cx_Oracle module binary. Installing cx_Oracle 5.3 ======================== If you require cx_Oracle 5.3, download a Windows installer from `PyPI `__ or use ``python -m pip install cx-oracle==5.3`` to install from source. Very old versions of cx_Oracle can be found in the files section at `SourceForce `__. Troubleshooting =============== If installation fails: - Use option ``-v`` with pip. Review your output and logs. Try to install using a different method. **Google anything that looks like an error.** Try some potential solutions. - Was there a network connection error? Do you need to set the environment variables ``http_proxy`` and/or ``https_proxy``? Or try ``pip install --proxy=http://proxy.example.com:80 cx_Oracle --upgrade``? - If upgrading gave no errors but the old version is still installed, try ``pip install cx_Oracle --upgrade --force-reinstall`` - If you do not have access to modify your system version of Python, can you use ``pip install cx_Oracle --upgrade --user`` or venv? - Do you get the error "``No module named pip``"? The pip module is builtin to Python but is sometimes removed by the OS. Use the venv module (builtin to Python 3.x) or virtualenv module instead. - Do you get the error "``fatal error: dpi.h: No such file or directory``" when building from source code? Ensure that your source installation has a subdirectory called "odpi" containing files. If missing, review the section on `Install Using GitHub`_. If using cx_Oracle fails: - Do you get the error "``DPI-1047: Oracle Client library cannot be loaded``"? - On Windows and macOS, try using :meth:`~cx_Oracle.init_oracle_client()`. See :ref:`usinginitoracleclient`. - Check that Python and your Oracle Client libraries are both 64-bit, or both 32-bit. The ``DPI-1047`` message will tell you whether the 64-bit or 32-bit Oracle Client is needed for your Python. - Set the environment variable ``DPI_DEBUG_LEVEL`` to 64 and restart cx_Oracle. The trace messages will show how and where cx_Oracle is looking for the Oracle Client libraries. At a Windows command prompt, this could be done with:: set DPI_DEBUG_LEVEL=64 On Linux and macOS, you might use:: export DPI_DEBUG_LEVEL=64 - On Windows, if you used :meth:`~cx_Oracle.init_oracle_client()` and have a full database installation, make sure this database is the `currently configured database `__. - On Windows, if you are not using :meth:`~cx_Oracle.init_oracle_client()`, then restart your command prompt and use ``set PATH`` to check the environment variable has the correct Oracle Client listed before any other Oracle directories. - On Windows, use the ``DIR`` command to verify that ``OCI.DLL`` exists in the directory passed to ``init_oracle_client()`` or set in ``PATH``. - On Windows, check that the correct `Windows Redistributables `__ have been installed. - On Linux, check the ``LD_LIBRARY_PATH`` environment variable contains the Oracle Client library directory. If you are using Oracle Instant Client, a preferred alternative is to ensure a file in the ``/etc/ld.so.conf.d`` directory contains the path to the Instant Client directory, and then run ``ldconfig``. - On macOS, make sure you are not using the bundled Python (use `Homebrew `__ or `Python.org `__ instead). If you are not using :meth:`~cx_Oracle.init_oracle_client()`, then put the Oracle Instant Client libraries in ``~/lib`` or ``/usr/local/lib``. - If you got "``DPI-1072: the Oracle Client library version is unsupported``", then review the installation requirements. cx_Oracle needs Oracle client libraries 11.2 or later. Note that version 19 is not supported on Windows 7. Similar steps shown above for ``DPI-1047`` may help. - If you have multiple versions of Python installed, make sure you are using the correct python and pip (or python3 and pip3) executables. python-cx_Oracle-8.3.0/doc/src/user_guide/introduction.rst000066400000000000000000000123741414105416400237110ustar00rootroot00000000000000.. _introduction: ************************* Introduction to cx_Oracle ************************* cx_Oracle is a Python extension module that enables Python access to Oracle Database. It conforms to the `Python Database API v2.0 Specification `__ with a considerable number of additions and a couple of exclusions. Architecture ------------ Python programs call cx_Oracle functions. Internally cx_Oracle dynamically loads Oracle Client libraries to access Oracle Database. The database can be on the same machine as Python, or it can be remote. .. _archfig: .. figure:: /images/cx_Oracle_arch.png cx_Oracle Architecture cx_Oracle is typically installed from `PyPI `__ using `pip `__. The Oracle Client libraries need to be installed separately. The libraries can be from an installation of `Oracle Instant Client `__, from a full Oracle Client installation, or even from an Oracle Database installation (if Python is running on the same machine as the database). Oracle’s standard client-server version interoperability allows connection to both older and newer databases from different Client library versions, see :ref:`cx_Oracle Installation `. Some behaviors of the Oracle Client libraries can optionally be configured with an ``oraaccess.xml`` file, for example to enable auto-tuning of a statement cache. See :ref:`optclientfiles`. The Oracle Net layer can optionally be configured with files such as ``tnsnames.ora`` and ``sqlnet.ora``, for example to enable :ref:`network encryption `. See :ref:`optnetfiles`. Oracle environment variables that are set before cx_Oracle first creates a database connection will affect cx_Oracle behavior. Optional variables include NLS_LANG, NLS_DATE_FORMAT and TNS_ADMIN. See :ref:`envset`. Features -------- The cx_Oracle feature highlights are: * Easy installation from PyPI * Support for multiple Oracle Client and Database versions * Execution of SQL and PL/SQL statements * Extensive Oracle data type support, including large objects (CLOB and BLOB) and binding of SQL objects * Connection management, including connection pooling * Oracle Database High Availability features * Full use of Oracle Network Service infrastructure, including encrypted network traffic and security features A complete list of supported features can be seen `here `_. Getting Started --------------- Install cx_Oracle using the :ref:`installation ` steps. Create a script ``query.py`` as shown below: .. code-block:: python # query.py import cx_Oracle # Establish the database connection connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1") # Obtain a cursor cursor = connection.cursor() # Data for binding manager_id = 145 first_name = "Peter" # Execute the query sql = """SELECT first_name, last_name FROM employees WHERE manager_id = :mid AND first_name = :fn""" cursor.execute(sql, mid=manager_id, fn=first_name) # Loop over the result set for row in cursor: print(row) This uses Oracle's `sample HR schema `__. Simple :ref:`connection ` to the database requires a username, password and connection string. Locate your Oracle Database `user name and password `_ and the database :ref:`connection string `, and use them in ``query.py``. For cx_Oracle, the connection string is commonly of the format ``hostname/servicename``, using the host name where the database is running and the Oracle Database service name of the database instance. The :ref:`cursor ` is the object that allows statements to be executed and results (if any) fetched. The data values in ``managerId`` and ``firstName`` are 'bound' to the statement placeholder 'bind variables' ``:mid`` and ``:fn`` when the statement is executed. This separates the statement text from the data, which helps avoid SQL Injection security risks. :ref:`Binding ` is also important for performance and scalability. The cursor allows rows to be iterated over and displayed. Run the script:: python query.py The output is:: ('Peter', 'Hall') ('Peter', 'Tucker') Examples and Tutorials ---------------------- The `Quick Start: Developing Python Applications for Oracle Database `__ and `Quick Start: Developing Python Applications for Oracle Autonomous Database `__ instructions have steps for Windows, Linux, and macOS. Runnable examples are in the `GitHub samples directory `__. A `Python cx_Oracle tutorial `__ is also available. python-cx_Oracle-8.3.0/doc/src/user_guide/json_data_type.rst000066400000000000000000000215561414105416400241750ustar00rootroot00000000000000.. _jsondatatype: ******************************* Working with the JSON Data Type ******************************* Native support for JSON data was introduced in Oracle Database 12c. You can use JSON with relational database features, including transactions, indexing, declarative querying, and views. You can project JSON data relationally, making it available for relational processes and tools. Also see :ref:`Simple Oracle Document Access (SODA) `, which allows access to JSON documents through a set of NoSQL-style APIs. Prior to Oracle Database 21, JSON in relational tables is stored as BLOB, CLOB or VARCHAR2 data, allowing easy access with cx_Oracle. Oracle Database 21 introduced a dedicated JSON data type with a new `binary storage format `__ that improves performance and functionality. To use the new dedicated JSON type, the Oracle Database and Oracle Client libraries must be version 21, or later. Also cx_Oracle must be 8.1, or later. For more information about using JSON in Oracle Database see the `Database JSON Developer's Guide `__. In Oracle Database 21, to create a table with a column called ``JSON_DATA`` for JSON data: .. code-block:: sql create table customers ( id integer not null primary key, json_data json ); For older Oracle Database versions the syntax is: .. code-block:: sql create table customers ( id integer not null primary key, json_data blob check (json_data is json) ); The check constraint with the clause ``IS JSON`` ensures only JSON data is stored in that column. The older syntax can still be used in Oracle Database 21, however the recommendation is to move to the new JSON type. With the old syntax, the storage can be BLOB, CLOB or VARCHAR2. Of these, BLOB is preferred to avoid character set conversion overheads. Using Oracle Database 21 and Oracle Client 21 with cx_Oracle 8.1 (or later), you can insert by binding as shown below: .. code-block:: python import datetime json_data = [ 2.78, True, 'Ocean Beach', b'Some bytes', {'keyA': 1, 'KeyB': 'Melbourne'}, datetime.date.today() ] var = cursor.var(cx_Oracle.DB_TYPE_JSON) var.setvalue(0, json_data) cursor.execute("insert into customers values (:1, :2)", [123, var]) # or these two lines can replace the three previous lines cursor.setinputsizes(None, cx_Oracle.DB_TYPE_JSON) cursor.execute("insert into customers values (:1, :2)", [123, json_data]) Fetching with: .. code-block:: python for row in cursor.execute("SELECT c.json_data FROM customers c"): print(row) gives output like:: ([Decimal('2.78'), True, 'Ocean Beach', b'Some bytes', {'keyA': Decimal('1'), 'KeyB': 'Melbourne'}, datetime.datetime(2020, 12, 2, 0, 0)],) With the older BLOB storage, or to insert JSON strings, use: .. code-block:: python import json customer_data = dict(name="Rod", dept="Sales", location="Germany") cursor.execute("insert into customers (id, json_data) values (:1, :2)", [1, json.dumps(customer_data)]) IN Bind Type Mapping ==================== When binding to a JSON value, the type parameter for the variable must be specified as :data:`cx_Oracle.DB_TYPE_JSON`. Python values are converted to JSON values as shown in the following table. The 'SQL Equivalent' syntax can be used in SQL INSERT and UPDATE statements if specific attribute types are needed but there is no direct mapping from Python. .. list-table:: :header-rows: 1 :widths: 1 1 1 :align: left * - Python Type or Value - JSON Attribute Type or Value - SQL Equivalent Example * - None - null - NULL * - True - true - n/a * - False - false - n/a * - int - NUMBER - json_scalar(1) * - float - NUMBER - json_scalar(1) * - decimal.Decimal - NUMBER - json_scalar(1) * - str - VARCHAR2 - json_scalar('String') * - datetime.date - TIMESTAMP - json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD')) * - datetime.datetime - TIMESTAMP - json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD')) * - bytes - RAW - json_scalar(utl_raw.cast_to_raw('A raw value')) * - list - Array - json_array(1, 2, 3 returning json) * - dict - Object - json_object(key 'Fred' value json_scalar(5), key 'George' value json_scalar('A string') returning json) * - n/a - CLOB - json_scalar(to_clob('A short CLOB')) * - n/a - BLOB - json_scalar(to_blob(utl_raw.cast_to_raw('A short BLOB'))) * - n/a - DATE - json_scalar(to_date('2020-03-10', 'YYYY-MM-DD')) * - n/a - INTERVAL YEAR TO MONTH - json_scalar(to_yminterval('+5-9')) * - n/a - INTERVAL DAY TO SECOND - json_scalar(to_dsinterval('P25DT8H25M')) * - n/a - BINARY_DOUBLE - json_scalar(to_binary_double(25)) * - n/a - BINARY_FLOAT - json_scalar(to_binary_float(15.5)) An example of creating a CLOB attribute with key ``mydocument`` in a JSON column using SQL is: .. code-block:: python cursor.execute(""" insert into mytab (myjsoncol) values (json_object(key 'mydocument' value json_scalar(to_clob(:b)) returning json))""", ['A short CLOB']) When `mytab` is queried in cx_Oracle, the CLOB data will be returned as a Python string, as shown by the following table. Output might be like:: {mydocument: 'A short CLOB'} Query and OUT Bind Type Mapping =============================== When getting Oracle Database 21 JSON values from the database, the following attribute mapping occurs: .. list-table:: :header-rows: 1 :widths: 1 1 :align: left * - Database JSON Attribute Type or Value - Python Type or Value * - null - None * - false - False * - true - True * - NUMBER - decimal.Decimal * - VARCHAR2 - str * - RAW - bytes * - CLOB - str * - BLOB - bytes * - DATE - datetime.datetime * - TIMESTAMP - datetime.datetime * - INTERVAL YEAR TO MONTH - not supported * - INTERVAL DAY TO SECOND - datetime.timedelta * - BINARY_DOUBLE - float * - BINARY_FLOAT - float * - Arrays - list * - Objects - dict SQL/JSON Path Expressions ========================= Oracle Database provides SQL access to JSON data using SQL/JSON path expressions. A path expression selects zero or more JSON values that match, or satisfy, it. Path expressions can use wildcards and array ranges. A simple path expression is ``$.friends`` which is the value of the JSON field ``friends``. For example, the previously created ``customers`` table with JSON column ``json_data`` can be queried like: .. code-block:: sql select c.json_data.location FROM customers c With the JSON ``'{"name":"Rod","dept":"Sales","location":"Germany"}'`` stored in the table, the queried value would be ``Germany``. The JSON_EXISTS functions tests for the existence of a particular value within some JSON data. To look for JSON entries that have a ``location`` field: .. code-block:: python for blob, in cursor.execute(""" select json_data from customers where json_exists(json_data, '$.location')"""): data = json.loads(blob.read()) print(data) This query might display:: {'name': 'Rod', 'dept': 'Sales', 'location': 'Germany'} The SQL/JSON functions ``JSON_VALUE`` and ``JSON_QUERY`` can also be used. Note that the default error-handling behavior for these functions is ``NULL ON ERROR``, which means that no value is returned if an error occurs. To ensure that an error is raised, use ``ERROR ON ERROR``. For more information, see `SQL/JSON Path Expressions `__ in the Oracle JSON Developer's Guide. Accessing Relational Data as JSON ================================= In Oracle Database 12.2, or later, the `JSON_OBJECT `__ function is a great way to convert relational table data to JSON: .. code-block:: python cursor.execute(""" select json_object('deptId' is d.department_id, 'name' is d.department_name) department from departments d where department_id < :did order by d.department_id""", [50]); for row in cursor: print(row) This produces:: ('{"deptId":10,"name":"Administration"}',) ('{"deptId":20,"name":"Marketing"}',) ('{"deptId":30,"name":"Purchasing"}',) ('{"deptId":40,"name":"Human Resources"}',) python-cx_Oracle-8.3.0/doc/src/user_guide/lob_data.rst000066400000000000000000000156241414105416400227360ustar00rootroot00000000000000.. _lobdata: ************************ Using CLOB and BLOB Data ************************ Oracle Database uses :ref:`lobobj` to store large data such as text, images, videos and other multimedia formats. The maximum size of a LOB is limited to the size of the tablespace storing it. There are four types of LOB (large object): * BLOB - Binary Large Object, used for storing binary data. cx_Oracle uses the type :attr:`cx_Oracle.DB_TYPE_BLOB`. * CLOB - Character Large Object, used for string strings in the database character set format. cx_Oracle uses the type :attr:`cx_Oracle.DB_TYPE_CLOB`. * NCLOB - National Character Large Object, used for string strings in the national character set format. cx_Oracle uses the type :attr:`cx_Oracle.DB_TYPE_NCLOB`. * BFILE - External Binary File, used for referencing a file stored on the host operating system outside of the database. cx_Oracle uses the type :attr:`cx_Oracle.DB_TYPE_BFILE`. LOBs can be streamed to, and from, Oracle Database. LOBs up to 1 GB in length can be also be handled directly as strings or bytes in cx_Oracle. This makes LOBs easy to work with, and has significant performance benefits over streaming. However it requires the entire LOB data to be present in Python memory, which may not be possible. See `GitHub `__ for LOB examples. Simple Insertion of LOBs ------------------------ Consider a table with CLOB and BLOB columns: .. code-block:: sql CREATE TABLE lob_tbl ( id NUMBER, c CLOB, b BLOB ); With cx_Oracle, LOB data can be inserted in the table by binding strings or bytes as needed: .. code-block:: python with open('example.txt', 'r') as f: text_data = f.read() with open('image.png', 'rb') as f: img_data = f.read() cursor.execute(""" insert into lob_tbl (id, c, b) values (:lobid, :clobdata, :blobdata)""", lobid=10, clobdata=text_data, blobdata=img_data) Note that with this approach, LOB data is limited to 1 GB in size. .. _directlobs: Fetching LOBs as Strings and Bytes ---------------------------------- CLOBs and BLOBs smaller than 1 GB can queried from the database directly as strings and bytes. This can be much faster than streaming. A :attr:`Connection.outputtypehandler` or :attr:`Cursor.outputtypehandler` needs to be used as shown in this example: .. code-block:: python def output_type_handler(cursor, name, default_type, size, precision, scale): if default_type == cx_Oracle.DB_TYPE_CLOB: return cursor.var(cx_Oracle.DB_TYPE_LONG, arraysize=cursor.arraysize) if default_type == cx_Oracle.DB_TYPE_BLOB: return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize) id_val = 1 text_data = "The quick brown fox jumps over the lazy dog" binary_data = b"Some binary data" cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)", [id_val, text_data, binary_data]) connection.outputtypehandler = output_type_handler cursor.execute("select c, b from lob_tbl where id = :1", [id_val]) clob_data, blob_data = cursor.fetchone() print("CLOB length:", len(clob_data)) print("CLOB data:", clob_data) print("BLOB length:", len(blob_data)) print("BLOB data:", blob_data) This displays:: CLOB length: 43 CLOB data: The quick brown fox jumps over the lazy dog BLOB length: 16 BLOB data: b'Some binary data' Streaming LOBs (Read) --------------------- Without the output type handler, the CLOB and BLOB values are fetched as :ref:`LOB objects`. The size of the LOB object can be obtained by calling :meth:`LOB.size()` and the data can be read by calling :meth:`LOB.read()`: .. code-block:: python id_val = 1 text_data = "The quick brown fox jumps over the lazy dog" binary_data = b"Some binary data" cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)", [id_val, text_data, binary_data]) cursor.execute("select b, c from lob_tbl where id = :1", [id_val]) b, c = cursor.fetchone() print("CLOB length:", c.size()) print("CLOB data:", c.read()) print("BLOB length:", b.size()) print("BLOB data:", b.read()) This approach produces the same results as the previous example but it will perform more slowly because it requires more :ref:`round-trips ` to Oracle Database and has higher overhead. It is needed, however, if the LOB data cannot be fetched as one block of data from the server. To stream the BLOB column, the :meth:`LOB.read()` method can be called repeatedly until all of the data has been read, as shown below: .. code-block:: python cursor.execute("select b from lob_tbl where id = :1", [10]) blob, = cursor.fetchone() offset = 1 num_bytes_in_chunk = 65536 with open("image.png", "wb") as f: while True: data = blob.read(offset, num_bytes_in_chunk) if data: f.write(data) if len(data) < num_bytes_in_chunk: break offset += len(data) Streaming LOBs (Write) ---------------------- If a row containing a LOB is being inserted or updated, and the quantity of data that is to be inserted or updated cannot fit in a single block of data, the data can be streamed using the method :meth:`LOB.write()` instead as shown in the following code: .. code-block:: python id_val = 9 lob_var = cursor.var(cx_Oracle.DB_TYPE_BLOB) cursor.execute(""" insert into lob_tbl (id, b) values (:1, empty_blob()) returning b into :2""", [id_val, lob_var]) blob, = lobVar.getvalue() offset = 1 num_bytes_in_chunk = 65536 with open("image.png", "rb") as f: while True: data = f.read(num_bytes_in_chunk) if data: blob.write(data, offset) if len(data) < num_bytes_in_chunk: break offset += len(data) connection.commit() Temporary LOBs -------------- All of the examples shown thus far have made use of permanent LOBs. These are LOBs that are stored in the database. Oracle also supports temporary LOBs that are not stored in the database but can be used to pass large quantities of data. These LOBs use space in the temporary tablespace until all variables referencing them go out of scope or the connection in which they are created is explicitly closed. When calling PL/SQL procedures with data that exceeds 32,767 bytes in length, cx_Oracle automatically creates a temporary LOB internally and passes that value through to the procedure. If the data that is to be passed to the procedure exceeds that which can fit in a single block of data, however, you can use the method :meth:`Connection.createlob()` to create a temporary LOB. This LOB can then be read and written just like in the examples shown above for persistent LOBs. python-cx_Oracle-8.3.0/doc/src/user_guide/plsql_execution.rst000066400000000000000000000260641414105416400244070ustar00rootroot00000000000000.. _plsqlexecution: **************** PL/SQL Execution **************** PL/SQL stored procedures, functions and anonymous blocks can be called from cx_Oracle. .. _plsqlproc: PL/SQL Stored Procedures ------------------------ The :meth:`Cursor.callproc()` method is used to call PL/SQL procedures. If a procedure with the following definition exists: .. code-block:: sql create or replace procedure myproc ( a_Value1 number, a_Value2 out number ) as begin a_Value2 := a_Value1 * 2; end; then the following Python code can be used to call it: .. code-block:: python out_val = cursor.var(int) cursor.callproc('myproc', [123, out_val]) print(out_val.getvalue()) # will print 246 Calling :meth:`Cursor.callproc()` actually generates an anonymous PL/SQL block as shown below, which is then executed: .. code-block:: python cursor.execute("begin myproc(:1,:2); end;", [123, out_val]) See :ref:`bind` for information on binding. .. _plsqlfunc: PL/SQL Stored Functions ----------------------- The :meth:`Cursor.callfunc()` method is used to call PL/SQL functions. The ``returnType`` parameter for :meth:`~Cursor.callfunc()` is expected to be a Python type, one of the :ref:`cx_Oracle types ` or an :ref:`Object Type `. If a function with the following definition exists: .. code-block:: sql create or replace function myfunc ( a_StrVal varchar2, a_NumVal number ) return number as begin return length(a_StrVal) + a_NumVal * 2; end; then the following Python code can be used to call it: .. code-block:: python return_val = cursor.callfunc("myfunc", int, ["a string", 15]) print(return_val) # will print 38 A more complex example that returns a spatial (SDO) object can be seen below. First, the SQL statements necessary to set up the example: .. code-block:: sql create table MyPoints ( id number(9) not null, point sdo_point_type not null ); insert into MyPoints values (1, sdo_point_type(125, 375, 0)); create or replace function spatial_queryfn ( a_Id number ) return sdo_point_type is t_Result sdo_point_type; begin select point into t_Result from MyPoints where Id = a_Id; return t_Result; end; / The Python code that will call this procedure looks as follows: .. code-block:: python obj_type = connection.gettype("SDO_POINT_TYPE") cursor = connection.cursor() return_val = cursor.callfunc("spatial_queryfn", obj_type, [1]) print(f"({return_val.X}, {return_val.Y}, {return_val.Z})") # will print (125, 375, 0) See :ref:`bind` for information on binding. Anonymous PL/SQL Blocks ----------------------- An anonymous PL/SQL block can be called as shown: .. code-block:: python var = cursor.var(int) cursor.execute(""" begin :out_val := length(:in_val); end;""", in_val="A sample string", out_val=var) print(var.getvalue()) # will print 15 See :ref:`bind` for information on binding. Creating Stored Procedures and Packages --------------------------------------- To create PL/SQL stored procedures and packages, use :meth:`Cursor.execute()` with a SQL CREATE command. Creation warning messages can be found from database views like USER_ERRORS. For example, creating a procedure with an error could be like: .. code-block:: python with connection.cursor() as cursor: cursor.execute(""" create or replace procedure badproc (a in number) as begin WRONG WRONG WRONG end;""") cursor.execute(""" select line, position, text from user_errors where name = 'BADPROC' and type = 'PROCEDURE' order by name, type, line, position""") errors = cursor.fetchall() if errors: for info in errors: print("Error at line {} position {}:\n{}".format(*info)) else: print("Created successfully") The output would be:: PLS-00103: Encountered the symbol "WRONG" when expecting one of the following: := . ( @ % ; Using DBMS_OUTPUT ----------------- The standard way to print output from PL/SQL is with the package `DBMS_OUTPUT `__. Note, PL/SQL code that uses ``DBMS_OUTPUT`` runs to completion before any output is available to the user. Also, other database connections cannot access the buffer. To use DBMS_OUTPUT: * Call the PL/SQL procedure ``DBMS_OUTPUT.ENABLE()`` to enable output to be buffered for the connection. * Execute some PL/SQL that calls ``DBMS_OUTPUT.PUT_LINE()`` to put text in the buffer. * Call ``DBMS_OUTPUT.GET_LINE()`` or ``DBMS_OUTPUT.GET_LINES()`` repeatedly to fetch the text from the buffer until there is no more output. For example: .. code-block:: python # enable DBMS_OUTPUT cursor.callproc("dbms_output.enable") # execute some PL/SQL that calls DBMS_OUTPUT.PUT_LINE cursor.execute(""" begin dbms_output.put_line('This is the cx_Oracle manual'); dbms_output.put_line('Demonstrating how to use DBMS_OUTPUT'); end;""") # tune this size for your application chunk_size = 100 # create variables to hold the output lines_var = cursor.arrayvar(str, chunk_size) num_lines_var = cursor.var(int) num_lines_var.setvalue(0, chunk_size) # fetch the text that was added by PL/SQL while True: cursor.callproc("dbms_output.get_lines", (lines_var, num_lines_var)) num_lines = num_lines_var.getvalue() lines = lines_var.getvalue()[:num_lines] for line in lines: print(line or "") if num_lines < chunk_size: break This will produce the following output:: This is the cx_Oracle manual Demonstrating use of DBMS_OUTPUT An alternative is to call ``DBMS_OUTPUT.GET_LINE()`` once per output line, which may be much slower: .. code-block:: python text_var = cursor.var(str) status_var = cursor.var(int) while True: cursor.callproc("dbms_output.get_line", (text_var, status_var)) if status_var.getvalue() != 0: break print(text_var.getvalue()) Implicit results ---------------- Implicit results permit a Python program to consume cursors returned by a PL/SQL block without the requirement to use OUT REF CURSOR parameters. The method :meth:`Cursor.getimplicitresults()` can be used for this purpose. It requires both the Oracle Client and Oracle Database to be 12.1 or higher. An example using implicit results is as shown: .. code-block:: python cursor.execute(""" declare cust_cur sys_refcursor; sales_cur sys_refcursor; begin open cust_cur for SELECT * FROM cust_table; dbms_sql.return_result(cust_cur); open sales_cur for SELECT * FROM sales_table; dbms_sql.return_result(sales_cur); end;""") for implicit_cursor in cursor.getimplicitresults(): for row in implicit_cursor: print(row) Data from both the result sets are returned:: (1, 'Tom') (2, 'Julia') (1000, 1, 'BOOKS') (2000, 2, 'FURNITURE') .. _ebr: Edition-Based Redefinition (EBR) -------------------------------- Oracle Database's `Edition-Based Redefinition `__ feature enables upgrading of the database component of an application while it is in use, thereby minimizing or eliminating down time. This feature allows multiple versions of views, synonyms, PL/SQL objects and SQL Translation profiles to be used concurrently. Different versions of the database objects are associated with an "edition". The simplest way to set an edition is to pass the ``edition`` parameter to :meth:`cx_Oracle.connect()` or :meth:`cx_Oracle.SessionPool()`: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", edition="newsales", encoding="UTF-8") The edition could also be set by setting the environment variable ``ORA_EDITION`` or by executing the SQL statement: .. code-block:: sql alter session set edition = ; Regardless of which method is used to set the edition, the value that is in use can be seen by examining the attribute :attr:`Connection.edition`. If no value has been set, the value will be None. This corresponds to the database default edition ``ORA$BASE``. Consider an example where one version of a PL/SQL function ``Discount`` is defined in the database default edition ``ORA$BASE`` and the other version of the same function is defined in a user created edition ``DEMO``. .. code-block:: sql connect / -- create function using the database default edition CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS BEGIN return price * 0.9; END; / A new edition named 'DEMO' is created and the user given permission to use editions. The use of ``FORCE`` is required if the user already contains one or more objects whose type is editionable and that also have non-editioned dependent objects. .. code-block:: sql connect system/ CREATE EDITION demo; ALTER USER ENABLE EDITIONS FORCE; GRANT USE ON EDITION demo to ; The ``Discount`` function for the demo edition is as follows: .. code-block:: sql connect / alter session set edition = demo; -- Function for the demo edition CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS BEGIN return price * 0.5; END; / The Python application can then call the required version of the PL/SQL function as shown: .. code-block:: python connection = cx_Oracle.connect(user=user, password=password, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8") print("Edition is:", repr(connection.edition)) cursor = connection.cursor() discounted_price = cursor.callfunc("Discount", int, [100]) print("Price after discount is:", discounted_price) # Use the edition parameter for the connection connection = cx_Oracle.connect(user=user, password=password, dsn="dbhost.example.com/orclpdb1", edition="demo", encoding="UTF-8") print("Edition is:", repr(connection.edition)) cursor = connection.cursor() discounted_price = cursor.callfunc("Discount", int, [100]) print("Price after discount is:", discounted_price) The output of the function call for the default and demo edition is as shown:: Edition is: None Price after discount is: 90 Edition is: 'DEMO' Price after discount is: 50 python-cx_Oracle-8.3.0/doc/src/user_guide/soda.rst000066400000000000000000000221461414105416400221140ustar00rootroot00000000000000.. _sodausermanual: ************************************ Simple Oracle Document Access (SODA) ************************************ Overview ======== Oracle Database Simple Oracle Document Access (SODA) allows documents to be inserted, queried, and retrieved from Oracle Database using a set of NoSQL-style cx_Oracle methods. Documents are generally JSON data but they can be any data at all (including video, images, sounds, or other binary content). Documents can be fetched from the database by key lookup or by using query-by-example (QBE) pattern-matching. SODA uses a SQL schema to store documents but you do not need to know SQL or how the documents are stored. However, access via SQL does allow use of advanced Oracle Database functionality such as analytics for reporting. Oracle SODA implementations are also available in `Node.js `__, `Java `__, `PL/SQL `__, `Oracle Call Interface `__ and via `REST `__. For general information on SODA, see the `SODA home page `__ and the Oracle Database `Introduction to Simple Oracle Document Access (SODA) `__ manual. For specific requirements see the cx_Oracle :ref:`SODA requirements `. cx_Oracle uses the following objects for SODA: * :ref:`SODA Database Object `: The top level object for cx_Oracle SODA operations. This is acquired from an Oracle Database connection. A 'SODA database' is an abstraction, allowing access to SODA collections in that 'SODA database', which then allow access to documents in those collections. A SODA database is analogous to an Oracle Database user or schema, a collection is analogous to a table, and a document is analogous to a table row with one column for a unique document key, a column for the document content, and other columns for various document attributes. * :ref:`SODA Collection Object `: Represents a collection of SODA documents. By default, collections allow JSON documents to be stored. This is recommended for most SODA users. However optional metadata can set various details about a collection, such as its database storage, whether it should track version and time stamp document components, how such components are generated, and what document types are supported. By default, the name of the Oracle Database table storing a collection is the same as the collection name. Note: do not use SQL to drop the database table, since SODA metadata will not be correctly removed. Use the :meth:`SodaCollection.drop()` method instead. * :ref:`SODA Document Object `: Represents a document. Typically the document content will be JSON. The document has properties including the content, a key, timestamps, and the media type. By default, document keys are automatically generated. See :ref:`SODA Document objects ` for the forms of SodaDoc. * :ref:`SODA Document Cursor `: A cursor object representing the result of the :meth:`SodaOperation.getCursor()` method from a :meth:`SodaCollection.find()` operation. It can be iterated over to access each SodaDoc. * :ref:`SODA Operation Object `: An internal object used with :meth:`SodaCollection.find()` to perform read and write operations on documents. Chained methods set properties on a SodaOperation object which is then used by a terminal method to find, count, replace, or remove documents. This is an internal object that should not be directly accessed. SODA Examples ============= Creating and adding documents to a collection can be done as follows: .. code-block:: python soda = connection.getSodaDatabase() # create a new SODA collection; this will open an existing collection, if # the name is already in use collection = soda.createCollection("mycollection") # insert a document into the collection; for the common case of a JSON # document, the content can be a simple Python dictionary which will # internally be converted to a JSON document content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}} returned_doc = collection.insertOneAndGet(content) key = returned_doc.key print('The key of the new SODA document is: ', key) By default, a system generated key is created when documents are inserted. With a known key, you can retrieve a document: .. code-block:: python # this will return a dictionary (as was inserted in the previous code) content = collection.find().key(key).getOne().getContent() print(content) You can also search for documents using query-by-example syntax: .. code-block:: python # Find all documents with names like 'Ma%' print("Names matching 'Ma%'") qbe = {'name': {'$like': 'Ma%'}} for doc in collection.find().filter(qbe).getDocuments(): content = doc.getContent() print(content["name"]) See the `samples directory `__ for runnable SODA examples. .. _sodametadatacache: Using the SODA Metadata Cache ============================= SODA metadata can be cached to improve the performance of :meth:`SodaDatabase.createCollection()` and :meth:`SodaDatabase.openCollection()` by reducing :ref:`round-trips ` to the database. Caching is available with Oracle Client 21.3 (or later). The feature is also available in Oracle Client 19 from 19.11 onwards. The metadata cache can be turned on when creating a connection pool with :meth:`cx_Oracle.SessionPool()`. Each pool has its own cache: .. code-block:: python # Create the session pool pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", soda_metadata_cache=True) The cache is not available for standalone connections. Applications using these should retain and reuse the :ref:`collection ` returned from ``createCollection()`` or ``openCollection()`` wherever possible, instead of making repeated calls to those methods. The cache is not used by ``createCollection()`` when explicitly passing metadata. In this case, instead of using only ``createCollection()`` and relying on its behavior of opening an existing collection like: .. code-block:: python mymetadata = { . . . } collection = soda.createCollection("mycollection", mymetadata) # open existing or create new collection collection.insertOne(mycontent) you will find it more efficient to use logic similar to: .. code-block:: python collection = soda.openCollection("mycollection") if collection is None: mymetadata = { . . . } collection = soda.createCollection("mycollection", mymetadata) collection.insertOne(mycontent) If collection metadata changes are made externally, the cache can become invalid. If this happens, the cache can be cleared by calling :meth:`SessionPool.reconfigure()` with ``soda_metadata_cache`` set to `False`, or by setting the attribute :attr:`SessionPool.soda_metadata_cache` to `False`. Use a second call to ``reconfigure()`` or set ``soda_metadata_cache`` to re-enable the cache. Committing SODA Work ==================== The general recommendation for SODA applications is to turn on :attr:`~Connection.autocommit` globally: .. code-block:: python connection.autocommit = True If your SODA document write operations are mostly independent of each other, this removes the overhead of application transaction management and the need for explicit :meth:`Connection.commit()` calls. When deciding how to commit transactions, beware of transactional consistency and performance requirements. If you are using individual SODA calls to insert or update a large number of documents with individual calls, you should turn :attr:`~Connection.autocommit` off and issue a single, explicit :meth:`~Connection.commit()` after all documents have been processed. Also consider using :meth:`SodaCollection.insertMany()` or :meth:`SodaCollection.insertManyAndGet()` which have performance benefits. If you are not autocommitting, and one of the SODA operations in your transaction fails, then previous uncommitted operations will not be rolled back. Your application should explicitly roll back the transaction with :meth:`Connection.rollback()` to prevent any later commits from committing a partial transaction. Note: - SODA DDL operations do not commit an open transaction the way that SQL always does for DDL statements. - When :attr:`~Connection.autocommit` is ``True``, most SODA methods will issue a commit before successful return. - SODA provides optimistic locking, see :meth:`SodaOperation.version()`. - When mixing SODA and relational access, any commit or rollback on the connection will affect all work. python-cx_Oracle-8.3.0/doc/src/user_guide/sql_execution.rst000066400000000000000000000657661414105416400240670ustar00rootroot00000000000000.. _sqlexecution: ************* SQL Execution ************* Executing SQL statements is the primary way in which a Python application communicates with Oracle Database. Statements are executed using the methods :meth:`Cursor.execute()` or :meth:`Cursor.executemany()`. Statements include queries, Data Manipulation Language (DML), and Data Definition Language (DDL). A few other `specialty statements `__ can also be executed. PL/SQL statements are discussed in :ref:`plsqlexecution`. Other chapters contain information on specific data types and features. See :ref:`batchstmnt`, :ref:`lobdata`, :ref:`jsondatatype`, and :ref:`xmldatatype`. cx_Oracle can be used to execute individual statements, one at a time. It does not read SQL*Plus ".sql" files. To read SQL files, use a technique like the one in ``run_sql_script()`` in `samples/sample_env.py `__ SQL statements should not contain a trailing semicolon (";") or forward slash ("/"). This will fail: .. code-block:: python cur.execute("select * from MyTable;") This is correct: .. code-block:: python cur.execute("select * from MyTable") SQL Queries =========== Queries (statements beginning with SELECT or WITH) can only be executed using the method :meth:`Cursor.execute()`. Rows can then be iterated over, or can be fetched using one of the methods :meth:`Cursor.fetchone()`, :meth:`Cursor.fetchmany()` or :meth:`Cursor.fetchall()`. There is a :ref:`default type mapping ` to Python types that can be optionally :ref:`overridden `. .. IMPORTANT:: Interpolating or concatenating user data with SQL statements, for example ``cur.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")``, is a security risk and impacts performance. Use :ref:`bind variables ` instead. For example, ``cur.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)``. .. _fetching: Fetch Methods ------------- After :meth:`Cursor.execute()`, the cursor is returned as a convenience. This allows code to iterate over rows like: .. code-block:: python cur = connection.cursor() for row in cur.execute("select * from MyTable"): print(row) Rows can also be fetched one at a time using the method :meth:`Cursor.fetchone()`: .. code-block:: python cur = connection.cursor() cur.execute("select * from MyTable") while True: row = cur.fetchone() if row is None: break print(row) If rows need to be processed in batches, the method :meth:`Cursor.fetchmany()` can be used. The size of the batch is controlled by the ``numRows`` parameter, which defaults to the value of :attr:`Cursor.arraysize`. .. code-block:: python cur = connection.cursor() cur.execute("select * from MyTable") num_rows = 10 while True: rows = cur.fetchmany(num_rows) if not rows: break for row in rows: print(row) If all of the rows need to be fetched, and can be contained in memory, the method :meth:`Cursor.fetchall()` can be used. .. code-block:: python cur = connection.cursor() cur.execute("select * from MyTable") rows = cur.fetchall() for row in rows: print(row) The fetch methods return data as tuples. To return results as dictionaries, see :ref:`rowfactories`. Closing Cursors --------------- A cursor may be used to execute multiple statements. Once it is no longer needed, it should be closed by calling :meth:`~Cursor.close()` in order to reclaim resources in the database. It will be closed automatically when the variable referencing it goes out of scope (and no further references are retained). One other way to control the lifetime of a cursor is to use a "with" block, which ensures that a cursor is closed once the block is completed. For example: .. code-block:: python with connection.cursor() as cursor: for row in cursor.execute("select * from MyTable"): print(row) This code ensures that, once the block is completed, the cursor is closed and resources have been reclaimed by the database. In addition, any attempt to use the variable ``cursor`` outside of the block will simply fail. .. _querymetadata: Query Column Metadata --------------------- After executing a query, the column metadata such as column names and data types can be obtained using :attr:`Cursor.description`: .. code-block:: python cur = connection.cursor() cur.execute("select * from MyTable") for column in cur.description: print(column) This could result in metadata like:: ('ID', , 39, None, 38, 0, 0) ('NAME', , 20, 20, None, None, 1) .. _defaultfetchtypes: Fetch Data Types ---------------- The following table provides a list of all of the data types that cx_Oracle knows how to fetch. The middle column gives the type that is returned in the :ref:`query metadata `. The last column gives the type of Python object that is returned by default. Python types can be changed with :ref:`Output Type Handlers `. .. list-table:: :header-rows: 1 :widths: 1 1 1 :align: left * - Oracle Database Type - cx_Oracle Database Type - Default Python type * - BFILE - :attr:`cx_Oracle.DB_TYPE_BFILE` - :ref:`cx_Oracle.LOB ` * - BINARY_DOUBLE - :attr:`cx_Oracle.DB_TYPE_BINARY_DOUBLE` - float * - BINARY_FLOAT - :attr:`cx_Oracle.DB_TYPE_BINARY_FLOAT` - float * - BLOB - :attr:`cx_Oracle.DB_TYPE_BLOB` - :ref:`cx_Oracle.LOB ` * - CHAR - :attr:`cx_Oracle.DB_TYPE_CHAR` - str * - CLOB - :attr:`cx_Oracle.DB_TYPE_CLOB` - :ref:`cx_Oracle.LOB ` * - CURSOR - :attr:`cx_Oracle.DB_TYPE_CURSOR` - :ref:`cx_Oracle.Cursor ` * - DATE - :attr:`cx_Oracle.DB_TYPE_DATE` - datetime.datetime * - INTERVAL DAY TO SECOND - :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS` - datetime.timedelta * - JSON - :attr:`cx_Oracle.DB_TYPE_JSON` - dict, list or a scalar value [4]_ * - LONG - :attr:`cx_Oracle.DB_TYPE_LONG` - str * - LONG RAW - :attr:`cx_Oracle.DB_TYPE_LONG_RAW` - bytes * - NCHAR - :attr:`cx_Oracle.DB_TYPE_NCHAR` - str * - NCLOB - :attr:`cx_Oracle.DB_TYPE_NCLOB` - :ref:`cx_Oracle.LOB ` * - NUMBER - :attr:`cx_Oracle.DB_TYPE_NUMBER` - float or int [1]_ * - NVARCHAR2 - :attr:`cx_Oracle.DB_TYPE_NVARCHAR` - str * - OBJECT [3]_ - :attr:`cx_Oracle.DB_TYPE_OBJECT` - :ref:`cx_Oracle.Object ` * - RAW - :attr:`cx_Oracle.DB_TYPE_RAW` - bytes * - ROWID - :attr:`cx_Oracle.DB_TYPE_ROWID` - str * - TIMESTAMP - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP` - datetime.datetime * - TIMESTAMP WITH LOCAL TIME ZONE - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` - datetime.datetime [2]_ * - TIMESTAMP WITH TIME ZONE - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` - datetime.datetime [2]_ * - UROWID - :attr:`cx_Oracle.DB_TYPE_ROWID` - str * - VARCHAR2 - :attr:`cx_Oracle.DB_TYPE_VARCHAR` - str .. [1] If the precision and scale obtained from query column metadata indicate that the value can be expressed as an integer, the value will be returned as an int. If the column is unconstrained (no precision and scale specified), the value will be returned as a float or an int depending on whether the value itself is an integer. In all other cases the value is returned as a float. .. [2] The timestamps returned are naive timestamps without any time zone information present. .. [3] These include all user-defined types such as VARRAY, NESTED TABLE, etc. .. [4] If the JSON is an object, then a dict is returned. If it is an array, then a list is returned. If it is a scalar value, then that particular scalar value is returned. .. _outputtypehandlers: Changing Fetched Data Types with Output Type Handlers ----------------------------------------------------- Sometimes the default conversion from an Oracle Database type to a Python type must be changed in order to prevent data loss or to fit the purposes of the Python application. In such cases, an output type handler can be specified for queries. Output type handlers do not affect values returned from :meth:`Cursor.callfunc()` or :meth:`Cursor.callproc()`. Output type handlers can be specified on the :attr:`connection ` or on the :attr:`cursor `. If specified on the cursor, fetch type handling is only changed on that particular cursor. If specified on the connection, all cursors created by that connection will have their fetch type handling changed. The output type handler is expected to be a function with the following signature:: handler(cursor, name, defaultType, size, precision, scale) The parameters are the same information as the query column metadata found in :attr:`Cursor.description`. The function is called once for each column that is going to be fetched. The function is expected to return a :ref:`variable object ` (generally by a call to :func:`Cursor.var()`) or the value ``None``. The value ``None`` indicates that the default type should be used. Examples of output handlers are shown in :ref:`numberprecision`, :ref:`directlobs` and :ref:`fetching-raw-data`. Also see samples such as `samples/type_handlers.py `__ .. _numberprecision: Fetched Number Precision ------------------------ One reason for using an output type handler is to ensure that numeric precision is not lost when fetching certain numbers. Oracle Database uses decimal numbers and these cannot be converted seamlessly to binary number representations like Python floats. In addition, the range of Oracle numbers exceeds that of floating point numbers. Python has decimal objects which do not have these limitations and cx_Oracle knows how to perform the conversion between Oracle numbers and Python decimal values if directed to do so. The following code sample demonstrates the issue: .. code-block:: python cur = connection.cursor() cur.execute("create table test_float (X number(5, 3))") cur.execute("insert into test_float values (7.1)") connection.commit() cur.execute("select * from test_float") val, = cur.fetchone() print(val, "* 3 =", val * 3) This displays ``7.1 * 3 = 21.299999999999997`` Using Python decimal objects, however, there is no loss of precision: .. code-block:: python import decimal def number_to_decimal(cursor, name, default_type, size, precision, scale): if default_type == cx_Oracle.DB_TYPE_NUMBER: return cursor.var(decimal.Decimal, arraysize=cursor.arraysize) cur = connection.cursor() cur.outputtypehandler = number_to_decimal cur.execute("select * from test_float") val, = cur.fetchone() print(val, "* 3 =", val * 3) This displays ``7.1 * 3 = 21.3`` The Python ``decimal.Decimal`` converter gets called with the string representation of the Oracle number. The output from ``decimal.Decimal`` is returned in the output tuple. See `samples/return_numbers_as_decimals.py `__ .. _outconverters: Changing Query Results with Outconverters ----------------------------------------- cx_Oracle "outconverters" can be used with :ref:`output type handlers ` to change returned data. For example, to make queries return empty strings instead of NULLs: .. code-block:: python def out_converter(value): if value is None: return '' return value def output_type_handler(cursor, name, default_type, size, precision, scale): if default_type in (cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.DB_TYPE_CHAR): return cursor.var(str, size, arraysize=cur.arraysize, outconverter=out_converter) connection.outputtypehandler = output_type_handler .. _rowfactories: Changing Query Results with Rowfactories ---------------------------------------- cx_Oracle "rowfactories" are methods called for each row that is retrieved from the database. The :meth:`Cursor.rowfactory` method is called with the tuple that would normally be returned from the database. The method can convert the tuple to a different value and return it to the application in place of the tuple. For example, to fetch each row of a query as a dictionary: .. code-block:: python cursor.execute("select * from locations where location_id = 1000") columns = [col[0] for col in cursor.description] cursor.rowfactory = lambda *args: dict(zip(columns, args)) data = cursor.fetchone() print(data) The output is:: {'LOCATION_ID': 1000, 'STREET_ADDRESS': '1297 Via Cola di Rie', 'POSTAL_CODE': '00989', 'CITY': 'Roma', 'STATE_PROVINCE': None, 'COUNTRY_ID': 'IT'} If you join tables where the same column name occurs in both tables with different meanings or values, then use a column alias in the query. Otherwise only one of the similarly named columns will be included in the dictionary: .. code-block:: sql select cat_name, cats.color as cat_color, dog_name, dogs.color from cats, dogs .. _scrollablecursors: Scrollable Cursors ------------------ Scrollable cursors enable applications to move backwards, forwards, to skip rows, and to move to a particular row in a query result set. The result set is cached on the database server until the cursor is closed. In contrast, regular cursors are restricted to moving forward. A scrollable cursor is created by setting the parameter ``scrollable=True`` when creating the cursor. The method :meth:`Cursor.scroll()` is used to move to different locations in the result set. Examples are: .. code-block:: python cursor = connection.cursor(scrollable=True) cursor.execute("select * from ChildTable order by ChildId") cursor.scroll(mode="last") print("LAST ROW:", cursor.fetchone()) cursor.scroll(mode="first") print("FIRST ROW:", cursor.fetchone()) cursor.scroll(8, mode="absolute") print("ROW 8:", cursor.fetchone()) cursor.scroll(6) print("SKIP 6 ROWS:", cursor.fetchone()) cursor.scroll(-4) print("SKIP BACK 4 ROWS:", cursor.fetchone()) .. _fetchobjects: Fetching Oracle Database Objects and Collections ------------------------------------------------ Oracle Database named object types and user-defined types can be fetched directly in queries. Each item is represented as a :ref:`Python object ` corresponding to the Oracle Database object. This Python object can be traversed to access its elements. Attributes including :attr:`ObjectType.name` and :attr:`ObjectType.iscollection`, and methods including :meth:`Object.aslist` and :meth:`Object.asdict` are available. For example, if a table ``mygeometrytab`` contains a column ``geometry`` of Oracle's predefined Spatial object type `SDO_GEOMETRY `__, then it can be queried and printed: .. code-block:: python cur.execute("select geometry from mygeometrytab") for obj, in cur: dumpobject(obj) Where ``dumpobject()`` is defined as: .. code-block:: python def dumpobject(obj, prefix = ""): if obj.type.iscollection: print(prefix, "[") for value in obj.aslist(): if isinstance(value, cx_Oracle.Object): dumpobject(value, prefix + " ") else: print(prefix + " ", repr(value)) print(prefix, "]") else: print(prefix, "{") for attr in obj.type.attributes: value = getattr(obj, attr.name) if isinstance(value, cx_Oracle.Object): print(prefix + " " + attr.name + ":") dumpobject(value, prefix + " ") else: print(prefix + " " + attr.name + ":", repr(value)) print(prefix, "}") This might produce output like:: { SDO_GTYPE: 2003 SDO_SRID: None SDO_POINT: { X: 1 Y: 2 Z: 3 } SDO_ELEM_INFO: [ 1 1003 3 ] SDO_ORDINATES: [ 1 1 5 7 ] } Other information on using Oracle objects is in :ref:`Using Bind Variables `. Performance-sensitive applications should consider using scalar types instead of objects. If you do use objects, avoid calling :meth:`Connection.gettype()` unnecessarily, and avoid objects with large numbers of attributes. .. _rowlimit: Limiting Rows ------------- Query data is commonly broken into one or more sets: - To give an upper bound on the number of rows that a query has to process, which can help improve database scalability. - To perform 'Web pagination' that allows moving from one set of rows to a next, or previous, set on demand. - For fetching of all data in consecutive small sets for batch processing. This happens because the number of records is too large for Python to handle at one time. The latter can be handled by calling :meth:`Cursor.fetchmany()` with one execution of the SQL query. 'Web pagination' and limiting the maximum number of rows are discussed in this section. For each 'page' of results, a SQL query is executed to get the appropriate set of rows from a table. Since the query may be executed more than once, make sure to use :ref:`bind variables ` for row numbers and row limits. Oracle Database 12c SQL introduced an ``OFFSET`` / ``FETCH`` clause which is similar to the ``LIMIT`` keyword of MySQL. In Python you can fetch a set of rows using: .. code-block:: python myoffset = 0 // do not skip any rows (start at row 1) mymaxnumrows = 20 // get 20 rows sql = """SELECT last_name FROM employees ORDER BY last_name OFFSET :offset ROWS FETCH NEXT :maxnumrows ROWS ONLY""" cur = connection.cursor() for row in cur.execute(sql, offset=myoffset, maxnumrows=mymaxnumrows): print(row) In applications where the SQL query is not known in advance, this method sometimes involves appending the ``OFFSET`` clause to the 'real' user query. Be very careful to avoid SQL injection security issues. For Oracle Database 11g and earlier there are several alternative ways to limit the number of rows returned. The old, canonical paging query is:: SELECT * FROM (SELECT a.*, ROWNUM AS rnum FROM (YOUR_QUERY_GOES_HERE -- including the order by) a WHERE ROWNUM <= MAX_ROW) WHERE rnum >= MIN_ROW Here, ``MIN_ROW`` is the row number of first row and ``MAX_ROW`` is the row number of the last row to return. For example:: SELECT * FROM (SELECT a.*, ROWNUM AS rnum FROM (SELECT last_name FROM employees ORDER BY last_name) a WHERE ROWNUM <= 20) WHERE rnum >= 1 This always has an 'extra' column, here called RNUM. An alternative and preferred query syntax for Oracle Database 11g uses the analytic ``ROW_NUMBER()`` function. For example to get the 1st to 20th names the query is:: SELECT last_name FROM (SELECT last_name, ROW_NUMBER() OVER (ORDER BY last_name) AS myr FROM employees) WHERE myr BETWEEN 1 and 20 Make sure to use :ref:`bind variables ` for the upper and lower limit values. .. _crc: Client Result Cache ------------------- Python cx_Oracle applications can use Oracle Database's `Client Result Cache `__ The CRC enables client-side caching of SQL query (SELECT statement) results in client memory for immediate use when the same query is re-executed. This is useful for reducing the cost of queries for small, mostly static, lookup tables, such as for postal codes. CRC reduces network :ref:`round-trips `, and also reduces database server CPU usage. The cache is at the application process level. Access and invalidation is managed by the Oracle Client libraries. This removes the need for extra application logic, or external utilities, to implement a cache. CRC can be enabled by setting the `database parameters `__ ``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then restarting the database. For example, to set the parameters: .. code-block:: sql SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE; SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE; CRC can alternatively be configured in an :ref:`oraaccess.xml ` or :ref:`sqlnet.ora ` file on the Python host, see `Client Configuration Parameters `__. Tables can then be created, or altered, so repeated queries use CRC. This allows existing applications to use CRC without needing modification. For example: .. code-block:: sql SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE); SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE); Alternatively, hints can be used in SQL statements. For example: .. code-block:: sql SELECT /*+ result_cache */ postal_code FROM locations .. _fetching-raw-data: Fetching Raw Data ----------------- Sometimes cx_Oracle may have problems converting data stored in the database to Python strings. This can occur if the data stored in the database doesn't match the character set defined by the database. The `encoding_errors` parameter to :meth:`Cursor.var()` permits the data to be returned with some invalid data replaced, but for additional control the parameter `bypass_decode` can be set to `True` and cx_Oracle will bypass the decode step and return `bytes` instead of `str` for data stored in the database as strings. The data can then be examined and corrected as required. This approach should only be used for troubleshooting and correcting invalid data, not for general use! The following sample demonstrates how to use this feature: .. code-block:: python # define output type handler def return_strings_as_bytes(cursor, name, default_type, size, precision, scale): if default_type == cx_Oracle.DB_TYPE_VARCHAR: return cursor.var(str, arraysize=cursor.arraysize, bypass_decode=True) # set output type handler on cursor before fetching data with connection.cursor() as cursor: cursor.outputtypehandler = return_strings_as_bytes cursor.execute("select content, charset from SomeTable") data = cursor.fetchall() This will produce output as:: [(b'Fianc\xc3\xa9', b'UTF-8')] Note that last ``\xc3\xa9`` is é in UTF-8. Since this is valid UTF-8 you can then perform a decode on the data (the part that was bypassed): .. code-block:: python value = data[0][0].decode("UTF-8") This will return the value "Fiancé". If you want to save ``b'Fianc\xc3\xa9'`` into the database directly without using a Python string, you will need to create a variable using :meth:`Cursor.var()` that specifies the type as :data:`~cx_Oracle.DB_TYPE_VARCHAR` (otherwise the value will be treated as :data:`~cx_Oracle.DB_TYPE_RAW`). The following sample demonstrates this: .. code-block:: python with cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1") as conn: with conn.cursor() cursor: var = cursor.var(cx_Oracle.DB_TYPE_VARCHAR) var.setvalue(0, b"Fianc\xc4\x9b") cursor.execute(""" update SomeTable set SomeColumn = :param where id = 1""", param=var) .. warning:: The database will assume that the bytes provided are in the character set expected by the database so only use this for troubleshooting or as directed. .. _codecerror: Querying Corrupt Data --------------------- If queries fail with the error "codec can't decode byte" when you select data, then: * Check your :ref:`character set ` is correct. Review the :ref:`client and database character sets `. Check with :ref:`fetching-raw-data`. Consider using UTF-8, if this is appropriate: .. code-block:: python connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb1", encoding="UTF-8", nencoding="UTF-8") * Check for corrupt data in the database. If data really is corrupt, you can pass options to the internal `decode() `__ used by cx_Oracle to allow it to be selected and prevent the whole query failing. Do this by creating an :ref:`outputtypehandler ` and setting ``encoding_errors``. For example to replace corrupt characters in character columns: .. code-block:: python def output_type_handler(cursor, name, default_type, size, precision, scale): if default_type == cx_Oracle.DB_TYPE_VARCHAR: return cursor.var(default_type, size, arraysize=cursor.arraysize, encoding_errors="replace") cursor.outputtypehandler = output_type_handler cursor.execute("select column1, column2 from SomeTableWithBadData") Other codec behaviors can be chosen for ``encoding_errors``, see `Error Handlers `__. .. _dml: INSERT and UPDATE Statements ============================ SQL Data Manipulation Language statements (DML) such as INSERT and UPDATE can easily be executed with cx_Oracle. For example: .. code-block:: python cur = connection.cursor() cur.execute("insert into MyTable values (:idbv, :nmbv)", [1, "Fredico"]) Do not concatenate or interpolate user data into SQL statements. See :ref:`bind` instead. See :ref:`txnmgmnt` for best practices on committing and rolling back data changes. When handling multiple data values, use :meth:`~Cursor.executemany()` for performance. See :ref:`batchstmnt` Inserting NULLs --------------- Oracle requires a type, even for null values. When you pass the value None, then cx_Oracle assumes the type is STRING. If this is not the desired type, you can explicitly set it. For example, to insert a null :ref:`Oracle Spatial SDO_GEOMETRY ` object: .. code-block:: python type_obj = connection.gettype("SDO_GEOMETRY") cur = connection.cursor() cur.setinputsizes(type_obj) cur.execute("insert into sometable values (:1)", [None]) python-cx_Oracle-8.3.0/doc/src/user_guide/startup.rst000066400000000000000000000043051414105416400226650ustar00rootroot00000000000000.. _startup: ************************************* Starting and Stopping Oracle Database ************************************* This chapter covers how to start up and shutdown Oracle Database using cx_Oracle. =========================== Starting Oracle Database Up =========================== cx_Oracle can start up a database instance. A privileged connection is required. This example shows a script that could be run as the 'oracle' operating system user who administers a local database installation on Linux. It assumes that the environment variable ``ORACLE_SID`` has been set to the SID of the database that should be started: .. code-block:: python # the connection must be in PRELIM_AUTH mode to perform startup connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH) connection.startup() # the following statements must be issued in normal SYSDBA mode connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA, encoding="UTF-8") cursor = connection.cursor() cursor.execute("alter database mount") cursor.execute("alter database open") To start up a remote database, you may need to configure the Oracle Net listener to use `static service registration `_ by adding a ``SID_LIST_LISTENER`` entry to the database `listener.ora` file. ============================= Shutting Oracle Database Down ============================= cx_Oracle has the ability to shutdown the database using a privileged connection. This example also assumes that the environment variable ``ORACLE_SID`` has been set: .. code-block:: python # need to connect as SYSDBA or SYSOPER connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA) # first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used, # there is no need for any of the other steps connection.shutdown(mode=cx_Oracle.DBSHUTDOWN_IMMEDIATE) # now close and dismount the database cursor = connection.cursor() cursor.execute("alter database close normal") cursor.execute("alter database dismount") # perform the final shutdown call connection.shutdown(mode=cx_Oracle.DBSHUTDOWN_FINAL) python-cx_Oracle-8.3.0/doc/src/user_guide/tracing_sql.rst000066400000000000000000000132751414105416400234770ustar00rootroot00000000000000.. _tracingsql: ********************************* Tracing SQL and PL/SQL Statements ********************************* Subclass Connections ==================== Subclassing enables applications to add "hooks" for connection and statement execution. This can be used to alter, or log, connection and execution parameters, and to extend cx_Oracle functionality. The example below demonstrates subclassing a connection to log SQL execution to a file. This example also shows how connection credentials can be embedded in the custom subclass, so application code does not need to supply them. .. code-block:: python class Connection(cx_Oracle.Connection): log_file_name = "log.txt" def __init__(self): connect_string = "hr/hr_password@dbhost.example.com/orclpdb1" self._log("Connect to the database") return super(Connection, self).__init__(connect_string) def _log(self, message): with open(self.log_file_name, "a") as f: print(message, file=f) def execute(self, sql, parameters): self._log(sql) cursor = self.cursor() try: return cursor.execute(sql, parameters) except cx_Oracle.Error as e: error_obj, = e.args self._log(error_obj.message) raise connection = Connection() connection.execute(""" select department_name from departments where department_id = :id""", dict(id=270)) The messages logged in ``log.txt`` are:: Connect to the database select department_name from departments where department_id = :id If an error occurs, perhaps due to a missing table, the log file would contain instead:: Connect to the database select department_name from departments where department_id = :id ORA-00942: table or view does not exist In production applications be careful not to log sensitive information. See `Subclassing.py `__ for an example. .. _endtoendtracing: Oracle Database End-to-End Tracing ================================== Oracle Database End-to-end application tracing simplifies diagnosing application code flow and performance problems in multi-tier or multi-user environments. The connection attributes, :attr:`~Connection.client_identifier`, :attr:`~Connection.clientinfo`, :attr:`~Connection.dbop`, :attr:`~Connection.module` and :attr:`~Connection.action`, set the metadata for end-to-end tracing. You can use data dictionary and ``V$`` views to monitor tracing or use other application tracing utilities. The attributes are sent to the database when the next :ref:`round-trip ` to the database occurs, for example when the next SQL statement is executed. The attribute values will remain set in connections released back to connection pools. When the application re-acquires a connection from the pool it should initialize the values to a desired state before using that connection. The example below shows setting the action, module and client identifier attributes on the connection object: .. code-block:: python # Set the tracing metadata connection.client_identifier = "pythonuser" connection.action = "Query Session tracing parameters" connection.module = "End-to-end Demo" for row in cursor.execute(""" SELECT username, client_identifier, module, action FROM V$SESSION WHERE username = 'SYSTEM'"""): print(row) The output will be:: ('SYSTEM', 'pythonuser', 'End-to-end Demo', 'Query Session tracing parameters') The values can also be manually set as shown by calling `DBMS_APPLICATION_INFO procedures `__ or `DBMS_SESSION.SET_IDENTIFIER `__. These incur round-trips to the database, however, reducing scalability. .. code-block:: sql BEGIN DBMS_SESSION.SET_IDENTIFIER('pythonuser'); DBMS_APPLICATION_INFO.set_module('End-to-End Demo'); DBMS_APPLICATION_INFO.set_action(action_name => 'Query Session tracing parameters'); END; Low Level SQL Tracing in cx_Oracle ================================== cx_Oracle is implemented using the `ODPI-C `__ wrapper on top of the Oracle Client libraries. The ODPI-C tracing capability can be used to log executed cx_Oracle statements to the standard error stream. Before executing Python, set the environment variable ``DPI_DEBUG_LEVEL`` to 16. At a Windows command prompt, this could be done with:: set DPI_DEBUG_LEVEL=16 On Linux, you might use:: export DPI_DEBUG_LEVEL=16 After setting the variable, run the Python Script, for example on Linux:: python end-to-endtracing.py 2> log.txt For an application that does a single query, the log file might contain a tracing line consisting of the prefix 'ODPI', a thread identifier, a timestamp, and the SQL statement executed:: ODPI [26188] 2019-03-26 09:09:03.909: ODPI-C 3.1.1 ODPI [26188] 2019-03-26 09:09:03.909: debugging messages initialized at level 16 ODPI [26188] 2019-03-26 09:09:09.917: SQL SELECT * FROM jobss Traceback (most recent call last): File "end-to-endtracing.py", line 14, in cursor.execute("select * from jobss") cx_Oracle.DatabaseError: ORA-00942: table or view does not exist See `ODPI-C Debugging `__ for documentation on ``DPI_DEBUG_LEVEL``. python-cx_Oracle-8.3.0/doc/src/user_guide/tuning.rst000066400000000000000000000421321414105416400224670ustar00rootroot00000000000000.. _tuning: **************** Tuning cx_Oracle **************** Some general tuning tips are: * Tune your application architecture. A general application goal is to reduce the number of :ref:`round-trips ` between cx_Oracle and the database. For multi-user applications, make use of connection pooling. Create the pool once during application initialization. Do not oversize the pool, see :ref:`connpool` . Use a session callback function to set session state, see :ref:`Session CallBacks for Setting Pooled Connection State `. Make use of efficient cx_Oracle functions. For example, to insert multiple rows use :meth:`Cursor.executemany()` instead of :meth:`Cursor.execute()`. * Tune your SQL statements. See the `SQL Tuning Guide `__. Use :ref:`bind variables ` to avoid statement reparsing. Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query, see :ref:`Tuning Fetch Performance `. Do simple optimizations like :ref:`limiting the number of rows ` and avoiding selecting columns not used in the application. It may be faster to work with simple scalar relational values than to use Oracle Database object types. Make good use of PL/SQL to avoid executing many individual statements from cx_Oracle. Tune the :ref:`Statement Cache `. Enable :ref:`Client Result Caching ` for small lookup tables. * Tune your database. See the `Database Performance Tuning Guide `__. * Tune your network. For example, when inserting or retrieving a large number of rows (or for large data), or when using a slow network, then tune the Oracle Network Session Data Unit (SDU) and socket buffer sizes, see `Oracle Net Services: Best Practices for Database Performance and High Availability `__. * Do not commit or rollback unnecessarily. Use :attr:`Connection.autocommit` on the last of a sequence of DML statements. .. _tuningfetch: Tuning Fetch Performance ======================== To tune queries you can adjust cx_Oracle's internal buffer sizes to improve the speed of fetching rows across the network from the database, and to optimize memory usage. Regardless of which cx_Oracle method is used to get query results, internally all rows are fetched in batches from the database and buffered before being returned to the application. The internal buffer sizes can have a significant performance impact. The sizes do not affect how, or when, rows are returned to your application. They do not affect the minimum or maximum number of rows returned by a query. For best performance, tune "array fetching" with :attr:`Cursor.arraysize` and "row prefetching" with :attr:`Cursor.prefetchrows` before calling :meth:`Cursor.execute()`. Queries that return LOBs and similar types will never prefetch rows, so the ``prefetchrows`` value is ignored in those cases. The common query tuning scenario is for SELECT statements that return a large number of rows over a slow network. Increasing ``arraysize`` can improve performance by reducing the number of :ref:`round-trips ` to the database. However increasing this value increases the amount of memory required. Adjusting ``prefetchrows`` will also affect performance and memory usage. Row prefetching and array fetching are both internal buffering techniques to reduce :ref:`round-trips ` to the database. The difference is the code layer that is doing the buffering, and when the buffering occurs. The Oracle Client libraries used by cx_Oracle have separate "execute SQL statement" and "fetch data" calls. Prefetching allows query results to be returned to the application when the successful statement execution acknowledgment is returned from the database. This means that a subsequent internal "fetch data" operation does not always need to make a round-trip to the database because rows are already buffered in the Oracle Client libraries. Reducing round-trips helps performance and scalability. An overhead of prefetching is the need for an additional data copy from Oracle Client's prefetch buffers. Choosing values for ``arraysize`` and ``prefetchrows`` ++++++++++++++++++++++++++++++++++++++++++++++++++++++ The best :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` values can be found by experimenting with your application under the expected load of normal application use. This is because the cost of the extra memory copy from the prefetch buffers when fetching a large quantity of rows or very "wide" rows may outweigh the cost of a round-trip for a single cx_Oracle user on a fast network. However under production application load, the reduction of round-trips may help performance and overall system scalability. The documentation in :ref:`round-trips ` shows how to measure round-trips. Here are some suggestions for the starting point to begin your tuning: * To tune queries that return an unknown number of rows, estimate the number of rows returned and start with an appropriate :attr:`Cursor.arraysize` value. The default is 100. Then set :attr:`Cursor.prefetchrows` to the ``arraysize`` value. For example: .. code-block:: python cur = connection.cursor() cur.prefetchrows = 1000 cur.arraysize = 1000 for row in cur.execute("SELECT * FROM very_big_table"): print(row) Adjust the values as needed for performance, memory and round-trip usage. Do not make the sizes unnecessarily large. For a large quantity of rows or very "wide" rows on fast networks you may prefer to leave ``prefetchrows`` at its default value of 2. Keep ``arraysize`` as big, or bigger than, ``prefetchrows``. * If you are fetching a fixed number of rows, start your tuning by setting ``arraysize`` to the number of expected rows, and set ``prefetchrows`` to one greater than this value. (Adding one removes the need for a round-trip to check for end-of-fetch). For example, if you are querying 20 rows, perhaps to :ref:`display a page ` of data, set ``prefetchrows`` to 21 and ``arraysize`` to 20: .. code-block:: python cur = connection.cursor() cur.prefetchrows = 21 cur.arraysize = 20 for row in cur.execute(""" SELECT last_name FROM employees ORDER BY last_name OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY"""): print(row) This will return all rows for the query in one round-trip. * If you know that a query returns just one row then set :attr:`Cursor.arraysize` to 1 to minimize memory usage. The default prefetch value of 2 allows minimal round-trips for single-row queries: .. code-block:: python cur = connection.cursor() cur.arraysize = 1 cur.execute("select * from MyTable where id = 1"): row = cur.fetchone() print(row) In cx_Oracle, the ``arraysize`` and ``prefetchrows`` values are only examined when a statement is executed the first time. To change the values, create a new cursor. For example, to change ``arraysize`` for a repeated statement: .. code-block:: python array_sizes = (10, 100, 1000) for size in array_sizes: cursor = connection.cursor() cursor.arraysize = size start = time.time() cursor.execute(sql).fetchall() elapsed = time.time() - start print("Time for", size, elapsed, "seconds") There are two cases that will benefit from setting :attr:`Cursor.prefetchrows` to 0: * When passing REF CURSORS into PL/SQL packages. Setting ``prefetchrows`` to 0 can stop rows being prematurely (and silently) fetched into cx_Oracle's internal buffers, making them unavailable to the PL/SQL code that receives the REF CURSOR. * When querying a PL/SQL function that uses PIPE ROW to emit rows at intermittent intervals. By default, several rows needs to be emitted by the function before cx_Oracle can return them to the application. Setting ``prefetchrows`` to 0 helps give a consistent flow of data to the application. Prefetching can also be enabled in an external :ref:`oraaccess.xml ` file, which may be useful for tuning an application when modifying its code is not feasible. Setting the size in ``oraaccess.xml`` will affect the whole application, so it should not be the first tuning choice. One place where increasing ``arraysize`` is particularly useful is in copying data from one database to another: .. code-block:: python # setup cursors source_cursor = source_connection.cursor() source_cursor.arraysize = 1000 target_cursor = target_connection.cursor() # perform fetch and bulk insertion source_cursor.execute("select * from MyTable") while True: rows = source_cursor.fetchmany() if not rows: break target_cursor.executemany("insert into MyTable values (:1, :2)", rows) target_connection.commit() Tuning REF CURSORS ++++++++++++++++++ In cx_Oracle, REF CURSORS can also be tuned by setting the values of ``arraysize`` and ``prefetchrows``. The prefetchrows value must be set before calling the PL/SQL procedure as the REF CURSOR is executed on the server. For example: .. code-block:: python # Set the arraysize and prefetch rows of the REF cursor ref_cursor = connection.cursor() ref_cursor.prefetchrows = 1000 ref_cursor.arraysize = 1000 # Perform the tuned fetch sum_rows = 0 cursor.callproc("myrefcursorproc", [ref_cursor]) print("Sum of IntCol for", num_rows, "rows:") for row in ref_cursor: sum_rows += row[0] print(sum_rows) .. _roundtrips: Database Round-trips ==================== A round-trip is defined as the trip from the Oracle Client libraries (used by cx_Oracle) to the database and back. Calling each cx_Oracle function, or accessing each attribute, will require zero or more round-trips. Along with tuning an application's architecture and `tuning its SQL statements `__, a general performance and scalability goal is to minimize `round-trips `__. Some general tips for reducing round-trips are: * Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query. * Use :meth:`Cursor.executemany()` for optimal DML execution. * Only commit when necessary. Use :attr:`Connection.autocommit` on the last statement of a transaction. * For connection pools, use a callback to set connection state, see :ref:`Session CallBacks for Setting Pooled Connection State `. * Make use of PL/SQL procedures which execute multiple SQL statements instead of executing them individually from cx_Oracle. * Use scalar types instead of Oracle Database object types. * Avoid overuse of :meth:`Connection.ping()`. * Avoid setting :data:`SessionPool.ping_interval` to 0 or a small value. * When using SODA, use pooled connections and enable the :ref:`SODA metadata cache `. Finding the Number of Round-Trips +++++++++++++++++++++++++++++++++ Oracle's `Automatic Workload Repository `__ (AWR) reports show 'SQL*Net roundtrips to/from client' and are useful for finding the overall behavior of a system. Sometimes you may wish to find the number of round-trips used for a specific application. Snapshots of the ``V$SESSTAT`` view taken before and after doing some work can be used for this: .. code-block:: sql SELECT ss.value, sn.display_name FROM v$sesstat ss, v$statname sn WHERE ss.sid = SYS_CONTEXT('USERENV','SID') AND ss.statistic# = sn.statistic# AND sn.name LIKE '%roundtrip%client%'; .. _stmtcache: Statement Caching ================= cx_Oracle's :meth:`Cursor.execute()` and :meth:`Cursor.executemany()` functions use the `Oracle Call Interface statement cache `__ for efficient re-execution of statements. Statement caching lets Oracle Database cursors be used without re-parsing the statement. Statement caching also reduces metadata transfer costs between cx_Oracle and the database. Performance and scalability are improved. Each standalone or pooled connection has its own cache of statements with a default size of 20. The size can be set when creating connection pools or standalone connections. The size can subsequently be changed with :attr:`Connection.stmtcachesize` or :attr:`SessionPool.stmtcachesize`. In general, set the statement cache size to the size of the working set of statements being executed by the application. To manually tune the cache, monitor the general application load and the `Automatic Workload Repository `__ (AWR) "bytes sent via SQL*Net to client" values. The latter statistic should benefit from not shipping statement metadata to cx_Oracle. Adjust the statement cache size to your satisfaction. With Oracle Database 12c, or later, the statement cache size can be automatically tuned using an :ref:`oraaccess.xml ` file. Statement caching can be disabled by setting the size to 0. Disabling the cache may be beneficial when the quantity or order of statements causes cache entries to be flushed before they get a chance to be reused. For example if there are more distinct statements than cache slots, and the order of statement execution causes older statements to be flushed from the cache before the statements are re-executed. With connection pools, the effect of changing :attr:`SessionPool.stmtcachesize` after pool creation depends on the Oracle Client version: - When using Oracle Client 21 (or later), changing the cache size does not immediately affect connections previously acquired and currently in use. When those connections are subsequently released to the pool and re-acquired, they will then use the new value. If it is neccessary to change the size on a connection because it is not being released to the pool, use :data:`Connection.stmtcachesize`. - When using Oracle Client prior to version 21, changing the pool's statement cache size has no effect on connections that already exist in the pool but will affect new connections that are subsequently created, for example when the pool grows. To change the size on a connection, use :data:`Connection.stmtcachesize`. When it is inconvenient to pass statement text through an application, the :meth:`Cursor.prepare()` call can be used to avoid statement re-parsing. Subsequent ``execute()`` calls use the value ``None`` instead of the SQL text: .. code-block:: python cur.prepare("select * from dept where deptno = :id order by deptno") cur.execute(None, id = 20) res = cur.fetchall() print(res) cur.execute(None, id = 10) res = cur.fetchall() print(res) Statements passed to :meth:`~Cursor.prepare()` are also stored in the statement cache. .. _clientresultcache: Client Result Caching ===================== cx_Oracle applications can use Oracle Database's `Client Result Cache `__. The CRC enables client-side caching of SQL query (SELECT statement) results in client memory for immediate use when the same query is re-executed. This is useful for reducing the cost of queries for small, mostly static, lookup tables, such as for postal codes. CRC reduces network :ref:`round-trips `, and also reduces database server CPU usage. The cache is at the application process level. Access and invalidation is managed by the Oracle Client libraries. This removes the need for extra application logic, or external utilities, to implement a cache. CRC can be enabled by setting the `database parameters `__ ``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then restarting the database, for example: .. code-block:: sql SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE; SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE; SQL> STARTUP FORCE CRC can alternatively be configured in an :ref:`oraaccess.xml ` or :ref:`sqlnet.ora ` file on the Python host, see `Client Configuration Parameters `__. Tables can then be created, or altered, so repeated queries use CRC. This allows existing applications to use CRC without needing modification. For example: .. code-block:: sql SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE); SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE); Alternatively, hints can be used in SQL statements. For example: .. code-block:: sql SELECT /*+ result_cache */ postal_code FROM locations python-cx_Oracle-8.3.0/doc/src/user_guide/txn_management.rst000066400000000000000000000055731414105416400242000ustar00rootroot00000000000000.. _txnmgmnt: ********************** Transaction Management ********************** A database transaction is a grouping of SQL statements that make a logical data change to the database. When :meth:`Cursor.execute()` executes a SQL statement, a transaction is started or continued. By default, cx_Oracle does not commit this transaction to the database. The methods :meth:`Connection.commit()` and :meth:`Connection.rollback()` methods can be used to explicitly commit or rollback a transaction: .. code-block:: python cursor.execute("INSERT INTO mytab (name) VALUES ('John')") connection.commit() When a database connection is closed, such as with :meth:`Connection.close()`, or when variables referencing the connection go out of scope, any uncommitted transaction will be rolled back. Autocommitting ============== An alternative way to commit is to set the attribute :attr:`~Connection.autocommit` of the connection to ``True``. This ensures all :ref:`DML ` statements (INSERT, UPDATE etc) are committed as they are executed. Unlike :meth:`Connection.commit()`, this does not require an additional :ref:`round-trip ` to the database so it is more efficient when used appropriately. Note that irrespective of the autocommit value, Oracle Database will always commit an open transaction when a DDL statement is executed. When executing multiple DML statements that constitute a single transaction, it is recommended to use autocommit mode only for the last DML statement in the sequence of operations. Unnecessarily committing causes extra database load, and can destroy transactional consistency. The example below shows a new customer being added to the table ``CUST_TABLE``. The corresponding ``SALES`` table is updated with a purchase of 3000 pens from the customer. The final insert uses autocommit mode to commit both new records: .. code-block:: python # Add a new customer id_var = cursor.var(int) connection.autocommit = False # make sure any previous value is off cursor.execute(""" INSERT INTO cust_table (name) VALUES ('John') RETURNING id INTO :bvid""", bvid=id_var) # Add sales data for the new customer and commit all new values id_val = id_var.getvalue()[0] connection.autocommit = True cursor.execute("INSERT INTO sales_table VALUES (:bvid, 'pens', 3000)", bvid=id_val) Explicit Transactions ===================== The method :meth:`Connection.begin()` can be used to explicitly start a local or global transaction. Without parameters, this explicitly begins a local transaction; otherwise, this explicitly begins a distributed (global) transaction with the given parameters. See the Oracle documentation for more details. Note that in order to make use of global (distributed) transactions, the attributes :attr:`Connection.internal_name` and :attr:`Connection.external_name` attributes must be set. python-cx_Oracle-8.3.0/doc/src/user_guide/xml_data_type.rst000066400000000000000000000042311414105416400240130ustar00rootroot00000000000000.. _xmldatatype: ******************** Working with XMLTYPE ******************** Oracle XMLType columns are fetched as strings by default. This is currently limited to the maximum length of a ``VARCHAR2`` column. To return longer XML values, they must be queried as LOB values instead. The examples below demonstrate using XMLType data with cx_Oracle. The following table will be used in these examples: .. code-block:: sql CREATE TABLE xml_table ( id NUMBER, xml_data SYS.XMLTYPE ); Inserting into the table can be done by simply binding a string as shown: .. code-block:: python xml_data = """ John Smith 43 Professor Mathematics """ cursor.execute("insert into xml_table values (:id, :xml)", id=1, xml=xml_data) This approach works with XML strings up to 1 GB in size. For longer strings, a temporary CLOB must be created using :meth:`Connection.createlob()` and bound as shown: .. code-block:: python clob = connection.createlob(cx_Oracle.DB_TYPE_CLOB) clob.write(xml_data) cursor.execute("insert into xml_table values (:id, sys.xmltype(:xml))", id=2, xml=clob) Fetching XML data can be done simply for values that are shorter than the length of a VARCHAR2 column, as shown: .. code-block:: python cursor.execute("select xml_data from xml_table where id = :id", id=1) xml_data, = cursor.fetchone() print(xml_data) # will print the string that was originally stored For values that exceed the length of a VARCHAR2 column, a CLOB must be returned instead by using the function ``XMLTYPE.GETCLOBVAL()`` as shown: .. code-block:: python cursor.execute(""" select xmltype.getclobval(xml_data) from xml_table where id = :id""", id=1) clob, = cursor.fetchone() print(clob.read()) The LOB that is returned can be streamed or a string can be returned instead of a CLOB. See :ref:`lobdata` for more information about processing LOBs. python-cx_Oracle-8.3.0/odpi/000077500000000000000000000000001414105416400156735ustar00rootroot00000000000000python-cx_Oracle-8.3.0/pyproject.toml000066400000000000000000000001441414105416400176530ustar00rootroot00000000000000[build-system] requires = ["setuptools >= 40.6.0", "wheel"] build-backend = "setuptools.build_meta" python-cx_Oracle-8.3.0/samples/000077500000000000000000000000001414105416400164045ustar00rootroot00000000000000python-cx_Oracle-8.3.0/samples/README.md000066400000000000000000000043531414105416400176700ustar00rootroot00000000000000# cx_Oracle Examples This directory contains samples for [cx_Oracle][6]. Documentation is [here][7]. A separate tutorial is [here][8]. 1. The schemas and SQL objects that are referenced in the samples can be created by running the Python script [setup_samples.py][1]. The script requires SYSDBA privileges and will prompt for these credentials as well as the names of the schemas and edition that will be created, unless a number of environment variables are set as documented in the Python script [sample_env.py][2]. Run the script using the following command: python setup_samples.py Alternatively, the [SQL script][3] can be run directly via SQL\*Plus, which will always prompt for the names of the schemas and edition that will be created. sqlplus sys/syspassword@hostname/servicename @sql/setup_samples.sql 2. Run a Python script, for example: python query.py 3. After running cx_Oracle samples, the schemas and SQL objects can be dropped by running the Python script [drop_samples.py][4]. The script requires SYSDBA privileges and will prompt for these credentials as well as the names of the schemas and edition that will be dropped, unless a number of environment variables are set as documented in the Python script [sample_env.py][2]. Run the script using the following command: python drop_samples.py Alternatively, the [SQL script][5] can be run directly via SQL\*Plus, which will always prompt for the names of the schemas and edition that will be dropped. sqlplus sys/syspassword@hostname/servicename @sql/drop_samples.sql [1]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/setup_samples.py [2]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/sample_env.py [3]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/sql/setup_samples.sql [4]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/drop_samples.py [5]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/sql/drop_samples.sql [6]: https://oracle.github.io/python-cx_Oracle/ [7]: http://cx-oracle.readthedocs.org/en/latest/index.html [8]: https://oracle.github.io/python-cx_Oracle/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html python-cx_Oracle-8.3.0/samples/app_context.py000066400000000000000000000027401414105416400213050ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # app_context.py # This script demonstrates the use of application context. Application # context is available within logon triggers and can be retrieved by using the # function sys_context(). # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env # define constants used throughout the script; adjust as desired APP_CTX_NAMESPACE = "CLIENTCONTEXT" APP_CTX_ENTRIES = [ ( APP_CTX_NAMESPACE, "ATTR1", "VALUE1" ), ( APP_CTX_NAMESPACE, "ATTR2", "VALUE2" ), ( APP_CTX_NAMESPACE, "ATTR3", "VALUE3" ) ] connection = oracledb.connect(sample_env.get_main_connect_string(), appcontext=APP_CTX_ENTRIES) cursor = connection.cursor() for namespace, name, value in APP_CTX_ENTRIES: cursor.execute("select sys_context(:1, :2) from dual", (namespace, name)) value, = cursor.fetchone() print("Value of context key", name, "is", value) python-cx_Oracle-8.3.0/samples/aq_notification.py000066400000000000000000000032711414105416400221300ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # aq_notification.py # This script demonstrates using advanced queuing notification. Once this # script is running, use another session to enqueue a few messages to the # "DEMO_BOOK_QUEUE" queue. This is most easily accomplished by running the # object_aq.py sample. # # This script requires cx_Oracle 6.4 and higher. #------------------------------------------------------------------------------ import time import cx_Oracle as oracledb import sample_env registered = True def process_messages(message): global registered print("Message type:", message.type) if message.type == oracledb.EVENT_DEREG: print("Deregistration has taken place...") registered = False return print("Queue name:", message.queueName) print("Consumer name:", message.consumerName) connection = oracledb.connect(sample_env.get_main_connect_string(), events=True) sub = connection.subscribe(namespace=oracledb.SUBSCR_NAMESPACE_AQ, name="DEMO_BOOK_QUEUE", callback=process_messages, timeout=300) print("Subscription:", sub) print("--> Connection:", sub.connection) print("--> Callback:", sub.callback) print("--> Namespace:", sub.namespace) print("--> Protocol:", sub.protocol) print("--> Timeout:", sub.timeout) while registered: print("Waiting for notifications....") time.sleep(5) python-cx_Oracle-8.3.0/samples/array_dml_rowcounts.py000066400000000000000000000035201414105416400230530ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # array_dml_rowcounts.py # # Demonstrate the use of the 12.1 feature that allows cursor.executemany() # to return the number of rows affected by each individual execution as a list. # The parameter "arraydmlrowcounts" must be set to True in the call to # cursor.executemany() after which cursor.getarraydmlrowcounts() can be called. # # This script requires cx_Oracle 5.2 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # show the number of rows for each parent ID as a means of verifying the # output from the delete statement for parent_id, count in cursor.execute(""" select ParentId, count(*) from ChildTable group by ParentId order by ParentId"""): print("Parent ID:", parent_id, "has", int(count), "rows.") print() # delete the following parent IDs only parent_ids_to_delete = [20, 30, 50] print("Deleting Parent IDs:", parent_ids_to_delete) print() # enable array DML row counts for each iteration executed in executemany() cursor.executemany(""" delete from ChildTable where ParentId = :1""", [(i,) for i in parent_ids_to_delete], arraydmlrowcounts = True) # display the number of rows deleted for each parent ID row_counts = cursor.getarraydmlrowcounts() for parent_id, count in zip(parent_ids_to_delete, row_counts): print("Parent ID:", parent_id, "deleted", count, "rows.") python-cx_Oracle-8.3.0/samples/batch_errors.py000066400000000000000000000064431414105416400214420ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # batch_errors.py # # Demonstrate the use of the Oracle Database 12.1 feature that allows # cursor.executemany() to complete successfully, even if errors take # place during the execution of one or more of the individual # executions. The parameter "batcherrors" must be set to True in the # call to cursor.executemany() after which cursor.getbatcherrors() can # be called, which will return a list of error objects. # # This script requires cx_Oracle 5.2 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # define data to insert data_to_insert = [ (1016, 10, 'Child B of Parent 10'), (1017, 10, 'Child C of Parent 10'), (1018, 20, 'Child D of Parent 20'), (1018, 20, 'Child D of Parent 20'), # duplicate key (1019, 30, 'Child C of Parent 30'), (1020, 30, 'Child D of Parent 40'), (1021, 60, 'Child A of Parent 60'), # parent does not exist (1022, 40, 'Child F of Parent 40'), ] # retrieve the number of rows in the table cursor.execute(""" select count(*) from ChildTable""") count, = cursor.fetchone() print("number of rows in child table:", int(count)) print("number of rows to insert:", len(data_to_insert)) # old method: executemany() with data errors results in stoppage after the # first error takes place; the row count is updated to show how many rows # actually succeeded try: cursor.executemany("insert into ChildTable values (:1, :2, :3)", data_to_insert) except oracledb.DatabaseError as e: error, = e.args print("FAILED with error:", error.message) print("number of rows which succeeded:", cursor.rowcount) # demonstrate that the row count is accurate cursor.execute(""" select count(*) from ChildTable""") count, = cursor.fetchone() print("number of rows in child table after failed insert:", int(count)) # roll back so we can perform the same work using the new method connection.rollback() # new method: executemany() with batch errors enabled (and array DML row counts # also enabled) results in no immediate error being raised cursor.executemany("insert into ChildTable values (:1, :2, :3)", data_to_insert, batcherrors=True, arraydmlrowcounts=True) # where errors have taken place, the row count is 0; otherwise it is 1 row_counts = cursor.getarraydmlrowcounts() print("Array DML row counts:", row_counts) # display the errors that have taken place errors = cursor.getbatcherrors() print("number of errors which took place:", len(errors)) for error in errors: print("Error", error.message.rstrip(), "at row offset", error.offset) # demonstrate that all of the rows without errors have been successfully # inserted cursor.execute(""" select count(*) from ChildTable""") count, = cursor.fetchone() print("number of rows in child table after successful insert:", int(count)) python-cx_Oracle-8.3.0/samples/bind_insert.py000066400000000000000000000045521414105416400212640ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # bind_insert.py # # Demonstrate how to insert a row into a table using bind variables. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) #------------------------------------------------------------------------------ # "Bind by position" #------------------------------------------------------------------------------ rows = [ (1, "First"), (2, "Second"), (3, "Third"), (4, "Fourth"), (5, None), # Insert a NULL value (6, "Sixth"), (7, "Seventh") ] cursor = connection.cursor() # predefine maximum string size to avoid data scans and memory reallocations; # the None value indicates that the default processing can take place cursor.setinputsizes(None, 20) cursor.executemany("insert into mytab(id, data) values (:1, :2)", rows) #------------------------------------------------------------------------------ # "Bind by name" #------------------------------------------------------------------------------ rows = [ {"d": "Eighth", "i": 8}, {"d": "Ninth", "i": 9}, {"d": "Tenth", "i": 10} ] cursor = connection.cursor() # Predefine maximum string size to avoid data scans and memory reallocations cursor.setinputsizes(d=20) cursor.executemany("insert into mytab(id, data) values (:i, :d)", rows) #------------------------------------------------------------------------------ # Inserting a single bind still needs tuples #------------------------------------------------------------------------------ rows = [ ("Eleventh",), ("Twelth",) ] cursor = connection.cursor() cursor.executemany("insert into mytab(id, data) values (11, :1)", rows) #------------------------------------------------------------------------------ # Now query the results back #------------------------------------------------------------------------------ # Don't commit - this lets the demo be run multiple times #connection.commit() for row in cursor.execute('select * from mytab'): print(row) python-cx_Oracle-8.3.0/samples/bind_query.py000066400000000000000000000022401414105416400211150ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # bind_query.py # # Demonstrate how to perform a simple query limiting the rows retrieved using # a bind variable. Since the query that is executed is identical, no additional # parsing is required, thereby reducing overhead and increasing performance. It # also permits data to be bound without having to be concerned about escaping # special characters or SQL injection attacks. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() sql = 'select * from SampleQueryTab where id = :bvid' print("Query results with id = 4") for row in cursor.execute(sql, bvid = 4): print(row) print() print("Query results with id = 1") for row in cursor.execute(sql, bvid = 1): print(row) print() python-cx_Oracle-8.3.0/samples/bulk_aq.py000066400000000000000000000043631414105416400204020ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # bulk_aq.py # This script demonstrates how to use bulk enqueuing and dequeuing of # messages with advanced queuing. It makes use of a RAW queue created in the # sample setup. # # This script requires cx_Oracle 8.2 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env QUEUE_NAME = "DEMO_RAW_QUEUE" PAYLOAD_DATA = [ "The first message", "The second message", "The third message", "The fourth message", "The fifth message", "The sixth message", "The seventh message", "The eighth message", "The ninth message", "The tenth message", "The eleventh message", "The twelfth and final message" ] # connect to database connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # create queue queue = connection.queue(QUEUE_NAME) queue.deqoptions.wait = oracledb.DEQ_NO_WAIT queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG # dequeue all existing messages to ensure the queue is empty, just so that # the results are consistent while queue.deqone(): pass # enqueue a few messages print("Enqueuing messages...") batch_size = 6 data_to_enqueue = PAYLOAD_DATA while data_to_enqueue: batch_data = data_to_enqueue[:batch_size] data_to_enqueue = data_to_enqueue[batch_size:] messages = [connection.msgproperties(payload=d) for d in batch_data] for data in batch_data: print(data) queue.enqmany(messages) connection.commit() # dequeue the messages print("\nDequeuing messages...") batch_size = 8 while True: messages = queue.deqmany(batch_size) if not messages: break for props in messages: print(props.payload.decode()) connection.commit() print("\nDone.") python-cx_Oracle-8.3.0/samples/call_timeout.py000066400000000000000000000030371414105416400214420ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # call_timeout.py # # Demonstrate the use of the Oracle Client 18c feature that enables round trips # to the database to time out if a specified amount of time (in milliseconds) # has passed without a response from the database. # # This script requires cx_Oracle 7.0 and higher and Oracle Client 18.1 and # higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) connection.call_timeout = 2000 print("Call timeout set at", connection.call_timeout, "milliseconds...") cursor = connection.cursor() cursor.execute("select sysdate from dual") today, = cursor.fetchone() print("Fetch of current date before timeout:", today) # dbms_session.sleep() replaces dbms_lock.sleep() from Oracle Database 18c sleep_proc_name = "dbms_session.sleep" \ if int(connection.version.split(".")[0]) >= 18 \ else "dbms_lock.sleep" print("Sleeping...should time out...") try: cursor.callproc(sleep_proc_name, (3,)) except oracledb.DatabaseError as e: print("ERROR:", e) cursor.execute("select sysdate from dual") today, = cursor.fetchone() print("Fetch of current date after timeout:", today) python-cx_Oracle-8.3.0/samples/connection_pool.py000066400000000000000000000055041414105416400221520ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # connection_pool.py # This script demonstrates the use of connection pooling. Pools can # significantly reduce connection times for long running applications that # repeatedly open and close connections. Internal features help protect against # dead connections, and also aid use of Oracle Database features such as FAN # and Application Continuity. # The script uses threading to show multiple users of the pool. One thread # performs a database sleep while another performs a query. A more typical # application might be a web service that handles requests from multiple users. # Note only one operation (such as an execute or fetch) can take place at a time # on each connection. # # Also see session_callback.py. # #------------------------------------------------------------------------------ import threading import cx_Oracle as oracledb import sample_env # Create a Connection Pool pool = oracledb.SessionPool(user=sample_env.get_main_user(), password=sample_env.get_main_password(), dsn=sample_env.get_connect_string(), min=2, max=5, increment=1) def the_long_query(): with pool.acquire() as conn: cursor = conn.cursor() cursor.arraysize = 25000 print("the_long_query(): beginning execute...") cursor.execute(""" select * from TestNumbers cross join TestNumbers cross join TestNumbers cross join TestNumbers cross join TestNumbers cross join TestNumbers""") print("the_long_query(): done execute...") while True: rows = cursor.fetchmany() if not rows: break print("the_long_query(): fetched", len(rows), "rows...") print("the_long_query(): all done!") def do_a_lock(): with pool.acquire() as conn: # dbms_session.sleep() replaces dbms_lock.sleep() # from Oracle Database 18c sleep_proc_name = "dbms_session.sleep" \ if int(conn.version.split(".")[0]) >= 18 \ else "dbms_lock.sleep" cursor = conn.cursor() print("do_a_lock(): beginning execute...") cursor.callproc(sleep_proc_name, (5,)) print("do_a_lock(): done execute...") thread1 = threading.Thread(target=the_long_query) thread1.start() thread2 = threading.Thread(target=do_a_lock) thread2.start() thread1.join() thread2.join() print("All done!") python-cx_Oracle-8.3.0/samples/cqn.py000066400000000000000000000052251414105416400175430ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # cqn.py # This script demonstrates using continuous query notification in Python, a # feature that is available in Oracle 11g and later. Once this script is # running, use another session to insert, update or delete rows from the table # TestTempTable and you will see the notification of that change. # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import time import cx_Oracle as oracledb import sample_env registered = True def callback(message): global registered print("Message type:", message.type) if not message.registered: print("Deregistration has taken place...") registered = False return print("Message database name:", message.dbname) print("Message tranasction id:", message.txid) print("Message queries:") for query in message.queries: print("--> Query ID:", query.id) print("--> Query Operation:", query.operation) for table in query.tables: print("--> --> Table Name:", table.name) print("--> --> Table Operation:", table.operation) if table.rows is not None: print("--> --> Table Rows:") for row in table.rows: print("--> --> --> Row RowId:", row.rowid) print("--> --> --> Row Operation:", row.operation) print("-" * 60) print("=" * 60) connection = oracledb.connect(sample_env.get_main_connect_string(), events=True) qos = oracledb.SUBSCR_QOS_QUERY | oracledb.SUBSCR_QOS_ROWIDS sub = connection.subscribe(callback=callback, timeout=1800, qos=qos) print("Subscription:", sub) print("--> Connection:", sub.connection) print("--> Callback:", sub.callback) print("--> Namespace:", sub.namespace) print("--> Protocol:", sub.protocol) print("--> Timeout:", sub.timeout) print("--> Operations:", sub.operations) print("--> Rowids?:", bool(sub.qos & oracledb.SUBSCR_QOS_ROWIDS)) query_id = sub.registerquery("select * from TestTempTable") print("Registered query:", query_id) while registered: print("Waiting for notifications....") time.sleep(5) python-cx_Oracle-8.3.0/samples/cqn2.py000066400000000000000000000060231414105416400176220ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # cqn2.py # This script demonstrates using continuous query notification in Python, a # feature that is available in Oracle 11g and later. Once this script is # running, use another session to insert, update or delete rows from the table # TestTempTable and you will see the notification of that change. # # This script differs from cqn.py in that it shows how a connection can be # acquired from a session pool and used to query the changes that have been # made. # # This script requires cx_Oracle 7 or higher. #------------------------------------------------------------------------------ import time import cx_Oracle as oracledb import sample_env registered = True def callback(message): global registered if not message.registered: print("Deregistration has taken place...") registered = False return connection = pool.acquire() for query in message.queries: for table in query.tables: if table.rows is None: print("Too many row changes detected in table", table.name) continue num_rows_deleted = 0 print(len(table.rows), "row changes detected in table", table.name) for row in table.rows: if row.operation & oracledb.OPCODE_DELETE: num_rows_deleted += 1 continue ops = [] if row.operation & oracledb.OPCODE_INSERT: ops.append("inserted") if row.operation & oracledb.OPCODE_UPDATE: ops.append("updated") cursor = connection.cursor() cursor.execute(""" select IntCol from TestTempTable where rowid = :rid""", rid=row.rowid) int_col, = cursor.fetchone() print(" Row with IntCol", int_col, "was", " and ".join(ops)) if num_rows_deleted > 0: print(" ", num_rows_deleted, "rows deleted") print("=" * 60) pool = oracledb.SessionPool(user=sample_env.get_main_user(), password=sample_env.get_main_password(), dsn=sample_env.get_connect_string(), min=2, max=5, increment=1, events=True) with pool.acquire() as connection: qos = oracledb.SUBSCR_QOS_QUERY | oracledb.SUBSCR_QOS_ROWIDS sub = connection.subscribe(callback=callback, timeout=1800, qos=qos) print("Subscription created with ID:", sub.id) query_id = sub.registerquery("select * from TestTempTable") print("Registered query with ID:", query_id) while registered: print("Waiting for notifications....") time.sleep(5) python-cx_Oracle-8.3.0/samples/database_change_notification.py000066400000000000000000000047161414105416400246050ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # database_change_notification.py # This script demonstrates using database change notification in Python, a # feature that is available in Oracle 10g Release 2. Once this script is # running, use another session to insert, update or delete rows from the table # TestTempTable and you will see the notification of that change. # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import time import cx_Oracle as oracledb import sample_env registered = True def callback(message): global registered print("Message type:", message.type) if not message.registered: print("Deregistration has taken place...") registered = False return print("Message database name:", message.dbname) print("Message tranasction id:", message.txid) print("Message tables:") for table in message.tables: print("--> Table Name:", table.name) print("--> Table Operation:", table.operation) if table.rows is not None: print("--> Table Rows:") for row in table.rows: print("--> --> Row RowId:", row.rowid) print("--> --> Row Operation:", row.operation) print("-" * 60) print("=" * 60) connection = oracledb.connect(sample_env.get_main_connect_string(), events=True) sub = connection.subscribe(callback=callback, timeout=1800, qos=oracledb.SUBSCR_QOS_ROWIDS) print("Subscription:", sub) print("--> Connection:", sub.connection) print("--> ID:", sub.id) print("--> Callback:", sub.callback) print("--> Namespace:", sub.namespace) print("--> Protocol:", sub.protocol) print("--> Timeout:", sub.timeout) print("--> Operations:", sub.operations) print("--> Rowids?:", bool(sub.qos & oracledb.SUBSCR_QOS_ROWIDS)) sub.registerquery("select * from TestTempTable") while registered: print("Waiting for notifications....") time.sleep(5) python-cx_Oracle-8.3.0/samples/database_shutdown.py000066400000000000000000000025151414105416400224600ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # database_shutdown.py # This script demonstrates shutting down a database using Python. The # connection used assumes that the environment variable ORACLE_SID has been # set. # # This script requires cx_Oracle 4.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb # need to connect as SYSDBA or SYSOPER connection = oracledb.connect(mode=oracledb.SYSDBA) # first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used, # there is no need for any of the other steps connection.shutdown(mode=oracledb.DBSHUTDOWN_IMMEDIATE) # now close and dismount the database cursor = connection.cursor() cursor.execute("alter database close normal") cursor.execute("alter database dismount") # perform the final shutdown call connection.shutdown(mode=oracledb.DBSHUTDOWN_FINAL) python-cx_Oracle-8.3.0/samples/database_startup.py000066400000000000000000000022721414105416400223070ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # database_startup.py # This script demonstrates starting up a database using Python. The # connection used assumes that the environment variable ORACLE_SID has been # set. # # This script requires cx_Oracle 4.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb # the connection must be in PRELIM_AUTH mode connection = oracledb.connect(mode=oracledb.SYSDBA | oracledb.PRELIM_AUTH) connection.startup() # the following statements must be issued in normal SYSDBA mode connection = oracledb.connect("/", mode=oracledb.SYSDBA) cursor = connection.cursor() cursor.execute("alter database mount") cursor.execute("alter database open") python-cx_Oracle-8.3.0/samples/dbms_output.py000066400000000000000000000030001414105416400213140ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # dbms_output.py # This script demonstrates one method of fetching the lines produced by # the DBMS_OUTPUT package. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # enable DBMS_OUTPUT cursor.callproc("dbms_output.enable") # execute some PL/SQL that generates output with DBMS_OUTPUT.PUT_LINE cursor.execute(""" begin dbms_output.put_line('This is the cx_Oracle manual'); dbms_output.put_line(''); dbms_output.put_line('Demonstrating use of DBMS_OUTPUT'); end;""") # tune this size for your application chunk_size = 10 # create variables to hold the output lines_var = cursor.arrayvar(str, chunk_size) num_lines_var = cursor.var(int) num_lines_var.setvalue(0, chunk_size) # fetch the text that was added by PL/SQL while True: cursor.callproc("dbms_output.get_lines", (lines_var, num_lines_var)) num_lines = num_lines_var.getvalue() lines = lines_var.getvalue()[:num_lines] for line in lines: print(line or "") if num_lines < chunk_size: break python-cx_Oracle-8.3.0/samples/dml_returning_multiple_rows.py000066400000000000000000000033061414105416400246160ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # dml_returning_multiple_rows.py # This script demonstrates the use of DML returning with multiple rows being # returned at once. # # This script requires cx_Oracle 6.0 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env # truncate table first so that script can be rerun connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() print("Truncating table...") cursor.execute("truncate table TestTempTable") # populate table with a few rows for i in range(5): data = (i + 1, "Test String #%d" % (i + 1)) print("Adding row", data) cursor.execute("insert into TestTempTable values (:1, :2)", data) # now delete them and use DML returning to return the data that was inserted int_col = cursor.var(int) string_col = cursor.var(str) print("Deleting data with DML returning...") cursor.execute(""" delete from TestTempTable returning IntCol, StringCol into :int_col, :string_col""", int_col=int_col, string_col=string_col) print("Data returned:") for int_val, string_val in zip(int_col.getvalue(), string_col.getvalue()): print(tuple([int_val, string_val])) python-cx_Oracle-8.3.0/samples/drcp.py000066400000000000000000000032701414105416400177100ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # drcp.py # This script demonstrates the use of Database Resident Connection Pooling # (DRCP) which provides a connection pool in the database server, thereby # reducing the cost of creating and tearing down client connections. The pool # can be started and stopped in the database by issuing the following commands # in SQL*Plus: # # exec dbms_connection_pool.start_pool() # exec dbms_connection_pool.stop_pool() # # Statistics regarding the pool can be acquired from the following query: # # select * from v$cpool_cc_stats; # # There is no difference in how a connection is used once it has been # established. # # DRCP has most benefit when used in conjunction with cx_Oracle's local # connection pool, see the cx_Oracle documentation. # # This script requires cx_Oracle 5.0 or higher. # #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env conn = oracledb.connect(sample_env.get_drcp_connect_string(), cclass="PYCLASS", purity=oracledb.ATTR_PURITY_SELF) cursor = conn.cursor() print("Performing query using DRCP...") for row in cursor.execute("select * from TestNumbers order by IntCol"): print(row) python-cx_Oracle-8.3.0/samples/drop_samples.py000066400000000000000000000017631414105416400214550ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # drop_samples.py # # Drops the database objects used for the cx_Oracle samples. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env def drop_samples(conn): print("Dropping sample schemas and edition...") sample_env.run_sql_script(conn, "drop_samples", main_user=sample_env.get_main_user(), edition_user=sample_env.get_edition_user(), edition_name=sample_env.get_edition_name()) if __name__ == "__main__": conn = oracledb.connect(sample_env.get_admin_connect_string()) drop_samples(conn) print("Done.") python-cx_Oracle-8.3.0/samples/editioning.py000066400000000000000000000062041414105416400211110ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # editioning.py # This script demonstrates the use of Edition-Based Redefinition, available # in Oracle# Database 11.2 and higher. See the Oracle documentation on the # subject for additional information. Adjust the contants at the top of the # script for your own database as needed. # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import os import cx_Oracle as oracledb import sample_env # connect to the editions user and create a procedure edition_connect_string = sample_env.get_edition_connect_string() edition_name = sample_env.get_edition_name() connection = oracledb.connect(edition_connect_string) print("Edition should be None, actual value is:", repr(connection.edition)) cursor = connection.cursor() cursor.execute(""" create or replace function TestEditions return varchar2 as begin return 'Base Procedure'; end;""") result = cursor.callfunc("TestEditions", str) print("Function should return 'Base Procedure', actually returns:", repr(result)) # next, change the edition and recreate the procedure in the new edition cursor.execute("alter session set edition = %s" % edition_name) print("Edition should be", repr(edition_name.upper()), "actual value is:", repr(connection.edition)) cursor.execute(""" create or replace function TestEditions return varchar2 as begin return 'Edition 1 Procedure'; end;""") result = cursor.callfunc("TestEditions", str) print("Function should return 'Edition 1 Procedure', actually returns:", repr(result)) # next, change the edition back to the base edition and demonstrate that the # original function is being called cursor.execute("alter session set edition = ORA$BASE") result = cursor.callfunc("TestEditions", str) print("Function should return 'Base Procedure', actually returns:", repr(result)) # the edition can be set upon connection connection = oracledb.connect(edition_connect_string, edition=edition_name.upper()) cursor = connection.cursor() result = cursor.callfunc("TestEditions", str) print("Function should return 'Edition 1 Procedure', actually returns:", repr(result)) # it can also be set via the environment variable ORA_EDITION os.environ["ORA_EDITION"] = edition_name.upper() connection = oracledb.connect(edition_connect_string) print("Edition should be", repr(edition_name.upper()), "actual value is:", repr(connection.edition)) cursor = connection.cursor() result = cursor.callfunc("TestEditions", str) print("Function should return 'Edition 1 Procedure', actually returns:", repr(result)) python-cx_Oracle-8.3.0/samples/generic_row_factory.py000066400000000000000000000031151414105416400230100ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # generic_row_factory.py # # Demonstrate the ability to return named tuples for all queries using a # subclassed cursor and row factory. #------------------------------------------------------------------------------ import collections import cx_Oracle as oracledb import sample_env class Connection(oracledb.Connection): def cursor(self): return Cursor(self) class Cursor(oracledb.Cursor): def execute(self, statement, args=None): prepare_needed = (self.statement != statement) result = super().execute(statement, args or []) if prepare_needed: description = self.description if description is not None: names = [d[0] for d in description] self.rowfactory = collections.namedtuple("GenericQuery", names) return result # create new subclassed connection and cursor connection = Connection(sample_env.get_main_connect_string()) cursor = connection.cursor() # the names are now available directly for each query executed for row in cursor.execute("select ParentId, Description from ParentTable"): print(row.PARENTID, "->", row.DESCRIPTION) print() for row in cursor.execute("select ChildId, Description from ChildTable"): print(row.CHILDID, "->", row.DESCRIPTION) print() python-cx_Oracle-8.3.0/samples/implicit_results.py000066400000000000000000000027701414105416400223570ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # implicit_results.py # This script demonstrates the use of the 12.1 feature that allows PL/SQL # procedures to return result sets implicitly, without having to explicitly # define them. # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # use PL/SQL block to return two cursors cursor.execute(""" declare c1 sys_refcursor; c2 sys_refcursor; begin open c1 for select * from TestNumbers; dbms_sql.return_result(c1); open c2 for select * from TestStrings; dbms_sql.return_result(c2); end;""") # display results for ix, result_set in enumerate(cursor.getimplicitresults()): print("Result Set #" + str(ix + 1)) for row in result_set: print(row) print() python-cx_Oracle-8.3.0/samples/insert_geometry.py000066400000000000000000000040461414105416400222010ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # insert_geometry.py # This script demonstrates the ability to create Oracle objects (this example # uses SDO_GEOMETRY) and insert them into a table. # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env # create and populate Oracle objects connection = oracledb.connect(sample_env.get_main_connect_string()) type_obj = connection.gettype("MDSYS.SDO_GEOMETRY") element_info_type_obj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") ordinate_type_obj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY") obj = type_obj.newobject() obj.SDO_GTYPE = 2003 obj.SDO_ELEM_INFO = element_info_type_obj.newobject() obj.SDO_ELEM_INFO.extend([1, 1003, 3]) obj.SDO_ORDINATES = ordinate_type_obj.newobject() obj.SDO_ORDINATES.extend([1, 1, 5, 7]) print("Created object", obj) # create table, if necessary cursor = connection.cursor() cursor.execute(""" select count(*) from user_tables where table_name = 'TESTGEOMETRY'""") count, = cursor.fetchone() if count == 0: print("Creating table...") cursor.execute(""" create table TestGeometry ( IntCol number(9) not null, Geometry MDSYS.SDO_GEOMETRY not null )""") # remove all existing rows and then add a new one print("Removing any existing rows...") cursor.execute("delete from TestGeometry") print("Adding row to table...") cursor.execute("insert into TestGeometry values (1, :obj)", obj=obj) connection.commit() print("Success!") python-cx_Oracle-8.3.0/samples/json_blob.py000066400000000000000000000053161414105416400207320ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # json_blob.py # Shows how to use a BLOB as a JSON column store. # # Note: with Oracle Database 21c using the new JSON type is recommended # instead, see json_direct.py # # Documentation: # cx_Oracle: https://cx-oracle.readthedocs.io/en/latest/user_guide/json_data_type.html # Oracle Database: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN # #------------------------------------------------------------------------------ import json import sys import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) client_version = oracledb.clientversion()[0] db_version = int(connection.version.split(".")[0]) # Minimum database vesion is 12 if db_version < 12: sys.exit("This example requires Oracle Database 12.1.0.2 or later") # Create a table cursor = connection.cursor() cursor.execute(""" begin execute immediate 'drop table customers'; exception when others then if sqlcode <> -942 then raise; end if; end;""") cursor.execute(""" create table customers ( id integer not null primary key, json_data blob check (json_data is json) ) lob (json_data) store as (cache)""") # Insert JSON data data = dict(name="Rod", dept="Sales", location="Germany") inssql = "insert into customers values (:1, :2)" if client_version >= 21 and db_version >= 21: # Take advantage of direct binding cursor.setinputsizes(None, oracledb.DB_TYPE_JSON) cursor.execute(inssql, [1, data]) else: # Insert the data as a JSON string cursor.execute(inssql, [1, json.dumps(data)]) # Select JSON data sql = "SELECT c.json_data FROM customers c" for j, in cursor.execute(sql): print(json.loads(j.read())) # Using JSON_VALUE to extract a value from a JSON column sql = """SELECT JSON_VALUE(json_data, '$.location') FROM customers OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY""" for r in cursor.execute(sql): print(r) # Using dot-notation to extract a value from a JSON (BLOB storage) column sql = """SELECT c.json_data.location FROM customers c OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY""" for j, in cursor.execute(sql): print(j) # Using JSON_OBJECT to extract relational data as JSON sql = """SELECT JSON_OBJECT('key' IS d.dummy) dummy FROM dual d""" for r in cursor.execute(sql): print(r) python-cx_Oracle-8.3.0/samples/json_direct.py000066400000000000000000000052601414105416400212640ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # json_direct.py # Shows some JSON features of Oracle Database 21c. # See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN # # For JSON with older databases see json_blob.py #------------------------------------------------------------------------------ import json import sys import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) client_version = oracledb.clientversion()[0] db_version = int(connection.version.split(".")[0]) # this script only works with Oracle Database 21 if db_version < 21: sys.exit("This example requires Oracle Database 21.1 or later. " "Try json_blob.py") # Create a table cursor = connection.cursor() cursor.execute(""" begin execute immediate 'drop table customers'; exception when others then if sqlcode <> -942 then raise; end if; end;""") cursor.execute(""" create table customers ( id integer not null primary key, json_data json )""") # Insert JSON data data = dict(name="Rod", dept="Sales", location="Germany") inssql = "insert into customers values (:1, :2)" if client_version >= 21: # Take advantage of direct binding cursor.setinputsizes(None, oracledb.DB_TYPE_JSON) cursor.execute(inssql, [1, data]) else: # Insert the data as a JSON string cursor.execute(inssql, [1, json.dumps(data)]) # Select JSON data sql = "SELECT c.json_data FROM customers c" if client_version >= 21: for j, in cursor.execute(sql): print(j) else: for j, in cursor.execute(sql): print(json.loads(j.read())) # Using JSON_VALUE to extract a value from a JSON column sql = """SELECT JSON_VALUE(json_data, '$.location') FROM customers OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY""" for r in cursor.execute(sql): print(r) # Using dot-notation to extract a value from a JSON column sql = """SELECT c.json_data.location FROM customers c OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY""" if client_version >= 21: for j, in cursor.execute(sql): print(j) else: for j, in cursor.execute(sql): print(json.loads(j.read())) # Using JSON_OBJECT to extract relational data as JSON sql = """SELECT JSON_OBJECT('key' IS d.dummy) dummy FROM dual d""" for r in cursor.execute(sql): print(r) python-cx_Oracle-8.3.0/samples/last_rowid.py000066400000000000000000000037351414105416400211350ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # last_rowid.py # Demonstrates the use of the cursor.lastrowid attribute. # # This script requires cx_Oracle 7.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) row1 = [1, "First"] row2 = [2, "Second"] # insert a couple of rows and retain the rowid of each cursor = connection.cursor() cursor.execute("insert into mytab (id, data) values (:1, :2)", row1) rowid1 = cursor.lastrowid print("Row 1:", row1) print("Rowid 1:", rowid1) print() cursor.execute("insert into mytab (id, data) values (:1, :2)", row2) rowid2 = cursor.lastrowid print("Row 2:", row2) print("Rowid 2:", rowid2) print() # the row can be fetched with the rowid that was retained cursor.execute("select id, data from mytab where rowid = :1", [rowid1]) print("Row 1:", cursor.fetchone()) cursor.execute("select id, data from mytab where rowid = :1", [rowid2]) print("Row 2:", cursor.fetchone()) print() # updating multiple rows only returns the rowid of the last updated row cursor.execute("update mytab set data = data || ' (Modified)'") cursor.execute("select id, data from mytab where rowid = :1", [cursor.lastrowid]) print("Last updated row:", cursor.fetchone()) # deleting multiple rows only returns the rowid of the last deleted row cursor.execute("delete from mytab") print("Rowid of last deleted row:", cursor.lastrowid) # deleting no rows results in a value of None cursor.execute("delete from mytab") print("Rowid when no rows are deleted:", cursor.lastrowid) # Don't commit - this lets us run the demo multiple times #connection.commit() python-cx_Oracle-8.3.0/samples/multi_consumer_aq.py000066400000000000000000000037271414105416400225150ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # multi_consumer_aq.py # This script demonstrates how to use multi-consumer advanced queuing. It # makes use of a RAW queue created in the sample setup. # # This script requires cx_Oracle 8.2 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env QUEUE_NAME = "DEMO_RAW_QUEUE_MULTI" PAYLOAD_DATA = [ "The first message", "The second message", "The third message", "The fourth and final message" ] # connect to database connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # create queue queue = connection.queue(QUEUE_NAME) queue.deqoptions.wait = oracledb.DEQ_NO_WAIT queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG # enqueue a few messages print("Enqueuing messages...") for data in PAYLOAD_DATA: print(data) queue.enqone(connection.msgproperties(payload=data)) connection.commit() print() # dequeue the messages for consumer A print("Dequeuing the messages for consumer A...") queue.deqoptions.consumername = "SUBSCRIBER_A" while True: props = queue.deqone() if not props: break print(props.payload.decode()) connection.commit() print() # dequeue the message for consumer B print("Dequeuing the messages for consumer B...") queue.deqoptions.consumername = "SUBSCRIBER_B" while True: props = queue.deqone() if not props: break print(props.payload.decode()) connection.commit() print("\nDone.") python-cx_Oracle-8.3.0/samples/object_aq.py000066400000000000000000000040661414105416400207130ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # object_aq.py # This script demonstrates how to use advanced queuing with objects. It makes # use of a simple type and queue created in the sample setup. # # This script requires cx_Oracle 8.2 and higher. #------------------------------------------------------------------------------ import decimal import cx_Oracle as oracledb import sample_env BOOK_TYPE_NAME = "UDT_BOOK" QUEUE_NAME = "DEMO_BOOK_QUEUE" BOOK_DATA = [ ("The Fellowship of the Ring", "Tolkien, J.R.R.", decimal.Decimal("10.99")), ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.", decimal.Decimal("7.99")) ] # connect to database connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # create queue books_type = connection.gettype(BOOK_TYPE_NAME) queue = connection.queue(QUEUE_NAME, payload_type=books_type) queue.deqoptions.wait = oracledb.DEQ_NO_WAIT queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG # dequeue all existing messages to ensure the queue is empty, just so that # the results are consistent while queue.deqone(): pass # enqueue a few messages print("Enqueuing messages...") for title, authors, price in BOOK_DATA: book = books_type.newobject() book.TITLE = title book.AUTHORS = authors book.PRICE = price print(title) queue.enqone(connection.msgproperties(payload=book)) connection.commit() # dequeue the messages print("\nDequeuing messages...") while True: props = queue.deqone() if not props: break print(props.payload.TITLE) connection.commit() print("\nDone.") python-cx_Oracle-8.3.0/samples/plsql_collection.py000066400000000000000000000030211414105416400223200ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # plsql_collection.py # # Demonstrate how to get the value of a PL/SQL collection from a stored # procedure. # # This feature is new in cx_Oracle 5.3 and is only available in Oracle # Database 12.1 and higher. The ability to get the collection as a dictionary # is new in cx_Oracle 7.0. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) # create new empty object of the correct type # note the use of a PL/SQL type defined in a package type_obj = connection.gettype("PKG_DEMO.UDT_STRINGLIST") obj = type_obj.newobject() # call the stored procedure which will populate the object cursor = connection.cursor() cursor.callproc("pkg_Demo.DemoCollectionOut", (obj,)) # show the indexes that are used by the collection print("Indexes and values of collection:") ix = obj.first() while ix is not None: print(ix, "->", obj.getelement(ix)) ix = obj.next(ix) print() # show the values as a simple list print("Values of collection as list:") print(obj.aslist()) print() # show the values as a simple dictionary print("Values of collection as dictionary:") print(obj.asdict()) print() python-cx_Oracle-8.3.0/samples/plsql_function.py000066400000000000000000000012731414105416400220210ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # plsql_function.py # # Demonstrate how to call a PL/SQL function and get its return value. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() res = cursor.callfunc('myfunc', int, ('abc', 2)) print(res) python-cx_Oracle-8.3.0/samples/plsql_procedure.py000066400000000000000000000013601414105416400221610ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # plsql_procedure.py # # Demonstrate how to call a PL/SQL stored procedure and get the results of an # OUT variable. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() myvar = cursor.var(int) cursor.callproc('myproc', (123, myvar)) print(myvar.getvalue()) python-cx_Oracle-8.3.0/samples/plsql_record.py000066400000000000000000000030571414105416400214540ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # plsql_record.py # # Demonstrate how to bind (in and out) a PL/SQL record. # # This feature is only available in Oracle Database 12.1 and higher. #------------------------------------------------------------------------------ import datetime import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) # create new object of the correct type # note the use of a PL/SQL record defined in a package # a table record identified by TABLE%ROWTYPE can also be used type_obj = connection.gettype("PKG_DEMO.UDT_DEMORECORD") obj = type_obj.newobject() obj.NUMBERVALUE = 6 obj.STRINGVALUE = "Test String" obj.DATEVALUE = datetime.datetime(2016, 5, 28) obj.BOOLEANVALUE = False # show the original values print("NUMBERVALUE ->", obj.NUMBERVALUE) print("STRINGVALUE ->", obj.STRINGVALUE) print("DATEVALUE ->", obj.DATEVALUE) print("BOOLEANVALUE ->", obj.BOOLEANVALUE) print() # call the stored procedure which will modify the object cursor = connection.cursor() cursor.callproc("pkg_Demo.DemoRecordsInOut", (obj,)) # show the modified values print("NUMBERVALUE ->", obj.NUMBERVALUE) print("STRINGVALUE ->", obj.STRINGVALUE) print("DATEVALUE ->", obj.DATEVALUE) print("BOOLEANVALUE ->", obj.BOOLEANVALUE) print() python-cx_Oracle-8.3.0/samples/query.py000066400000000000000000000023161414105416400201250ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # query.py # # Demonstrate how to perform a query in different ways. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) sql = """ select * from SampleQueryTab where id < 6 order by id""" print("Get all rows via iterator") cursor = connection.cursor() for result in cursor.execute(sql): print(result) print() print("Query one row at a time") cursor.execute(sql) row = cursor.fetchone() print(row) row = cursor.fetchone() print(row) print() print("Fetch many rows") cursor.execute(sql) res = cursor.fetchmany(3) print(res) print() print("Fetch each row as a Dictionary") cursor.execute(sql) columns = [col[0] for col in cursor.description] cursor.rowfactory = lambda *args: dict(zip(columns, args)) for row in cursor: print(row) python-cx_Oracle-8.3.0/samples/query_arraysize.py000066400000000000000000000020541414105416400222150ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # query_arraysize.py # # Demonstrate how to alter the array size and prefetch rows value on a cursor # in order to reduce the number of network round trips and overhead required to # fetch all of the rows from a large table. #------------------------------------------------------------------------------ import time import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) start = time.time() cursor = connection.cursor() cursor.prefetchrows = 1000 cursor.arraysize = 1000 cursor.execute('select * from bigtab') res = cursor.fetchall() # print(res) # uncomment to display the query results elapsed = (time.time() - start) print("Retrieved", len(res), "rows in", elapsed, "seconds") python-cx_Oracle-8.3.0/samples/query_strings_as_bytes.py000066400000000000000000000037661414105416400236010ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # query_strings_as_bytes.py # # Demonstrates how to query strings as bytes (bypassing decoding of the bytes # into a Python string). This can be useful when attempting to fetch data that # was stored in the database in the wrong encoding. # # This script requires cx_Oracle 8.2 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env STRING_VAL = 'I bought a cafetière on the Champs-Élysées' def return_strings_as_bytes(cursor, name, default_type, size, precision, scale): if default_type == oracledb.DB_TYPE_VARCHAR: return cursor.var(str, arraysize=cursor.arraysize, bypass_decode=True) with oracledb.connect(sample_env.get_main_connect_string()) as conn: # truncate table and populate with our data of choice with conn.cursor() as cursor: cursor.execute("truncate table TestTempTable") cursor.execute("insert into TestTempTable values (1, :val)", val=STRING_VAL) conn.commit() # fetch the data normally and show that it is returned as a string with conn.cursor() as cursor: cursor.execute("select IntCol, StringCol from TestTempTable") print("Data fetched using normal technique:") for row in cursor: print(row) print() # fetch the data, bypassing the decode and show that it is returned as # bytes with conn.cursor() as cursor: cursor.outputtypehandler = return_strings_as_bytes cursor.execute("select IntCol, StringCol from TestTempTable") print("Data fetched using bypass decode technique:") for row in cursor: print(row) python-cx_Oracle-8.3.0/samples/raw_aq.py000066400000000000000000000033511414105416400202320ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # raw_aq.py # This script demonstrates how to use advanced queuing with RAW data. It # makes use of a RAW queue created in the sample setup. # # This script requires cx_Oracle 8.2 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env QUEUE_NAME = "DEMO_RAW_QUEUE" PAYLOAD_DATA = [ "The first message", "The second message", "The third message", "The fourth and final message" ] # connect to database connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # create queue queue = connection.queue(QUEUE_NAME) queue.deqoptions.wait = oracledb.DEQ_NO_WAIT queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG # dequeue all existing messages to ensure the queue is empty, just so that # the results are consistent while queue.deqone(): pass # enqueue a few messages print("Enqueuing messages...") for data in PAYLOAD_DATA: print(data) queue.enqone(connection.msgproperties(payload=data)) connection.commit() # dequeue the messages print("\nDequeuing messages...") while True: props = queue.deqone() if not props: break print(props.payload.decode()) connection.commit() print("\nDone.") python-cx_Oracle-8.3.0/samples/ref_cursor.py000066400000000000000000000035001414105416400211250ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # ref_cursor.py # Demonstrates the use of REF cursors. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() ref_cursor = connection.cursor() cursor.callproc("myrefcursorproc", (2, 6, ref_cursor)) print("Rows between 2 and 6:") for row in ref_cursor: print(row) print() ref_cursor = connection.cursor() cursor.callproc("myrefcursorproc", (8, 9, ref_cursor)) print("Rows between 8 and 9:") for row in ref_cursor: print(row) print() #------------------------------------------------------------------------------ # Setting prefetchrows and arraysize of a REF cursor can improve performance # when fetching a large number of rows (Tuned Fetch) #------------------------------------------------------------------------------ # Truncate the table used for this demo cursor.execute("truncate table TestTempTable") # Populate the table with a large number of rows num_rows = 50000 sql = "insert into TestTempTable (IntCol) values (:1)" data = [(n + 1,) for n in range(num_rows)] cursor.executemany(sql, data) # Set the arraysize and prefetch rows of the REF cursor ref_cursor = connection.cursor() ref_cursor.prefetchrows = 1000 ref_cursor.arraysize = 1000 # Perform the tuned fetch sum_rows = 0 cursor.callproc("myrefcursorproc2", [ref_cursor]) print("Sum of IntCol for", num_rows, "rows:") for row in ref_cursor: sum_rows += row[0] print(sum_rows) python-cx_Oracle-8.3.0/samples/return_lobs_as_strings.py000066400000000000000000000053321414105416400235530ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # return_lobs_as_strings.py # Returns all CLOB values as strings and BLOB values as bytes. The # performance of this technique is significantly better than fetching the LOBs # and then reading the contents of the LOBs as it avoids round-trips to the # database. Be aware, however, that this method requires contiguous memory so # is not usable for very large LOBs. # # This script requires cx_Oracle 5.0 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env def output_type_handler(cursor, name, default_type, size, precision, scale): if default_type == oracledb.CLOB: return cursor.var(oracledb.LONG_STRING, arraysize=cursor.arraysize) if default_type == oracledb.BLOB: return cursor.var(oracledb.LONG_BINARY, arraysize=cursor.arraysize) connection = oracledb.connect(sample_env.get_main_connect_string()) connection.outputtypehandler = output_type_handler cursor = connection.cursor() # add some data to the tables print("Populating tables with data...") cursor.execute("truncate table TestClobs") cursor.execute("truncate table TestBlobs") long_string = "" for i in range(10): char = chr(ord('A') + i) long_string += char * 25000 # uncomment the line below for cx_Oracle 5.3 and earlier # cursor.setinputsizes(None, oracledb.LONG_STRING) cursor.execute("insert into TestClobs values (:1, :2)", (i + 1, "STRING " + long_string)) # uncomment the line below for cx_Oracle 5.3 and earlier # cursor.setinputsizes(None, oracledb.LONG_BINARY) cursor.execute("insert into TestBlobs values (:1, :2)", (i + 1, long_string.encode("ascii"))) connection.commit() # fetch the data and show the results print("CLOBS returned as strings") cursor.execute(""" select IntCol, ClobCol from TestClobs order by IntCol""") for int_col, value in cursor: print("Row:", int_col, "string of length", len(value)) print() print("BLOBS returned as bytes") cursor.execute(""" select IntCol, BlobCol from TestBlobs order by IntCol""") for int_col, value in cursor: print("Row:", int_col, "string of length", value and len(value) or 0) python-cx_Oracle-8.3.0/samples/return_numbers_as_decimals.py000066400000000000000000000025251414105416400243600ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # return_numbers_as_decimals.py # Returns all numbers as decimals by means of an output type handler. This is # needed if the full decimal precision of Oracle numbers is required by the # application. See this article # (http://blog.reverberate.org/2016/02/06/floating-point-demystified-part2.html) # for an explanation of why decimal numbers (like Oracle numbers) cannot be # represented exactly by floating point numbers. # # This script requires cx_Oracle 5.0 and higher. #------------------------------------------------------------------------------ import decimal import cx_Oracle as oracledb import sample_env def output_type_handler(cursor, name, default_type, size, precision, scale): if default_type == oracledb.NUMBER: return cursor.var(decimal.Decimal, arraysize=cursor.arraysize) connection = oracledb.connect(sample_env.get_main_connect_string()) connection.outputtypehandler = output_type_handler cursor = connection.cursor() cursor.execute("select * from TestNumbers") for row in cursor: print("Row:", row) python-cx_Oracle-8.3.0/samples/rows_as_instance.py000066400000000000000000000037421414105416400223250ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # rows_as_instance.py # Returns rows as instances instead of tuples. See the ceDatabase.Row class # in the cx_PyGenLib project (http://cx-pygenlib.sourceforge.net) for a more # advanced example. # # This script requires cx_Oracle 4.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env class Test: def __init__(self, a, b, c): self.a = a self.b = b self.c = c connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # change this to False if you want to create the table yourself using SQL*Plus # and then populate it with the data of your choice if True: cursor.execute(""" select count(*) from user_tables where table_name = 'TESTINSTANCES'""") count, = cursor.fetchone() if count: cursor.execute("drop table TestInstances") cursor.execute(""" create table TestInstances ( a varchar2(60) not null, b number(9) not null, c date not null )""") cursor.execute("insert into TestInstances values ('First', 5, sysdate)") cursor.execute("insert into TestInstances values ('Second', 25, sysdate)") connection.commit() # retrieve the data and display it cursor.execute("select * from TestInstances") cursor.rowfactory = Test print("Rows:") for row in cursor: print("a = %s, b = %s, c = %s" % (row.a, row.b, row.c)) python-cx_Oracle-8.3.0/samples/sample_env.py000066400000000000000000000140651414105416400211150ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Sets the environment used by the sample scripts. Production applications # should consider using External Authentication to avoid hard coded # credentials. # # You can set values in environment variables to bypass the sample requesting # the information it requires. # # CX_ORACLE_SAMPLES_MAIN_USER: user used for most samples # CX_ORACLE_SAMPLES_MAIN_PASSWORD: password of user used for most samples # CX_ORACLE_SAMPLES_EDITION_USER: user for editioning # CX_ORACLE_SAMPLES_EDITION_PASSWORD: password of user for editioning # CX_ORACLE_SAMPLES_EDITION_NAME: name of edition for editioning # CX_ORACLE_SAMPLES_CONNECT_STRING: connect string # CX_ORACLE_SAMPLES_DRCP_CONNECT_STRING: DRCP connect string # CX_ORACLE_SAMPLES_ADMIN_USER: admin user for setting up samples # CX_ORACLE_SAMPLES_ADMIN_PASSWORD: admin password for setting up samples # # CX_ORACLE_SAMPLES_CONNECT_STRING can be set to an Easy Connect string, or a # Net Service Name from a tnsnames.ora file or external naming service, # or it can be the name of a local Oracle database instance. # # If using Instant Client, then an Easy Connect string is generally # appropriate. The syntax is: # # [//]host_name[:port][/service_name][:server_type][/instance_name] # # Commonly just the host_name and service_name are needed # e.g. "localhost/orclpdb1" or "localhost/XEPDB1" # # If using a tnsnames.ora file, the file can be in a default # location such as $ORACLE_HOME/network/admin/tnsnames.ora or # /etc/tnsnames.ora. Alternatively set the TNS_ADMIN environment # variable and put the file in $TNS_ADMIN/tnsnames.ora. # # The administrative user for cloud databases is ADMIN and the administrative # user for on premises databases is SYSTEM. #------------------------------------------------------------------------------ import getpass import os import sys # default values DEFAULT_MAIN_USER = "pythondemo" DEFAULT_EDITION_USER = "pythoneditions" DEFAULT_EDITION_NAME = "python_e1" DEFAULT_CONNECT_STRING = "localhost/orclpdb1" DEFAULT_DRCP_CONNECT_STRING = "localhost/orclpdb1:pooled" # dictionary containing all parameters; these are acquired as needed by the # methods below (which should be used instead of consulting this dictionary # directly) and then stored so that a value is not requested more than once PARAMETERS = {} def get_value(name, label, default_value=""): value = PARAMETERS.get(name) if value is not None: return value env_name = "CX_ORACLE_SAMPLES_" + name value = os.environ.get(env_name) if value is None: if default_value: label += " [%s]" % default_value label += ": " if default_value: value = input(label).strip() else: value = getpass.getpass(label) if not value: value = default_value PARAMETERS[name] = value return value def get_main_user(): return get_value("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER) def get_main_password(): return get_value("MAIN_PASSWORD", "Password for %s" % get_main_user()) def get_edition_user(): return get_value("EDITION_USER", "Edition User Name", DEFAULT_EDITION_USER) def get_edition_password(): return get_value("EDITION_PASSWORD", "Password for %s" % get_edition_user()) def get_edition_name(): return get_value("EDITION_NAME", "Edition Name", DEFAULT_EDITION_NAME) def get_connect_string(): return get_value("CONNECT_STRING", "Connect String", DEFAULT_CONNECT_STRING) def get_main_connect_string(password=None): if password is None: password = get_main_password() return "%s/%s@%s" % (get_main_user(), password, get_connect_string()) def get_drcp_connect_string(): connect_string = get_value("DRCP_CONNECT_STRING", "DRCP Connect String", DEFAULT_DRCP_CONNECT_STRING) return "%s/%s@%s" % (get_main_user(), get_main_password(), connect_string) def get_edition_connect_string(): return "%s/%s@%s" % \ (get_edition_user(), get_edition_password(), get_connect_string()) def get_admin_connect_string(): admin_user = get_value("ADMIN_USER", "Administrative user", "admin") admin_password = get_value("ADMIN_PASSWORD", "Password for %s" % admin_user) return "%s/%s@%s" % (admin_user, admin_password, get_connect_string()) def run_sql_script(conn, script_name, **kwargs): statement_parts = [] cursor = conn.cursor() replace_values = [("&" + k + ".", v) for k, v in kwargs.items()] + \ [("&" + k, v) for k, v in kwargs.items()] script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) file_name = os.path.join(script_dir, "sql", script_name + "_exec.sql") for line in open(file_name): if line.strip() == "/": statement = "".join(statement_parts).strip() if statement: for search_value, replace_value in replace_values: statement = statement.replace(search_value, replace_value) try: cursor.execute(statement) except: print("Failed to execute SQL:", statement) raise statement_parts = [] else: statement_parts.append(line) cursor.execute(""" select name, type, line, position, text from dba_errors where owner = upper(:owner) order by name, type, line, position""", owner = get_main_user()) prev_name = prev_obj_type = None for name, obj_type, line_num, position, text in cursor: if name != prev_name or obj_type != prev_obj_type: print("%s (%s)" % (name, obj_type)) prev_name = name prev_obj_type = obj_type print(" %s/%s %s" % (line_num, position, text)) python-cx_Oracle-8.3.0/samples/scrollable_cursors.py000066400000000000000000000042571414105416400226700ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # scrollable_cursors.py # This script demonstrates how to use scrollable cursors. These allow moving # forward and backward in the result set but incur additional overhead on the # server to retain this information. # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) # show all of the rows available in the table cursor = connection.cursor() cursor.execute("select * from TestStrings order by IntCol") print("ALL ROWS") for row in cursor: print(row) print() # create a scrollable cursor cursor = connection.cursor(scrollable = True) # set array size smaller than the default (100) to force scrolling by the # database; otherwise, scrolling occurs directly within the buffers cursor.arraysize = 3 cursor.execute("select * from TestStrings order by IntCol") # scroll to last row in the result set; the first parameter is not needed and # is ignored) cursor.scroll(mode = "last") print("LAST ROW") print(cursor.fetchone()) print() # scroll to the first row in the result set; the first parameter not needed and # is ignored cursor.scroll(mode = "first") print("FIRST ROW") print(cursor.fetchone()) print() # scroll to an absolute row number cursor.scroll(5, mode = "absolute") print("ROW 5") print(cursor.fetchone()) print() # scroll forward six rows (the mode parameter defaults to relative) cursor.scroll(3) print("SKIP 3 ROWS") print(cursor.fetchone()) print() # scroll backward four rows (the mode parameter defaults to relative) cursor.scroll(-4) print("SKIP BACK 4 ROWS") print(cursor.fetchone()) print() python-cx_Oracle-8.3.0/samples/session_callback.py000066400000000000000000000136241414105416400222630ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # session_callback.py # # Demonstrate how to use a connection pool session callback written in # Python. The callback is invoked whenever a newly created session is acquired # from the pool, or when the requested tag does not match the tag that is # associated with the session. It is generally used to set session state, so # that the application can count on known session state, which allows the # application to reduce the number of round trips made to the database. # If all your connections should have the same session state, you can simplify # the session callback by removing the tagging logic. # # This script requires cx_Oracle 7.1 or higher. # # Also see session_callback_plsql.py # #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env # define a dictionary of NLS_DATE_FORMAT formats supported by this sample SUPPORTED_FORMATS = { "SIMPLE" : "'YYYY-MM-DD HH24:MI'", "FULL" : "'YYYY-MM-DD HH24:MI:SS'" } # define a dictionary of TIME_ZONE values supported by this sample SUPPORTED_TIME_ZONES = { "UTC" : "'UTC'", "MST" : "'-07:00'" } # define a dictionary of keys that are supported by this sample SUPPORTED_KEYS = { "NLS_DATE_FORMAT" : SUPPORTED_FORMATS, "TIME_ZONE" : SUPPORTED_TIME_ZONES } # define session callback def init_session(conn, requested_tag): # display the requested and actual tags print("init_session(): requested tag=%r, actual tag=%r" % \ (requested_tag, conn.tag)) # tags are expected to be in the form "key1=value1;key2=value2" # in this example, they are used to set NLS parameters and the tag is # parsed to validate it if requested_tag is not None: state_parts = [] for directive in requested_tag.split(";"): parts = directive.split("=") if len(parts) != 2: raise ValueError("Tag must contain key=value pairs") key, value = parts value_dict = SUPPORTED_KEYS.get(key) if value_dict is None: raise ValueError("Tag only supports keys: %s" % \ (", ".join(SUPPORTED_KEYS))) actual_value = value_dict.get(value) if actual_value is None: raise ValueError("Key %s only supports values: %s" % \ (key, ", ".join(value_dict))) state_parts.append("%s = %s" % (key, actual_value)) sql = "alter session set %s" % " ".join(state_parts) cursor = conn.cursor() cursor.execute(sql) # assign the requested tag to the connection so that when the connection # is closed, it will automatically be retagged; note that if the requested # tag is None (no tag was requested) this has no effect conn.tag = requested_tag # create pool with session callback defined pool = oracledb.SessionPool(user=sample_env.get_main_user(), password=sample_env.get_main_password(), dsn=sample_env.get_connect_string(), min=2, max=5, increment=1, session_callback=init_session) # acquire session without specifying a tag; since the session returned is # newly created, the callback will be invoked but since there is no tag # specified, no session state will be changed print("(1) acquire session without tag") with pool.acquire() as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying a tag; since the session returned has no tag, # the callback will be invoked; session state will be changed and the tag will # be saved when the connection is closed print("(2) acquire session with tag") with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying the same tag; since a session exists in the pool # with this tag, it will be returned and the callback will not be invoked but # the connection will still have the session state defined previously print("(3) acquire session with same tag") with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying a different tag; since no session exists in the # pool with this tag, a new session will be returned and the callback will be # invoked; session state will be changed and the tag will be saved when the # connection is closed print("(4) acquire session with different tag") with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC") as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying a different tag but also specifying that a # session with any tag can be acquired from the pool; a session with one of the # previously set tags will be returned and the callback will be invoked; # session state will be changed and the tag will be saved when the connection # is closed print("(4) acquire session with different tag but match any also specified") with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True) \ as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) python-cx_Oracle-8.3.0/samples/session_callback_plsql.py000066400000000000000000000110511414105416400234660ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # session_callback_plsql.py # # Demonstrate how to use a connection pool session callback written in # PL/SQL. The callback is invoked whenever the tag requested by the application # does not match the tag associated with the session in the pool. It should be # used to set session state, so that the application can count on known session # state, which allows the application to reduce the number of round trips to the # database. # # The primary advantage to this approach over the equivalent approach shown in # session_callback.py is when DRCP is used, as the callback is invoked on the # server and no round trip is required to set state. # # This script requires cx_Oracle 7.1 or higher. # # Also see session_callback.py # #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env # create pool with session callback defined pool = oracledb.SessionPool(user=sample_env.get_main_user(), password=sample_env.get_main_password(), dsn=sample_env.get_connect_string(), min=2, max=5, increment=1, session_callback="pkg_SessionCallback.TheCallback") # truncate table logging calls to PL/SQL session callback with pool.acquire() as conn: cursor = conn.cursor() cursor.execute("truncate table PLSQLSessionCallbacks") # acquire session without specifying a tag; the callback will not be invoked as # a result and no session state will be changed print("(1) acquire session without tag") with pool.acquire() as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying a tag; since the session returned has no tag, # the callback will be invoked; session state will be changed and the tag will # be saved when the connection is closed print("(2) acquire session with tag") with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying the same tag; since a session exists in the pool # with this tag, it will be returned and the callback will not be invoked but # the connection will still have the session state defined previously print("(3) acquire session with same tag") with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying a different tag; since no session exists in the # pool with this tag, a new session will be returned and the callback will be # invoked; session state will be changed and the tag will be saved when the # connection is closed print("(4) acquire session with different tag") with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC") as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session, specifying a different tag but also specifying that a # session with any tag can be acquired from the pool; a session with one of the # previously set tags will be returned and the callback will be invoked; # session state will be changed and the tag will be saved when the connection # is closed print("(4) acquire session with different tag but match any also specified") with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True) \ as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() print("main(): result is", repr(result)) # acquire session and display results from PL/SQL session logs with pool.acquire() as conn: cursor = conn.cursor() cursor.execute(""" select RequestedTag, ActualTag from PLSQLSessionCallbacks order by FixupTimestamp""") print("(5) PL/SQL session callbacks") for requestedTag, actualTag in cursor: print("Requested:", requestedTag, "Actual:", actualTag) python-cx_Oracle-8.3.0/samples/setup_samples.py000066400000000000000000000025541414105416400216500ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # setup_samples.py # # Creates users and populates their schemas with the tables and packages # necessary for running the sample scripts. An edition is also created for the # demonstration of PL/SQL editioning. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env import drop_samples # connect as administrative user (usually SYSTEM or ADMIN) conn = oracledb.connect(sample_env.get_admin_connect_string()) # drop existing users and editions, if applicable drop_samples.drop_samples(conn) # create sample schema and edition print("Creating sample schemas and edition...") sample_env.run_sql_script(conn, "setup_samples", main_user=sample_env.get_main_user(), main_password=sample_env.get_main_password(), edition_user=sample_env.get_edition_user(), edition_password=sample_env.get_edition_password(), edition_name=sample_env.get_edition_name()) print("Done.") python-cx_Oracle-8.3.0/samples/sharding_number_key.py000066400000000000000000000031651414105416400230020ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # sharding_number_key.py # This script demonstrates how to use sharding keys with a sharded database. # The sample schema provided does not include support for running this demo. A # sharded database must first be created. Information on how to create a # sharded database can be found in the documentation: # https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=SHARD # # This script requires cx_Oracle 6.1 and higher but it is recommended to use # cx_Oracle 7.3 and higher in order to avoid a set of known issues when using # sharding capabilities. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env pool = oracledb.SessionPool(user=sample_env.get_main_user(), password=sample_env.get_main_password(), dsn=sample_env.get_connect_string(), min=1, max=5, increment=1) def connect_and_display(sharding_key): print("Connecting with sharding key:", sharding_key) with pool.acquire(shardingkey=[sharding_key]) as conn: cursor = conn.cursor() cursor.execute("select sys_context('userenv', 'db_name') from dual") name, = cursor.fetchone() print("--> connected to database", name) connect_and_display(100) connect_and_display(167) python-cx_Oracle-8.3.0/samples/soda_basic.py000066400000000000000000000072451414105416400210550ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # soda_basic.py # A basic Simple Oracle Document Access (SODA) example. # # This script requires cx_Oracle 7.0 and higher. # Oracle Client must be at 18.3 or higher. # Oracle Database must be at 18.1 or higher. # The user must have been granted the SODA_APP privilege. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) # The general recommendation for simple SODA usage is to enable autocommit connection.autocommit = True # Create the parent object for SODA soda = connection.getSodaDatabase() # drop the collection if it already exists in order to ensure that the sample # runs smoothly each time collection = soda.openCollection("mycollection") if collection is not None: collection.drop() # Explicit metadata is used for maximum version portability. # Refer to the documentation. metadata = { "keyColumn": { "name": "ID" }, "contentColumn": { "name": "JSON_DOCUMENT", "sqlType": "BLOB" }, "versionColumn": { "name": "VERSION", "method": "UUID" }, "lastModifiedColumn": { "name": "LAST_MODIFIED" }, "creationTimeColumn": { "name": "CREATED_ON" } } # Create a new SODA collection and index # This will open an existing collection, if the name is already in use. collection = soda.createCollection("mycollection", metadata) index_spec = { 'name': 'CITY_IDX', 'fields': [ { 'path': 'address.city', 'datatype': 'string', 'order': 'asc' } ] } collection.createIndex(index_spec) # Insert a document. # A system generated key is created by default. content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}} doc = collection.insertOneAndGet(content) key = doc.key print('The key of the new SODA document is: ', key) # Fetch the document back doc = collection.find().key(key).getOne() # A SodaDocument content = doc.getContent() # A JavaScript object print('Retrieved SODA document dictionary is:') print(content) content = doc.getContentAsString() # A JSON string print('Retrieved SODA document string is:') print(content) # Replace document contents content = {'name': 'Matilda', 'address': {'city': 'Sydney'}} collection.find().key(key).replaceOne(content) # Insert some more documents without caring about their keys content = {'name': 'Venkat', 'address': {'city': 'Bengaluru'}} collection.insertOne(content) content = {'name': 'May', 'address': {'city': 'London'}} collection.insertOne(content) content = {'name': 'Sally-Ann', 'address': {'city': 'San Francisco'}} collection.insertOne(content) # Find all documents with names like 'Ma%' print("Names matching 'Ma%'") documents = collection.find().filter({'name': {'$like': 'Ma%'}}).getDocuments() for d in documents: content = d.getContent() print(content["name"]) # Count all documents c = collection.find().count() print('Collection has', c, 'documents') # Remove documents with cities containing 'o' print('Removing documents') c = collection.find().filter({'address.city': {'$regex': '.*o.*'}}).remove() print('Dropped', c, 'documents') # Count all documents c = collection.find().count() print('Collection has', c, 'documents') # Drop the collection if collection.drop(): print('Collection was dropped') python-cx_Oracle-8.3.0/samples/soda_bulk_insert.py000066400000000000000000000046201414105416400223070ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # soda_bulk_insert.py # Demonstrates the use of SODA bulk insert. # # This script requires cx_Oracle 7.2 and higher. # Oracle Client must be at 18.5 or higher. # Oracle Database must be at 18.1 or higher. # The user must have been granted the SODA_APP privilege. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env connection = oracledb.connect(sample_env.get_main_connect_string()) # the general recommendation for simple SODA usage is to enable autocommit connection.autocommit = True # create the parent object for all SODA work soda = connection.getSodaDatabase() # drop the collection if it already exists in order to ensure that the sample # runs smoothly each time collection = soda.openCollection("SodaBulkInsert") if collection is not None: collection.drop() # Explicit metadata is used for maximum version portability. # Refer to the documentation. metadata = { "keyColumn": { "name": "ID" }, "contentColumn": { "name": "JSON_DOCUMENT", "sqlType": "BLOB" }, "versionColumn": { "name": "VERSION", "method": "UUID" }, "lastModifiedColumn": { "name": "LAST_MODIFIED" }, "creationTimeColumn": { "name": "CREATED_ON" } } # create a new (or open an existing) SODA collection collection = soda.createCollection("SodaBulkInsert", metadata) # remove all documents from the collection collection.find().remove() # define some documents that will be stored in_docs = [ dict(name="Sam", age=8), dict(name="George", age=46), dict(name="Bill", age=35), dict(name="Sally", age=43), dict(name="Jill", age=28), dict(name="Cynthia", age=12) ] # perform bulk insert result_docs = collection.insertManyAndGet(in_docs) for doc in result_docs: print("Inserted SODA document with key", doc.key) print() # perform search of all persons under the age of 40 print("Persons under the age of 40:") for doc in collection.find().filter({'age': {'$lt': 40}}).getDocuments(): print(doc.getContent()["name"] + ",", "key", doc.key) python-cx_Oracle-8.3.0/samples/spatial_to_geopandas.py000066400000000000000000000235531414105416400231460ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # spatial_to_geopandas.py # GeoPandas is a popular python library for working with geospatial data. # GeoPandas extends the Pandas data analysis library with geospatial support # using the Shapely library for geometry object support. # # See http://geopandas.org, https://pandas.pydata.org, # and https://github.com/Toblerity/Shapely. # # This example shows how to bring geometries from Oracle Spatial (SDO_GEOMETRY # data type) into GeoPandas and perform a simple spatial operation. While the # spatial operation we perform in Python could have been performed in the # Oracle database, this example targets use cases where Python with GeoPandas # is being used to combine and work with geospatial data from numerous # additional sources such as files and web services. # # This script requires cx_Oracle (5.3 and higher) as well as GeoPandas and its # dependencies (see http://geopandas.org/install.html). #------------------------------------------------------------------------------ from shapely.wkb import loads import geopandas as gpd import cx_Oracle as oracledb import sample_env # create Oracle connection and cursor objects connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() # enable autocommit to avoid the additional round trip to the database to # perform a commit; this should not be used if multiple statements must be # executed for a single transaction connection.autocommit = True # define output type handler to fetch LOBs, avoiding the second round trip to # the database to read the LOB contents def output_type_handler(cursor, name, default_type, size, precision, scale): if default_type == oracledb.BLOB: return cursor.var(oracledb.LONG_BINARY, arraysize=cursor.arraysize) connection.outputtypehandler = output_type_handler # drop and create table print("Dropping and creating table...") cursor.execute(""" begin execute immediate 'drop table TestStates'; exception when others then if sqlcode <> -942 then raise; end if; end;""") cursor.execute(""" create table TestStates ( state VARCHAR2(30) not null, geometry SDO_GEOMETRY not null )""") # acquire types used for creating SDO_GEOMETRY objects type_obj = connection.gettype("MDSYS.SDO_GEOMETRY") element_info_type_obj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") ordinate_type_obj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY") # define function for creating an SDO_GEOMETRY object def create_geometry_obj(*ordinates): geometry = type_obj.newobject() geometry.SDO_GTYPE = 2003 geometry.SDO_SRID = 8307 geometry.SDO_ELEM_INFO = element_info_type_obj.newobject() geometry.SDO_ELEM_INFO.extend([1, 1003, 1]) geometry.SDO_ORDINATES = ordinate_type_obj.newobject() geometry.SDO_ORDINATES.extend(ordinates) return geometry # create SDO_GEOMETRY objects for three adjacent states in the USA geometry_nevada = create_geometry_obj(-114.052025, 37.103989, -114.049797, 37.000423, -113.484375, 37, -112.898598, 37.000401,-112.539604, 37.000683, -112, 37.000977, -111.412048, 37.001514, -111.133018, 37.00079,-110.75, 37.003201, -110.5, 37.004265, -110.469505, 36.998001, -110, 36.997967, -109.044571,36.999088, -109.045143, 37.375, -109.042824, 37.484692, -109.040848, 37.881176, -109.041405,38.153027, -109.041107, 38.1647, -109.059402, 38.275501, -109.059296, 38.5, -109.058868, 38.719906,-109.051765, 39, -109.050095, 39.366699, -109.050697, 39.4977, -109.050499, 39.6605, -109.050156,40.222694, -109.047577, 40.653641, -109.0494, 41.000702, -109.2313, 41.002102, -109.534233,40.998184, -110, 40.997398, -110.047768, 40.997696, -110.5, 40.994801, -111.045982, 40.998013,-111.045815, 41.251774, -111.045097, 41.579899, -111.045944, 42.001633, -111.506493, 41.999588,-112.108742, 41.997677, -112.16317, 41.996784, -112.172562, 41.996643, -112.192184, 42.001244,-113, 41.998314, -113.875, 41.988091, -114.040871, 41.993805, -114.038803, 41.884899, -114.041306,41, -114.04586, 40.116997, -114.046295, 39.906101, -114.046898, 39.542801, -114.049026, 38.67741, -114.049339, 38.572968, -114.049095, 38.14864, -114.0476, 37.80946,-114.05098, 37.746284, -114.051666, 37.604805, -114.052025, 37.103989) geometry_wyoming = create_geometry_obj(-111.045815, 41.251774, -111.045982, 40.998013, -110.5, 40.994801, -110.047768, 40.997696, -110, 40.997398, -109.534233, 40.998184, -109.2313, 41.002102, -109.0494, 41.000702, -108.525368, 40.999634, -107.917793, 41.002071, -107.317177, 41.002956, -106.857178, 41.002697, -106.455704, 41.002167, -106.320587, 40.999153, -106.189987, 40.997604, -105.729874, 40.996906, -105.276604, 40.998188, -104.942848, 40.998226, -104.625, 41.00145, -104.052742, 41.001423, -104.051781, 41.39333, -104.052032, 41.564301, -104.052185, 41.697983, -104.052109, 42.001736, -104.052277, 42.611626, -104.052643, 43.000614, -104.054337, 43.47784, -104.054298, 43.503101, -104.055, 43.8535, -104.054108, 44.141102, -104.054001, 44.180401, -104.055458, 44.570877, -104.057205, 44.997444, -104.664658, 44.998631, -105.037872, 45.000359, -105.088867, 45.000462, -105.912819, 45.000957, -105.927612, 44.99366, -106.024239, 44.993591, -106.263, 44.993801, -107.054871, 44.996384, -107.133545, 45.000141, -107.911095, 45.001343, -108.248672, 44.999504, -108.620628, 45.000328, -109.082314, 44.999664, -109.102745, 45.005955, -109.797951, 45.002247, -110.000771, 45.003502, -110.10936, 45.003967, -110.198761, 44.99625, -110.286026, 44.99691, -110.361946, 45.000656, -110.402176, 44.993874, -110.5, 44.992355, -110.704506, 44.99239, -110.784241, 45.003021, -111.05442, 45.001392, -111.054558, 44.666336, -111.048203, 44.474144, -111.046272, 43.983456, -111.044724, 43.501213, -111.043846, 43.3158, -111.043381, 43.02013, -111.042786, 42.719578, -111.045967, 42.513187, -111.045944, 42.001633, -111.045097, 41.579899, -111.045815, 41.251774) geometry_colorado = create_geometry_obj(-109.045143, 37.375, -109.044571, 36.999088, -108.378571, 36.999516, -107.481133, 37, -107.420311, 37, -106.876701, 37.00013, -106.869209, 36.992416, -106.475639, 36.993748, -106.006058, 36.995327, -105.717834, 36.995823, -105.220055, 36.995144, -105.154488, 36.995239, -105.028671, 36.992702, -104.407616, 36.993446, -104.007324, 36.996216, -103.085617, 37.000244, -103.001709, 37.000084, -102.986488, 36.998505, -102.759384, 37, -102.69767, 36.995132, -102.041794, 36.993061, -102.041191, 37.389172, -102.04113, 37.644268, -102.041695, 37.738529, -102.043938, 38.262466, -102.044113, 38.268803, -102.04483, 38.615234, -102.044762, 38.697556, -102.046112, 39.047035, -102.046707, 39.133144, -102.049301, 39.568176, -102.049347, 39.574062, -102.051277, 40.00309, -102.051117, 40.34922, -102.051003, 40.440018, -102.050873, 40.697556, -102.050835, 40.749596, -102.051155, 41.002384, -102.620567, 41.002609, -102.652992, 41.002342, -103.382011, 41.00227, -103.574036, 41.001736, -104.052742, 41.001423, -104.625, 41.00145, -104.942848, 40.998226, -105.276604, 40.998188, -105.729874, 40.996906, -106.189987, 40.997604, -106.320587, 40.999153, -106.455704, 41.002167, -106.857178, 41.002697, -107.317177, 41.002956, -107.917793, 41.002071, -108.525368, 40.999634, -109.0494, 41.000702, -109.047577, 40.653641, -109.050156, 40.222694, -109.050499, 39.6605, -109.050697, 39.4977, -109.050095, 39.366699, -109.051765, 39, -109.058868, 38.719906, -109.059296, 38.5, -109.059402, 38.275501, -109.041107, 38.1647, -109.041405, 38.153027, -109.040848, 37.881176, -109.042824, 37.484692, -109.045143, 37.375) # Insert rows for test states. If we were analyzing these geometries in Oracle # we would also add Spatial metadata and indexes. However in this example we # are only storing the geometries so that we load them back into Python, so we # will skip the metadata and indexes. print("Adding rows to table...") data = [ ('Nevada', geometry_nevada), ('Colorado', geometry_colorado), ('Wyoming', geometry_wyoming) ] cursor.executemany('insert into TestStates values (:state, :obj)', data) # We now have test geometries in Oracle Spatial (SDO_GEOMETRY) and will next # bring them back into Python to analyze with GeoPandas. GeoPandas is able to # consume geometries in the Well Known Text (WKT) and Well Known Binary (WKB) # formats. Oracle database includes utility functions to return SDO_GEOMETRY as # both WKT and WKB. Therefore we use that utility function in the query below # to provide results in a format readily consumable by GeoPandas. These utility # functions were introduced in Oracle 10g. We use WKB here; however the same # process applies for WKT. cursor.execute(""" SELECT state, sdo_util.to_wkbgeometry(geometry) FROM TestStates""") gdf = gpd.GeoDataFrame(cursor.fetchall(), columns=['state', 'wkbgeometry']) # create GeoSeries to replace the WKB geometry column gdf['geometry'] = gpd.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x))) del gdf['wkbgeometry'] # display the GeoDataFrame print() print(gdf) # perform a basic GeoPandas operation (unary_union) # to combine the 3 adjacent states into 1 geometry print() print("GeoPandas combining the 3 geometries into a single geometry...") print(gdf.unary_union) python-cx_Oracle-8.3.0/samples/sql/000077500000000000000000000000001414105416400172035ustar00rootroot00000000000000python-cx_Oracle-8.3.0/samples/sql/drop_samples.sql000066400000000000000000000020021414105416400224060ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * drop_samples.sql * Drops database objects used for cx_Oracle samples. * * Run this like: * sqlplus sys/syspassword@hostname/servicename as sysdba @drop_samples *---------------------------------------------------------------------------*/ whenever sqlerror exit failure -- get parameters set echo off termout on feedback off verify off accept main_user char default pythondemo - prompt "Name of main schema [pythondemo]: " accept edition_user char default pythoneditions - prompt "Name of edition schema [pythoneditions]: " accept edition_name char default python_e1 - prompt "Name of edition [python_e1]: " set feedback on -- perform work @@drop_samples_exec.sql exit python-cx_Oracle-8.3.0/samples/sql/drop_samples_exec.sql000066400000000000000000000023021414105416400234150ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * drop_samples_exec.sql * This script performs the actual work of dropping the database schemas and * edition used by the cx_Oracle samples. It is called by the drop_samples.sql * and setup_samples.sql files after acquiring the necessary parameters and * also by the Python script drop_samples.py. *---------------------------------------------------------------------------*/ begin for r in ( select username from dba_users where username in (upper('&main_user'), upper('&edition_user')) ) loop execute immediate 'drop user ' || r.username || ' cascade'; end loop; for r in ( select edition_name from dba_editions where edition_name in (upper('&edition_name')) ) loop execute immediate 'drop edition ' || r.edition_name || ' cascade'; end loop; end; / python-cx_Oracle-8.3.0/samples/sql/setup_samples.sql000066400000000000000000000024301414105416400226070ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * setup_samples.sql * Creates and populates schemas with the database objects used by the * cx_Oracle samples. An edition is also created for the demonstration of * PL/SQL editioning. * * Run this like: * sqlplus sys/syspassword@hostname/servicename as sysdba @setup_samples *---------------------------------------------------------------------------*/ whenever sqlerror exit failure -- get parameters set echo off termout on feedback off verify off accept main_user char default pythondemo - prompt "Name of main schema [pythondemo]: " accept main_password char prompt "Password for &main_user: " HIDE accept edition_user char default pythoneditions - prompt "Name of edition schema [pythoneditions]: " accept edition_password char prompt "Password for &edition_user: " HIDE accept edition_name char default python_e1 - prompt "Name of edition [python_e1]: " set feedback on -- perform work @@drop_samples_exec.sql @@setup_samples_exec.sql exit python-cx_Oracle-8.3.0/samples/sql/setup_samples_exec.sql000066400000000000000000000376361414105416400236330ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright 2017, 2020, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * setup_samples_exec.sql * This script performs the actual work of creating and populating the * schemas with the database objects used by the sample scripts. An edition * is also created for the demonstration of PL/SQL editioning. It is called by * the setup_samples.sql file after acquiring the necessary parameters and also * by the Python script setup_samples.py. *---------------------------------------------------------------------------*/ alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS' / alter session set nls_numeric_characters='.,' / create user &main_user identified by &main_password / grant create session, create table, create procedure, create type, select any dictionary, change notification, unlimited tablespace to &main_user / grant aq_administrator_role to &main_user / begin execute immediate 'begin dbms_session.sleep(0); end;'; exception when others then begin execute immediate 'grant execute on dbms_lock to &main_user'; exception when others then raise_application_error(-20000, 'Ensure the following grant is made: ' || 'grant execute on dbms_lock to ' || user || ' with grant option'); end; end; / begin for r in ( select role from dba_roles where role in ('SODA_APP') ) loop execute immediate 'grant ' || r.role || ' to &main_user'; end loop; end; / create user &edition_user identified by &edition_password / grant create session, create procedure to &edition_user / alter user &edition_user enable editions / create edition &edition_name / grant use on edition &edition_name to &edition_user / -- create types create type &main_user..udt_SubObject as object ( SubNumberValue number, SubStringValue varchar2(60) ); / create or replace type &main_user..udt_Building as object ( BuildingId number(9), NumFloors number(3), Description varchar2(60), DateBuilt date ); / create or replace type &main_user..udt_Book as object ( Title varchar2(100), Authors varchar2(100), Price number(5,2) ); / -- create tables create table &main_user..TestNumbers ( IntCol number(9) not null, NumberCol number(9, 2) not null, FloatCol float not null, UnconstrainedCol number not null, NullableCol number(38) ) / create table &main_user..TestStrings ( IntCol number(9) not null, StringCol varchar2(20) not null, RawCol raw(30) not null, FixedCharCol char(40) not null, NullableCol varchar2(50) ) / create table &main_user..TestCLOBs ( IntCol number(9) not null, CLOBCol clob not null ) / create table &main_user..TestBLOBs ( IntCol number(9) not null, BLOBCol blob not null ) / create table &main_user..TestTempTable ( IntCol number(9) not null, StringCol varchar2(400), constraint TestTempTable_pk primary key (IntCol) ) / create table &main_user..TestUniversalRowids ( IntCol number(9) not null, StringCol varchar2(250) not null, DateCol date not null, constraint TestUniversalRowids_pk primary key (IntCol, StringCol, DateCol) ) organization index / create table &main_user..TestBuildings ( BuildingId number(9) not null, BuildingObj &main_user..udt_Building not null ) / create table &main_user..BigTab ( mycol varchar2(20) ) / create table &main_user..SampleQueryTab ( id number not null, name varchar2(20) not null ) / create table &main_user..MyTab ( id number, data varchar2(20) ) / create table &main_user..ParentTable ( ParentId number(9) not null, Description varchar2(60) not null, constraint ParentTable_pk primary key (ParentId) ) / create table &main_user..ChildTable ( ChildId number(9) not null, ParentId number(9) not null, Description varchar2(60) not null, constraint ChildTable_pk primary key (ChildId), constraint ChildTable_fk foreign key (ParentId) references &main_user..ParentTable ) / create table &main_user..Ptab ( myid number, mydata varchar(20) ) / create table &main_user..PlsqlSessionCallbacks ( RequestedTag varchar2(250), ActualTag varchar2(250), FixupTimestamp timestamp ) / -- create queue table, queues and subscribers for demonstrating Advanced Queuing begin dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE_TAB', '&main_user..UDT_BOOK'); dbms_aqadm.create_queue('&main_user..DEMO_BOOK_QUEUE', '&main_user..BOOK_QUEUE_TAB'); dbms_aqadm.start_queue('&main_user..DEMO_BOOK_QUEUE'); dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_TAB', 'RAW'); dbms_aqadm.create_queue('&main_user..DEMO_RAW_QUEUE', '&main_user..RAW_QUEUE_TAB'); dbms_aqadm.start_queue('&main_user..DEMO_RAW_QUEUE'); dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_MULTI_TAB', 'RAW', multiple_consumers => true); dbms_aqadm.create_queue('&main_user..DEMO_RAW_QUEUE_MULTI', '&main_user..RAW_QUEUE_MULTI_TAB'); dbms_aqadm.start_queue('&main_user..DEMO_RAW_QUEUE_MULTI'); dbms_aqadm.add_subscriber('&main_user..DEMO_RAW_QUEUE_MULTI', sys.aq$_agent('SUBSCRIBER_A', null, null)); dbms_aqadm.add_subscriber('&main_user..DEMO_RAW_QUEUE_MULTI', sys.aq$_agent('SUBSCRIBER_B', null, null)); end; / -- populate tables begin for i in 1..20000 loop insert into &main_user..BigTab (mycol) values (dbms_random.string('A', 20)); end loop; end; / begin for i in 1..10 loop insert into &main_user..TestNumbers values (i, i + i * 0.25, i + i * .75, i * i * i + i *.5, decode(mod(i, 2), 0, null, power(143, i))); end loop; end; / declare t_RawValue raw(30); function ConvertHexDigit(a_Value number) return varchar2 is begin if a_Value between 0 and 9 then return to_char(a_Value); end if; return chr(ascii('A') + a_Value - 10); end; function ConvertToHex(a_Value varchar2) return varchar2 is t_HexValue varchar2(60); t_Digit number; begin for i in 1..length(a_Value) loop t_Digit := ascii(substr(a_Value, i, 1)); t_HexValue := t_HexValue || ConvertHexDigit(trunc(t_Digit / 16)) || ConvertHexDigit(mod(t_Digit, 16)); end loop; return t_HexValue; end; begin for i in 1..10 loop t_RawValue := hextoraw(ConvertToHex('Raw ' || to_char(i))); insert into &main_user..TestStrings values (i, 'String ' || to_char(i), t_RawValue, 'Fixed Char ' || to_char(i), decode(mod(i, 2), 0, null, 'Nullable ' || to_char(i))); end loop; end; / insert into &main_user..ParentTable values (10, 'Parent 10') / insert into &main_user..ParentTable values (20, 'Parent 20') / insert into &main_user..ParentTable values (30, 'Parent 30') / insert into &main_user..ParentTable values (40, 'Parent 40') / insert into &main_user..ParentTable values (50, 'Parent 50') / insert into &main_user..ChildTable values (1001, 10, 'Child A of Parent 10') / insert into &main_user..ChildTable values (1002, 20, 'Child A of Parent 20') / insert into &main_user..ChildTable values (1003, 20, 'Child B of Parent 20') / insert into &main_user..ChildTable values (1004, 20, 'Child C of Parent 20') / insert into &main_user..ChildTable values (1005, 30, 'Child A of Parent 30') / insert into &main_user..ChildTable values (1006, 30, 'Child B of Parent 30') / insert into &main_user..ChildTable values (1007, 40, 'Child A of Parent 40') / insert into &main_user..ChildTable values (1008, 40, 'Child B of Parent 40') / insert into &main_user..ChildTable values (1009, 40, 'Child C of Parent 40') / insert into &main_user..ChildTable values (1010, 40, 'Child D of Parent 40') / insert into &main_user..ChildTable values (1011, 40, 'Child E of Parent 40') / insert into &main_user..ChildTable values (1012, 50, 'Child A of Parent 50') / insert into &main_user..ChildTable values (1013, 50, 'Child B of Parent 50') / insert into &main_user..ChildTable values (1014, 50, 'Child C of Parent 50') / insert into &main_user..ChildTable values (1015, 50, 'Child D of Parent 50') / insert into &main_user..SampleQueryTab values (1, 'Anthony') / insert into &main_user..SampleQueryTab values (2, 'Barbie') / insert into &main_user..SampleQueryTab values (3, 'Chris') / insert into &main_user..SampleQueryTab values (4, 'Dazza') / insert into &main_user..SampleQueryTab values (5, 'Erin') / insert into &main_user..SampleQueryTab values (6, 'Frankie') / insert into &main_user..SampleQueryTab values (7, 'Gerri') / commit / -- -- For PL/SQL Examples -- create or replace function &main_user..myfunc ( a_Data varchar2, a_Id number ) return number as begin insert into &main_user..ptab (mydata, myid) values (a_Data, a_Id); return (a_Id * 2); end; / create or replace procedure &main_user..myproc ( a_Value1 number, a_Value2 out number ) as begin a_Value2 := a_Value1 * 2; end; / create or replace procedure &main_user..myrefcursorproc ( a_StartingValue number, a_EndingValue number, a_RefCursor out sys_refcursor ) as begin open a_RefCursor for select * from TestStrings where IntCol between a_StartingValue and a_EndingValue; end; / create procedure &main_user..myrefcursorproc2 ( a_RefCursor out sys_refcursor ) as begin open a_RefCursor for select * from TestTempTable; end; / -- -- Create package for demoing PL/SQL collections and records. -- create or replace package &main_user..pkg_Demo as type udt_StringList is table of varchar2(100) index by binary_integer; type udt_DemoRecord is record ( NumberValue number, StringValue varchar2(30), DateValue date, BooleanValue boolean ); procedure DemoCollectionOut ( a_Value out nocopy udt_StringList ); procedure DemoRecordsInOut ( a_Value in out nocopy udt_DemoRecord ); end; / create or replace package body &main_user..pkg_Demo as procedure DemoCollectionOut ( a_Value out nocopy udt_StringList ) is begin a_Value(-1048576) := 'First element'; a_Value(-576) := 'Second element'; a_Value(284) := 'Third element'; a_Value(8388608) := 'Fourth element'; end; procedure DemoRecordsInOut ( a_Value in out nocopy udt_DemoRecord ) is begin a_Value.NumberValue := a_Value.NumberValue * 2; a_Value.StringValue := a_Value.StringValue || ' (Modified)'; a_Value.DateValue := a_Value.DateValue + 5; a_Value.BooleanValue := not a_Value.BooleanValue; end; end; / -- -- Create package for demoing PL/SQL session callback -- create or replace package &main_user..pkg_SessionCallback as procedure TheCallback ( a_RequestedTag varchar2, a_ActualTag varchar2 ); end; / create or replace package body &main_user..pkg_SessionCallback as type udt_Properties is table of varchar2(64) index by varchar2(64); procedure LogCall ( a_RequestedTag varchar2, a_ActualTag varchar2 ) is pragma autonomous_transaction; begin insert into PlsqlSessionCallbacks values (a_RequestedTag, a_ActualTag, systimestamp); commit; end; procedure ParseProperty ( a_Property varchar2, a_Name out nocopy varchar2, a_Value out nocopy varchar2 ) is t_Pos number; begin t_Pos := instr(a_Property, '='); if t_Pos = 0 then raise_application_error(-20000, 'Tag must contain key=value pairs'); end if; a_Name := substr(a_Property, 1, t_Pos - 1); a_Value := substr(a_Property, t_Pos + 1); end; procedure SetProperty ( a_Name varchar2, a_Value varchar2 ) is t_ValidValues udt_Properties; begin if a_Name = 'TIME_ZONE' then t_ValidValues('UTC') := 'UTC'; t_ValidValues('MST') := '-07:00'; elsif a_Name = 'NLS_DATE_FORMAT' then t_ValidValues('SIMPLE') := 'YYYY-MM-DD HH24:MI'; t_ValidValues('FULL') := 'YYYY-MM-DD HH24:MI:SS'; else raise_application_error(-20000, 'Unsupported session setting'); end if; if not t_ValidValues.exists(a_Value) then raise_application_error(-20000, 'Unsupported session setting'); end if; execute immediate 'ALTER SESSION SET ' || a_Name || '=''' || t_ValidValues(a_Value) || ''''; end; procedure ParseTag ( a_Tag varchar2, a_Properties out nocopy udt_Properties ) is t_PropertyName varchar2(64); t_PropertyValue varchar2(64); t_StartPos number; t_EndPos number; begin t_StartPos := 1; while t_StartPos < length(a_Tag) loop t_EndPos := instr(a_Tag, ';', t_StartPos); if t_EndPos = 0 then t_EndPos := length(a_Tag) + 1; end if; ParseProperty(substr(a_Tag, t_StartPos, t_EndPos - t_StartPos), t_PropertyName, t_PropertyValue); a_Properties(t_PropertyName) := t_PropertyValue; t_StartPos := t_EndPos + 1; end loop; end; procedure TheCallback ( a_RequestedTag varchar2, a_ActualTag varchar2 ) is t_RequestedProps udt_Properties; t_ActualProps udt_Properties; t_PropertyName varchar2(64); begin LogCall(a_RequestedTag, a_ActualTag); ParseTag(a_RequestedTag, t_RequestedProps); ParseTag(a_ActualTag, t_ActualProps); t_PropertyName := t_RequestedProps.first; while t_PropertyName is not null loop if not t_ActualProps.exists(t_PropertyName) or t_ActualProps(t_PropertyName) != t_RequestedProps(t_PropertyName) then SetProperty(t_PropertyName, t_RequestedProps(t_PropertyName)); end if; t_PropertyName := t_RequestedProps.next(t_PropertyName); end loop; end; end; / python-cx_Oracle-8.3.0/samples/subclassing.py000066400000000000000000000036051414105416400212770ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # subclassing.py # # Demonstrate how to subclass connections and cursors in order to add # additional functionality (like logging) or create specialized interfaces for # paticular applications. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import sample_env # sample subclassed connection which overrides the constructor (so no # parameters are required) and the cursor() method (so that the subclassed # cursor is returned instead of the default cursor implementation) class Connection(oracledb.Connection): def __init__(self): connect_string = sample_env.get_main_connect_string() print("CONNECT to database") super().__init__(connect_string) def cursor(self): return Cursor(self) # sample subclassed cursor which overrides the execute() and fetchone() # methods in order to perform some simple logging class Cursor(oracledb.Cursor): def execute(self, statement, args): print("EXECUTE", statement) print("ARGS:") for arg_index, arg in enumerate(args): print(" ", arg_index + 1, "=>", repr(arg)) return super().execute(statement, args) def fetchone(self): print("FETCH ONE") return super().fetchone() # create instances of the subclassed connection and cursor connection = Connection() cursor = connection.cursor() # demonstrate that the subclassed connection and cursor are being used cursor.execute("select count(*) from ChildTable where ParentId = :1", (30,)) count, = cursor.fetchone() print("COUNT:", int(count)) python-cx_Oracle-8.3.0/samples/transaction_guard.py000066400000000000000000000056051414105416400224730ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # transaction_guard.py # This script demonstrates the use of Transaction Guard to verify if a # transaction has completed, ensuring that a duplicate transaction is not # created or attempted if the application chooses to handle the error. This # feature is only available in Oracle Database 12.1. It follows loosely the # OCI sample provided by Oracle in its documentation about OCI and Transaction # Guard. # # Run the following as SYSDBA to set up Transaction Guard # # grant execute on dbms_app_cont to pythondemo; # # declare # t_Params dbms_service.svc_parameter_array; # begin # t_Params('COMMIT_OUTCOME') := 'true'; # t_Params('RETENTION_TIMEOUT') := 604800; # dbms_service.create_service('orcl-tg', 'orcl-tg', t_Params); # dbms_service.start_service('orcl-tg'); # end; # / # # This script requires cx_Oracle 5.3 and higher. #------------------------------------------------------------------------------ import sys import cx_Oracle as oracledb import sample_env # constants CONNECT_STRING = "localhost/orcl-tg" # create transaction and generate a recoverable error pool = oracledb.SessionPool(user=sample_env.get_main_user(), password=sample_env.get_main_password(), dsn=CONNECT_STRING, min=1, max=9, increment=2) connection = pool.acquire() cursor = connection.cursor() cursor.execute(""" delete from TestTempTable where IntCol = 1""") cursor.execute(""" insert into TestTempTable values (1, null)""") input("Please kill %s session now. Press ENTER when complete." % \ sample_env.get_main_user()) try: connection.commit() # this should fail sys.exit("Session was not killed. Terminating.") except oracledb.DatabaseError as e: error_obj, = e.args if not error_obj.isrecoverable: sys.exit("Session is not recoverable. Terminating.") ltxid = connection.ltxid if not ltxid: sys.exit("Logical transaction not available. Terminating.") pool.drop(connection) # check if previous transaction completed connection = pool.acquire() cursor = connection.cursor() args = (oracledb.Binary(ltxid), cursor.var(bool), cursor.var(bool)) _, committed, completed = cursor.callproc("dbms_app_cont.get_ltxid_outcome", args) print("Failed transaction was committed:", committed) print("Failed call was completed:", completed) python-cx_Oracle-8.3.0/samples/tutorial/000077500000000000000000000000001414105416400202475ustar00rootroot00000000000000python-cx_Oracle-8.3.0/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html000066400000000000000000002512231414105416400326420ustar00rootroot00000000000000 Python and Oracle Database Tutorial: Scripting for the Future

Python and Oracle Database Tutorial: Scripting for the Future

Python cx_Oracle logo

Contents

Overview

This tutorial is an introduction to using Python with Oracle Database. It contains beginner and advanced material. Sections can be done in any order. Choose the content that interests you and your skill level. The tutorial has scripts to run and modify, and has suggested solutions.

Python is a popular general purpose dynamic scripting language. The cx_Oracle interface provides the Python API to access Oracle Database.

If you are new to Python review the Appendix: Python Primer to gain an understanding of the language.

When you have finished this tutorial, we recommend reviewing the cx_Oracle documention.

The original copy of these instructions that you are reading is here.

cx_Oracle Architecture

Python programs call cx_Oracle functions. Internally cx_Oracle dynamically loads Oracle Client libraries to access Oracle Database. The database can be on the same machine as Python, or it can be remote. If the database is local, the client libraries from the Oracle Database software installation can be used.

Python cx_Oracle architecture

Setup

  • Install software

    To get going, follow either of the quick start instructions:

    For this tutorial, you will need Python 3.6 (or later), cx_Oracle 7.3 (or later), and access to Oracle Database.

    The Advanced Queuing section requires Python cx_Oracle to be using Oracle client libraries 12.2 or later. The SODA section requires Oracle Database 18 or later, and Python cx_Oracle must be using Oracle libraries from 18.5, or later.

  • Download the tutorial scripts

    The Python scripts used in this example are in the cx_Oracle GitHub repository.

    Download a zip file of the repository from here and unzip it. Alternatively you can use 'git' to clone the repository with git clone https://github.com/oracle/python-cx_Oracle.git

    The samples/tutorial directory has scripts to run and modify. The samples/tutorial/solutions directory has scripts with suggested code changes.

  • Create a database user

    If you have an existing user, you may be able to use it for most examples (some examples may require extra permissions).

    If you need to create a new user, review the grants created in samples/tutorial/sql/create_user.sql. Then open a terminal window, change to the samples/tutorial/sql directory, and run the create_user.sql script as the SYSTEM user, for example:

    cd samples/tutorial/sql
    sqlplus -l system/systempassword@localhost/orclpdb1 @create_user
    

    The example above connects as the SYSTEM user. The connection string is "localhost/orclpdb1", meaning use the database service "orclpdb1" running on localhost (the computer you are running SQL*Plus on). Substitute values for your environment. If you are using Oracle Autonomous Database, use the ADMIN user instead of SYSTEM.

    When the tutorial is finished, the drop_user.sql script in the same directory can be used to remove the tutorial user.

  • Install the sample tables

    Once you have a database user, then you can create the tutorial tables by running a command like this, using your values for the tutorial username, password and connection string:

    sqlplus -l pythonhol/welcome@localhost/orclpdb1 @setup_tables
    
  • Start the Database Resident Connection Pool (DRCP)

    If you want to try the DRCP examples in section 2, start the DRCP pool. (The pool is already started in Oracle Autonomous Database).

    Run SQL*Plus with SYSDBA privileges, for example:

    sqlplus -l sys/syspassword@localhost/orclcdb as sysdba
    

    and execute the command:

    execute dbms_connection_pool.start_pool()
    

    Note you may need to do this in the container database, not a pluggable database.

  • Review the connection credentials used by the tutorial scripts

    Review db_config.py and db_config.sql in the tutorial directory. These are included in other Python and SQL files.

    Edit db_config.py and change the default values to match the connection information for your environment. Alternatively you can set the given envionment variables in your terminal window. For example, the default username is "pythonhol" unless the envionment variable "PYTHON_USER" contains a different username. The default connection string is for the 'orclpdb1' database service on the same machine as Python. (In Python Database API terminology, the connection string parameter is called the "data source name", or "dsn".) Using envionment variables is convenient because you will not be asked to re-enter the password when you run scripts:

    user = os.environ.get("PYTHON_USER", "pythonhol")
    
    dsn = os.environ.get("PYTHON_CONNECT_STRING", "localhost/orclpdb1")
    
    pw = os.environ.get("PYTHON_PASSWORD")
    if pw is None:
        pw = getpass.getpass("Enter password for %s: " % user)
    

    Also change the default username and connection string in the SQL*Plus configuration file db_config.sql:

    -- Default database username
    def user = "pythonhol"
    
    -- Default database connection string
    def connect_string = "localhost/orclpdb1"
    
    -- Prompt for the password
    accept pw char prompt 'Enter database password for &user: ' hide
    

    The tutorial instructions may need adjusting, depending on how you have set up your environment.

  • Review the Instant Client library path

    Review the Oracle Client library path settings in db_config.py. If cx_Oracle cannot locate Oracle Client libraries, then your applications will fail with an error like "DPI-1047: Cannot locate a 64-bit Oracle Client library".

    # On Linux this must be None.
    # Instead, the Oracle environment must be set before Python starts.
    instant_client_dir = None
    
    # On Windows, if your database is on the same machine, comment these lines out
    # and let instant_client_dir be None.  Otherwise, set this to your Instant
    # Client directory.  Note the use of the raw string r"..."  so backslashes can
    # be used as directory separators.
    if sys.platform.startswith("win"):
        instant_client_dir = r"c:\oracle\instantclient_19_10"
    
    # On macOS (Intel x86) set the directory to your Instant Client directory
    if sys.platform.startswith("darwin"):
        instant_client_dir = os.environ.get("HOME")+"/Downloads/instantclient_19_8"
    
    # This can be called at most once per process.
    if instant_client_dir is not None:
        cx_Oracle.init_oracle_client(lib_dir=instant_client_dir)
    

    Set instant_client_dir to None or to a valid path according to the following notes:

    • If you are on macOS or Windows, and you have installed Oracle Instant Client libraries because your database is on a remote machine, then set instant_client_dir to the path of the Instant Client libraries.

    • If you are on Windows and have a local database installed, then comment out the two Windows lines, so that instant_client_dir remains None.

    • In all other cases (including Linux with Oracle Instant Client), make sure that instant_client_dir is set to None. In these cases you must make sure that the Oracle libraries from Instant Client or your ORACLE_HOME are in your system library search path before you start Python. On Linux, the path can be configured with ldconfig or with the LD_LIBRARY_PATH environment variables.

1. Connecting to Oracle

You can connect from Python to a local, remote or cloud database. Documentation link for further reading: Connecting to Oracle Database.

  • 1.1 Creating a basic connection

    Review the code contained in connect.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    print("Database version:", con.version)
    

    The cx_Oracle module is imported to provide the API for accessing the Oracle database. Many inbuilt and third party modules can be included in Python scripts this way.

    The connect() method is passed the username, the password and the connection string that you configured in the db_config.py module. In this case, Oracle's Easy Connect connection string syntax is used. It consists of the hostname of your machine, localhost, and the database service name orclpdb1. (In Python Database API terminology, the connection string parameter is called the "data source name", or "dsn".)

    Open a command terminal and change to the tutorial directory:

    cd samples/tutorial

    Run the Python script:

    python connect.py

    The version number of the database should be displayed. An exception is raised if the connection fails. Adjust the username, password or connection string parameters to invalid values to see the exception.

    cx_Oracle also supports "external authentication", which allows connections without needing usernames and passwords to be embedded in the code. Authentication would then instead be performed by, for example, LDAP or Oracle Wallets.

  • 1.2 Indentation indicates code structure

    There are no statement terminators or begin/end keywords or braces to indicate blocks of code.

    Open connect.py in an editor. Indent the print statement with some spaces:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
      print("Database version:", con.version)
    

    Save the script and run it again:

    python connect.py 

    This raises an exception about the indentation. The number of spaces or tabs must be consistent in each block; otherwise, the Python interpreter will either raise an exception or execute code unexpectedly.

    Python may not always be able to identify accidental from deliberate indentation. Check your indentation is correct before running each example. Make sure to indent all statement blocks equally. Note the sample files use spaces, not tabs.

  • 1.3 Executing a query

    Open query.py in an editor. It looks like:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    

    Edit the file and add the code shown in bold below:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    
    cur = con.cursor()
    cur.execute("select * from dept order by deptno")
    res = cur.fetchall()
    for row in res:
        print(row)
    

    Make sure the print(row) line is indented. This lab uses spaces, not tabs.

    The code executes a query and fetches all data.

    Save the file and run it:

    python query.py

    In each loop iteration a new row is stored in row as a Python "tuple" and is displayed.

    Fetching data is described further in section 3.

  • 1.4 Closing connections

    Connections and other resources used by cx_Oracle will automatically be closed at the end of scope. This is a common programming style that takes care of the correct order of resource closure.

    Resources can also be explicitly closed to free up database resources if they are no longer needed. This is strongly recommended in blocks of code that remain active for some time.

    Open query.py in an editor and add calls to close the cursor and connection like:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    
    cur = con.cursor()
    cur.execute("select * from dept order by deptno")
    res = cur.fetchall()
    for row in res:
        print(row)
    
    cur.close()
    con.close()
    

    Running the script completes without error:

    python query.py

    If you swap the order of the two close() calls you will see an error.

  • 1.5 Checking versions

    Review the code contained in versions.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    
    print(cx_Oracle.version)

    Run the script:

    python versions.py

    This gives the version of the cx_Oracle interface.

    Edit the file to print the version of the database, and of the Oracle client libraries used by cx_Oracle:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    
    print(cx_Oracle.version)
    print("Database version:", con.version)
    print("Client version:", cx_Oracle.clientversion())
    

    When the script is run, it will display:

    7.1.0
    Database version: 19.3.0.0.0
    Client version: (19, 8, 0, 0, 0)
    

    Note the client version is a tuple.

    Any cx_Oracle installation can connect to older and newer Oracle Database versions. By checking the Oracle Database and client versions numbers, the application can make use of the best Oracle features available.

2. Connection Pooling

Connection pooling is important for performance in when multi-threaded applications frequently connect and disconnect from the database. Pooling also gives the best support for Oracle high availability features. Documentation link for further reading: Connection Pooling.

  • 2.1 Connection pooling

    Review the code contained in connect_pool.py:

    import cx_Oracle
    import threading
    import db_config
    
    pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn,
                                 min = 2, max = 5, increment = 1, threaded = True,
                                 getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT)
    
    def Query():
        con = pool.acquire()
        cur = con.cursor()
        for i in range(4):
            cur.execute("select myseq.nextval from dual")
            seqval, = cur.fetchone()
            print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
    
    thread1 = threading.Thread(name='#1', target=Query)
    thread1.start()
    
    thread2 = threading.Thread(name='#2', target=Query)
    thread2.start()
    
    thread1.join()
    thread2.join()
    
    print("All done!")
    

    The SessionPool() function creates a pool of Oracle connections for the user. Connections in the pool can be used by cx_Oracle by calling pool.acquire(). The initial pool size is 2 connections. The maximum size is 5 connections. When the pool needs to grow, then 1 new connection will be created at a time. The pool can shrink back to the minimum size of 2 when connections are no longer in use.

    The def Query(): line creates a method that is called by each thread.

    In the method, the pool.acquire() call gets one connection from the pool (as long as less than 5 are already in use). This connection is used in a loop of 4 iterations to query the sequence myseq. At the end of the method, cx_Oracle will automatically close the cursor and release the connection back to the pool for reuse.

    The seqval, = cur.fetchone() line fetches a row and puts the single value contained in the result tuple into the variable seqval. Without the comma, the value in seqval would be a tuple like "(1,)".

    Two threads are created, each invoking the Query() method.

    In a command terminal, run:

    python connect_pool.py

    The output shows interleaved query results as each thread fetches values independently. The order of interleaving may vary from run to run.

  • 2.2 Connection pool experiments

    Review connect_pool2.py, which has a loop for the number of threads, each iteration invoking the Query() method:

    import cx_Oracle
    import threading
    import db_config
    
    pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn,
                                 min = 2, max = 5, increment = 1, threaded = True,
                                 getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT)
    
    def Query():
        con = pool.acquire()
        cur = con.cursor()
        for i in range(4):
            cur.execute("select myseq.nextval from dual")
            seqval, = cur.fetchone()
            print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
    
    numberOfThreads = 2
    threadArray = []
    
    for i in range(numberOfThreads):
        thread = threading.Thread(name = '#' + str(i), target = Query)
        threadArray.append(thread)
        thread.start()
    
    for t in threadArray:
        t.join()
    
    print("All done!")
    

    In a command terminal, run:

    python connect_pool2.py

    Experiment with different values of the pool parameters and numberOfThreads. Larger initial pool sizes will make the pool creation slower, but the connections will be available immediately when needed.

    Try changing getmode to cx_Oracle.SPOOL_ATTRVAL_NOWAIT. When numberOfThreads exceeds the maximum size of the pool, the acquire() call will now generate an error such as "ORA-24459: OCISessionGet() timed out waiting for pool to create new connections".

    Pool configurations where min is the same as max (and increment = 0) are often recommended as a best practice. This avoids connection storms on the database server.

  • 2.3 Creating a DRCP Connection

    Database Resident Connection Pooling allows multiple Python processes on multiple machines to share a small pool of database server processes.

    Below left is a diagram without DRCP. Every application standalone connection (or cx_Oracle connection-pool connection) has its own database server process. Standalone application connect() and close calls require the expensive create and destroy of those database server processes. cx_Oracle connection pools reduce these costs by keeping database server processes open, but every cx_Oracle connection pool will requires its own set of database server processes, even if they are not doing database work: these idle server processes consumes database host resources. Below right is a diagram with DRCP. Scripts and Python processes can share database servers from a precreated pool of servers and return them when they are not in use.

    Picture of 3-tier application architecture without DRCP showing connections from multiple application processes each going to a server process in the database tier

    Without DRCP

    Picture of 3-tier application architecture with DRCP showing connections from multiple application processes going to a pool of server processes in the database tier

    With DRCP

    DRCP is useful when the database host machine does not have enough memory to handle the number of database server processes required. If DRCP is enabled, it is best used in conjunction with cx_Oracle's connection pooling. However, if the database host memory is large enough, then the default, 'dedicated' server process model is generally recommended. This can be with or without a cx_Oracle connection pool, depending on the connection rate.

    Batch scripts doing long running jobs should generally use dedicated connections. Both dedicated and DRCP servers can be used together in the same application or database.

    Review the code contained in connect_drcp.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn + ":pooled",
                            cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF)
    print("Database version:", con.version)
    

    This is similar to connect.py but ":pooled" is appended to the connection string, telling the database to use a pooled server. A Connection Class "PYTHONHOL" is also passed into the connect() method to allow grouping of database servers to applications. Note with Autonomous Database, the connection string has a different form, see the ADB documentation.

    The "purity" of the connection is defined as the ATTR_PURITY_SELF constant, meaning the session state (such as the default date format) might be retained between connection calls, giving performance benefits. Session information will be discarded if a pooled server is later reused by an application with a different connection class name.

    Applications that should never share session information should use a different connection class and/or use ATTR_PURITY_NEW to force creation of a new session. This reduces overall scalability but prevents applications mis-using session information.

    Run connect_drcp.py in a terminal window.

    python connect_drcp.py

    The output is simply the version of the database.

  • 2.4 Connection pooling and DRCP

    DRCP works well with cx_Oracle's connection pooling.

    Edit connect_pool2.py, reset any changed pool options, and modify it to use DRCP:

    import cx_Oracle
    import threading
    
    pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn + ":pooled",
                                 min = 2, max = 5, increment = 1, threaded = True,
                                 getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT)
    
    def Query():
        con = pool.acquire(cclass = "PYTHONHOL", purity = cx_Oracle.ATTR_PURITY_SELF)
        cur = conn.cursor()
        for i in range(4):
            cur.execute("select myseq.nextval from dual")
            seqval, = cur.fetchone()
            print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
    
    numberOfThreads = 2
    threadArray = []
    
    for i in range(numberOfThreads):
        thread = threading.Thread(name = '#' + str(i), target = Query)
        threadArray.append(thread)
        thread.start()
    
    for t in threadArray:
        t.join()
    
    print("All done!")
    

    The script logic does not need to be changed to benefit from DRCP connection pooling.

    Run the script:

    python connect_pool2.py

    Review drcp_query.sql and set the connection string to your database. Then open a new a terminal window and invoke SQL*Plus:

    sqlplus /nolog @drcp_query.sql

    This will prompt for the SYSTEM password and the database connection string. With Pluggable databases, you will need to connect to the container database. Note that with ADB, this view does not contain rows, so running this script is not useful.

    For other databases, the script shows the number of connection requests made to the pool since the database was started ("NUM_REQUESTS"), how many of those reused a pooled server's session ("NUM_HITS"), and how many had to create new sessions ("NUM_MISSES"). Typically the goal is a low number of misses.

    To see the pool configuration you can query DBA_CPOOL_INFO.

  • 2.5 More DRCP investigation

    To explore the behaviors of cx_Oracle connection pooling and DRCP pooling futher, you could try changing the purity to cx_Oracle.ATTR_PURITY_NEW to see the effect on the DRCP NUM_MISSES statistic.

    Another experiement is to include the time module at the file top:

    import time

    and add calls to time.sleep(1) in the code, for example in the query loop. Then look at the way the threads execute. Use drcp_query.sql to monitor the pool's behavior.

3. Fetching Data

Executing SELECT queries is the primary way to get data from Oracle Database. Documentation link for further reading: SQL Queries.

  • 3.1 A simple query

    There are a number of functions you can use to query an Oracle database, but the basics of querying are always the same:

    1. Execute the statement.
    2. Bind data values (optional).
    3. Fetch the results from the database.

    Review the code contained in query2.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    
    cur = con.cursor()
    cur.execute("select * from dept order by deptno")
    for deptno, dname, loc in cur:
        print("Department number: ", deptno)
        print("Department name: ", dname)
        print("Department location:", loc)
    

    The cursor() method opens a cursor for statements to use.

    The execute() method parses and executes the statement.

    The loop fetches each row from the cursor and unpacks the returned tuple into the variables deptno, dname, loc, which are then printed.

    Run the script in a terminal window:

    python query2.py

    The output is:

    Department number:  10
    Department name:  ACCOUNTING
    Department location: NEW YORK
    Department number:  20
    Department name:  RESEARCH
    Department location: DALLAS
    Department number:  30
    Department name:  SALES
    Department location: CHICAGO
    Department number:  40
    Department name:  OPERATIONS
    Department location: BOSTON
  • 3.2 Using fetchone()

    When the number of rows is large, the fetchall() call may use too much memory.

    Review the code contained in query_one.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    cur.execute("select * from dept order by deptno")
    row = cur.fetchone()
    print(row)
    
    row = cur.fetchone()
    print(row)
    

    This uses the fetchone() method to return just a single row as a tuple. When called multiple time, consecutive rows are returned:

    Run the script in a terminal window:

    python query_one.py

    The first two rows of the table are printed.

  • 3.3 Using fetchmany()

    Review the code contained in query_many.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    cur.execute("select * from dept order by deptno")
    res = cur.fetchmany(numRows = 3)
    print(res)
    

    The fetchmany() method returns a list of tuples. By default the number of rows returned is specified by the cursor attribute arraysize (which defaults to 100). Here the numRows parameter specifies that three rows should be returned.

    Run the script in a terminal window:

    python query_many.py

    The first three rows of the table are returned as a list (Python's name for an array) of tuples.

    You can access elements of the lists by position indexes. To see this, edit the file and add:

    print(res[0])    # first row
    print(res[0][1]) # second element of first row
    
  • 3.4 Scrollable cursors

    Scrollable cursors enable the application to move backwards as well as forwards in query results. They can be used to skip rows as well as move to a particular row.

    Review the code contained in query_scroll.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor(scrollable = True)
    
    cur.execute("select * from dept order by deptno")
    
    cur.scroll(2, mode = "absolute")  # go to second row
    print(cur.fetchone())
    
    cur.scroll(-1)                    # go back one row
    print(cur.fetchone())
    

    Run the script in a terminal window:

    python query_scroll.py

    Edit query_scroll.py and experiment with different scroll options and orders, such as:

    cur.scroll(1)  # go to next row
    print(cur.fetchone())
    
    cur.scroll(mode = "first")  # go to first row
    print(cur.fetchone())
    

    Try some scroll options that go beyond the number of rows in the resultset.

  • 3.5 Tuning with arraysize and prefetchrows

    This section demonstrates a way to improve query performance by increasing the number of rows returned in each batch from Oracle to the Python program.

    Row prefetching and array fetching are both internal buffering techniques to reduce round-trips to the database. The difference is the code layer that is doing the buffering, and when the buffering occurs.

    First, create a table with a large number of rows. Review query_arraysize.sql:

    create table bigtab (mycol varchar2(20));
    begin
      for i in 1..20000
      loop
       insert into bigtab (mycol) values (dbms_random.string('A',20));
      end loop;
    end;
    /
    show errors
    
    commit;
    

    In a terminal window run the script as:

    sqlplus /nolog @query_arraysize.sql

    Review the code contained in query_arraysize.py:

    import cx_Oracle
    import time
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    
    start = time.time()
    
    cur = con.cursor()
    cur.prefetchrows = 100
    cur.arraysize = 100
    cur.execute("select * from bigtab")
    res = cur.fetchall()
    # print(res)  # uncomment to display the query results
    
    elapsed = (time.time() - start)
    print(elapsed, "seconds")
    

    This uses the 'time' module to measure elapsed time of the query. The prefetchrows and arraysize values are 100. This causes batches of 100 records at a time to be returned from the database to a cache in Python. These values can be tuned to reduce the number of "round-trips" made to the database, often reducing network load and reducing the number of context switches on the database server. The fetchone(), fetchmany() and fetchall() methods will read from the cache before requesting more data from the database.

    In a terminal window, run:

    python query_arraysize.py

    Rerun a few times to see the average times.

    Experiment with different prefetchrows and arraysize values. For example, edit query_arraysize.py and change the arraysize to:

    cur.arraysize = 2000

    Rerun the script to compare the performance of different arraysize settings.

    In general, larger array sizes improve performance. Depending on how fast your system is, you may need to use different values than those given here to see a meaningful time difference.

    There is a time/space tradeoff for increasing the values. Larger values will require more memory in Python for buffering the records.

    If you know the query returns a fixed number of rows, for example 20 rows, then set arraysize to 20 and prefetchrows to 21. The addition of one for prefetchrows prevents a round-trip to check for end-of-fetch. The statement execution and fetch will take a total of one round-trip. This minimizes load on the database.

    If you know a query only returns a few records, decrease the arraysize from the default to reduce memory usage.

4. Binding Data

Bind variables enable you to re-execute statements with new data values without the overhead of re-parsing the statement. Binding improves code reusability, improves application scalability, and can reduce the risk of SQL injection attacks. Using bind variables is strongly recommended. Documentation link for further reading: Using Bind Variables.

  • 4.1 Binding in queries

    Review the code contained in bind_query.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    sql = "select * from dept where deptno = :id order by deptno"
    
    cur.execute(sql, id = 20)
    res = cur.fetchall()
    print(res)
    
    cur.execute(sql, id = 10)
    res = cur.fetchall()
    print(res)
    

    The statement contains a bind variable ":id" placeholder. The statement is executed twice with different values for the WHERE clause.

    From a terminal window, run:

    python bind_query.py

    The output shows the details for the two departments.

    An arbitrary number of named arguments can be used in an execute() call. Each argument name must match a bind variable name. Alternatively, instead of passing multiple arguments you could pass a second argument to execute() that is a sequence or a dictionary. Later examples show these syntaxes.

    To bind a database NULL, use the Python value None

    cx_Oracle uses Oracle Database's Statement Cache. As long as the statement you pass to execute() is in that cache, you can use different bind values and still avoid a full statement parse. The statement cache size is configurable for each connection. To see the default statement cache size, edit bind_query.py and add a line at the end:

    print(con.stmtcachesize)
    

    Re-run the file.

    In your applications you would set the statement cache size to the number of unique statements commonly executed.

  • 4.2 Binding in inserts

    Review the code in bind_insert.sql creating a table for inserting data:

    create table mytab (id number, data varchar2(20), constraint my_pk primary key (id));
    

    Run the script as:

    sqlplus /nolog @bind_insert.sql

    Review the code contained in bind_insert.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    rows = [ (1, "First" ), (2, "Second" ),
             (3, "Third" ), (4, "Fourth" ),
             (5, "Fifth" ), (6, "Sixth" ),
             (7, "Seventh" ) ]
    
    cur.executemany("insert into mytab(id, data) values (:1, :2)", rows)
    
    # Now query the results back
    
    cur2 = con.cursor()
    cur2.execute('select * from mytab')
    res = cur2.fetchall()
    print(res)
    

    The 'rows' array contains the data to be inserted.

    The executemany() call inserts all rows. This call uses "array binding", which is an efficient way to insert multiple records.

    The final part of the script queries the results back and displays them as a list of tuples.

    From a terminal window, run:

    python bind_insert.py

    The new results are automatically rolled back at the end of the script so re-running it will always show the same number of rows in the table.

  • 4.3 Batcherrors

    The Batcherrors features allows invalid data to be identified while allowing valid data to be inserted.

    Edit the data values in bind_insert.py and create a row with a duplicate key:

    rows = [ (1, "First" ), (2, "Second" ),
             (3, "Third" ), (4, "Fourth" ),
             (5, "Fifth" ), (6, "Sixth" ),
             (6, "Duplicate" ),
             (7, "Seventh" ) ]
    
    

    From a terminal window, run:

    python bind_insert.py

    The duplicate generates the error "ORA-00001: unique constraint (PYTHONHOL.MY_PK) violated". The data is rolled back and the query returns no rows.

    Edit the file again and enable batcherrors like:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    rows = [ (1, "First" ), (2, "Second" ),
             (3, "Third" ), (4, "Fourth" ),
             (5, "Fifth" ), (6, "Sixth" ),
             (6, "Duplicate" ),
             (7, "Seventh" ) ]
    
    cur.executemany("insert into mytab(id, data) values (:1, :2)", rows, batcherrors = True)
    
    for error in cur.getbatcherrors():
        print("Error", error.message.rstrip(), "at row offset", error.offset)
    
    # Now query the results back
    
    cur2 = con.cursor()
    cur2.execute('select * from mytab')
    res = cur2.fetchall()
    print(res)
    

    Run the file:

    python bind_insert.py

    The new code shows the offending duplicate row: "ORA-00001: unique constraint (PYTHONHOL.MY_PK) violated at row offset 6". This indicates the 6th data value (counting from 0) had a problem.

    The other data gets inserted and is queried back.

    At the end of the script, cx_Oracle will roll back an uncommitted transaction. If you want to commit results, you can use:

    con.commit()

    To force cx_Oracle to roll back, use:

    con.rollback()
  • 4.4 Binding named objects

    cx_Oracle can fetch and bind named object types such as Oracle's Spatial Data Objects (SDO).

    In a terminal window, start SQL*Plus using the lab credentials and connection string, such as:

    sqlplus -l pythonhol/welcome@localhost/orclpdb1
    

    Use the SQL*Plus DESCRIBE command to look at the SDO definition:

    desc MDSYS.SDO_GEOMETRY
    

    It contains various attributes and methods. The top level description is:

     Name                                      Null?    Type
     ----------------------------------------- -------- ----------------------------
     SDO_GTYPE                                          NUMBER
     SDO_SRID                                           NUMBER
     SDO_POINT                                          MDSYS.SDO_POINT_TYPE
     SDO_ELEM_INFO                                      MDSYS.SDO_ELEM_INFO_ARRAY
     SDO_ORDINATES                                      MDSYS.SDO_ORDINATE_ARRAY
    

    Review the code contained in bind_sdo.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    # Create table
    cur.execute("""begin
                     execute immediate 'drop table testgeometry';
                     exception when others then
                       if sqlcode <> -942 then
                         raise;
                       end if;
                   end;""")
    cur.execute("""create table testgeometry (
                   id number(9) not null,
                   geometry MDSYS.SDO_GEOMETRY not null)""")
    
    # Create and populate Oracle objects
    typeObj = con.gettype("MDSYS.SDO_GEOMETRY")
    elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
    ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
    obj = typeObj.newobject()
    obj.SDO_GTYPE = 2003
    obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
    obj.SDO_ELEM_INFO.extend([1, 1003, 3])
    obj.SDO_ORDINATES = ordinateTypeObj.newobject()
    obj.SDO_ORDINATES.extend([1, 1, 5, 7])
    print("Created object", obj)
    
    # Add a new row
    print("Adding row to table...")
    cur.execute("insert into testgeometry values (1, :objbv)", objbv = obj)
    print("Row added!")
    
    # Query the row
    print("Querying row just inserted...")
    cur.execute("select id, geometry from testgeometry");
    for row in cur:
        print(row)
    

    This uses gettype() to get the database types of the SDO and its object attributes. The newobject() calls create Python representations of those objects. The python object atributes are then set. Oracle VARRAY types such as SDO_ELEM_INFO_ARRAY are set with extend().

    Run the file:

    python bind_sdo.py

    The new SDO is shown as an object, similar to:

    (1, <cx_Oracle.Object MDSYS.SDO_GEOMETRY at 0x104a76230>)

    To show the attribute values, edit the the query code section at the end of the file. Add a new method that traverses the object. The file below the existing comment "# (Change below here)") should look like:

    # (Change below here)
    
    # Define a function to dump the contents of an Oracle object
    def dumpobject(obj, prefix = "  "):
        if obj.type.iscollection:
            print(prefix, "[")
            for value in obj.aslist():
                if isinstance(value, cx_Oracle.Object):
                    dumpobject(value, prefix + "  ")
                else:
                    print(prefix + "  ", repr(value))
            print(prefix, "]")
        else:
            print(prefix, "{")
            for attr in obj.type.attributes:
                value = getattr(obj, attr.name)
                if isinstance(value, cx_Oracle.Object):
                    print(prefix + "  " + attr.name + " :")
                    dumpobject(value, prefix + "    ")
                else:
                    print(prefix + "  " + attr.name + " :", repr(value))
            print(prefix, "}")
    
    # Query the row
    print("Querying row just inserted...")
    cur.execute("select id, geometry from testgeometry")
    for id, obj in cur:
        print("Id: ", id)
        dumpobject(obj)
    

    Run the file again:

    python bind_sdo.py

    This shows

    Querying row just inserted...
    Id:  1
       {
        SDO_GTYPE : 2003
        SDO_SRID : None
        SDO_POINT : None
        SDO_ELEM_INFO :
           [
             1
             1003
             3
           ]
        SDO_ORDINATES :
           [
             1
             1
             5
             7
           ]
       }
    

    To explore further, try setting the SDO attribute SDO_POINT, which is of type SDO_POINT_TYPE.

    The gettype() and newobject() methods can also be used to bind PL/SQL Records and Collections.

    Before deciding to use objects, review your performance goals because working with scalar values can be faster.

5. PL/SQL

PL/SQL is Oracle's procedural language extension to SQL. PL/SQL procedures and functions are stored and run in the database. Using PL/SQL lets all database applications reuse logic, no matter how the application accesses the database. Many data-related operations can be performed in PL/SQL faster than extracting the data into a program (for example, Python) and then processing it. Documentation link for further reading: PL/SQL Execution.

  • 5.1 PL/SQL functions

    Review plsql_func.sql which creates a PL/SQL stored function myfunc() to insert a row into a new table named ptab and return double the inserted value:

    create table ptab (mydata varchar(20), myid number);
    
    create or replace function myfunc(d_p in varchar2, i_p in number) return number as
      begin
        insert into ptab (mydata, myid) values (d_p, i_p);
        return (i_p * 2);
      end;
    /
    

    Run the script using:

    sqlplus /nolog @plsql_func.sql

    Review the code contained in plsql_func.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    res = cur.callfunc('myfunc', int, ('abc', 2))
    print(res)
    

    This uses callfunc() to execute the function. The second parameter is the type of the returned value. It should be one of the types supported by cx_Oracle or one of the type constants defined by cx_Oracle (such as cx_Oracle.NUMBER). The two PL/SQL function parameters are passed as a tuple, binding them to the function parameter arguments.

    From a terminal window, run:

    python plsql_func.py

    The output is a result of the PL/SQL function calculation.

  • 5.2 PL/SQL procedures

    Review plsql_proc.sql which creates a PL/SQL procedure myproc() to accept two parameters. The second parameter contains an OUT return value.

    create or replace procedure myproc(v1_p in number, v2_p out number) as
    begin
      v2_p := v1_p * 2;
    end;
    /
    

    Run the script with:

    sqlplus /nolog @plsql_proc.sql

    Review the code contained in plsql_proc.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    myvar = cur.var(int)
    cur.callproc('myproc', (123, myvar))
    print(myvar.getvalue())
    

    This creates an integer variable myvar to hold the value returned by the PL/SQL OUT parameter. The input number 123 and the output variable name are bound to the procedure call parameters using a tuple.

    To call the PL/SQL procedure, the callproc() method is used.

    In a terminal window, run:

    python plsql_proc.py

    The getvalue() method displays the returned value.

6. Type Handlers

Type handlers enable applications to alter data that is fetched from, or sent to, the database. Documentation links for further reading: Changing Fetched Data Types with Output Type Handlers and Changing Bind Data Types using an Input Type Handler.

  • 6.1 Basic output type handler

    Output type handlers enable applications to change how data is fetched from the database. For example, numbers can be returned as strings or decimal objects. LOBs can be returned as string or bytes.

    A type handler is enabled by setting the outputtypehandler attribute on either a cursor or the connection. If set on a cursor it only affects queries executed by that cursor. If set on a connection it affects all queries executed on cursors created by that connection.

    Review the code contained in type_output.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    print("Standard output...")
    for row in cur.execute("select * from dept"):
        print(row)
    

    In a terminal window, run:

    python type_output.py

    This shows the department number represented as digits like 10.

    Add an output type handler to the bottom of the file:

    def ReturnNumbersAsStrings(cursor, name, defaultType, size, precision, scale):
        if defaultType == cx_Oracle.NUMBER:
            return cursor.var(str, 9, cursor.arraysize)
    
    print("Output type handler output...")
    cur = con.cursor()
    cur.outputtypehandler = ReturnNumbersAsStrings
    for row in cur.execute("select * from dept"):
        print(row)
    

    This type handler converts any number columns to strings with maxium size 9.

    Run the script again:

    python type_output.py

    The new output shows the department numbers are now strings within quotes like '10'.

  • 6.2 Output type handlers and variable converters

    When numbers are fetched from the database, the conversion from Oracle's decimal representation to Python's binary format may need careful handling. To avoid unexpected issues, the general recommendation is to do number operations in SQL or PL/SQL, or to use the decimal module in Python.

    Output type handlers can be combined with variable converters to change how data is fetched.

    Review type_converter.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    for value, in cur.execute("select 0.1 from dual"):
        print("Value:", value, "* 3 =", value * 3)
    

    Run the file:

    python type_converter.py

    The output is like:

    Value: 0.1 * 3 = 0.30000000000000004

    Edit the file and add a type handler that uses a Python decimal converter:

    import cx_Oracle
    import decimal
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
        if defaultType == cx_Oracle.NUMBER:
            return cursor.var(str, 9, cursor.arraysize, outconverter = decimal.Decimal)
    
    cur.outputtypehandler = ReturnNumbersAsDecimal
    
    for value, in cur.execute("select 0.1 from dual"):
        print("Value:", value, "* 3 =", value * 3)
    

    The Python decimal.Decimal converter gets called with the string representation of the Oracle number. The output from decimal.Decimal is returned in the output tuple.

    Run the file again:

    python type_converter.py

    Output is like:

    Value: 0.1 * 3 = 0.3

    Although the code demonstrates the use of outconverter, in this particular case, the variable can be created simply by using the following code to replace the outputtypehandler function defined above:

    def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
        if defaultType == cx_Oracle.NUMBER:
            return cursor.var(decimal.Decimal, arraysize = cursor.arraysize)
    
  • 6.3 Input type handlers

    Input type handlers enable applications to change how data is bound to statements, or to enable new types to be bound directly without having to be converted individually.

    Review type_input.py, which is similar to the final bind_sdo.py from section 4.4, with the addition of a new class and converter (shown in bold):

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    # Create table
    cur.execute("""begin
                     execute immediate 'drop table testgeometry';
                     exception when others then
                       if sqlcode <> -942 then
                         raise;
                       end if;
                   end;""")
    cur.execute("""create table testgeometry (
                   id number(9) not null,
                   geometry MDSYS.SDO_GEOMETRY not null)""")
    
    # Create a Python class for an SDO
    class mySDO(object):
    
        def __init__(self, gtype, elemInfo, ordinates):
            self.gtype = gtype
            self.elemInfo = elemInfo
            self.ordinates = ordinates
    
    # Get Oracle type information
    objType = con.gettype("MDSYS.SDO_GEOMETRY")
    elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
    ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
    
    # Convert a Python object to MDSYS.SDO_GEOMETRY
    def SDOInConverter(value):
        obj = objType.newobject()
        obj.SDO_GTYPE = value.gtype
        obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
        obj.SDO_ELEM_INFO.extend(value.elemInfo)
        obj.SDO_ORDINATES = ordinateTypeObj.newobject()
        obj.SDO_ORDINATES.extend(value.ordinates)
        return obj
    
    def SDOInputTypeHandler(cursor, value, numElements):
        if isinstance(value, mySDO):
            return cursor.var(cx_Oracle.OBJECT, arraysize = numElements,
                    inconverter = SDOInConverter, typename = objType.name)
    
    sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7])  # Python object
    cur.inputtypehandler = SDOInputTypeHandler
    cur.execute("insert into testgeometry values (:1, :2)", (1, sdo))
    
    # Define a function to dump the contents of an Oracle object
    def dumpobject(obj, prefix = "  "):
        if obj.type.iscollection:
            print(prefix, "[")
            for value in obj.aslist():
                if isinstance(value, cx_Oracle.Object):
                    dumpobject(value, prefix + "  ")
                else:
                    print(prefix + "  ", repr(value))
            print(prefix, "]")
        else:
            print(prefix, "{")
            for attr in obj.type.attributes:
                value = getattr(obj, attr.name)
                if isinstance(value, cx_Oracle.Object):
                    print(prefix + "  " + attr.name + " :")
                    dumpobject(value, prefix + "    ")
                else:
                    print(prefix + "  " + attr.name + " :", repr(value))
            print(prefix, "}")
    
    # Query the row
    print("Querying row just inserted...")
    cur.execute("select id, geometry from testgeometry")
    for (id, obj) in cur:
        print("Id: ", id)
        dumpobject(obj)
    

    In the new file, a Python class mySDO is defined, which has attributes corresponding to each Oracle MDSYS.SDO_GEOMETRY attribute. The mySDO class is used lower in the code to create a Python instance:

    sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7])

    which is then directly bound into the INSERT statement like:

    cur.execute("insert into testgeometry values (:1, :2)", (1, sdo))

    The mapping between Python and Oracle objects is handled in SDOInConverter which uses the cx_Oracle newobject() and extend() methods to create an Oracle object from the Python object values. The SDOInConverter method is called by the input type handler SDOInputTypeHandler whenever an instance of mySDO is inserted with the cursor.

    To confirm the behavior, run the file:

    python type_input.py

7. LOBs

Oracle Database "LOB" long objects can be streamed using a LOB locator, or worked with directly as strings or bytes. Documentation link for further reading: Using CLOB and BLOB Data.

  • 7.1 Fetching a CLOB using a locator

    Review the code contained in clob.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    print("Inserting data...")
    cur.execute("truncate table testclobs")
    longString = ""
    for i in range(5):
        char = chr(ord('A') + i)
        longString += char * 250
        cur.execute("insert into testclobs values (:1, :2)",
                       (i + 1, "String data " + longString + ' End of string'))
    con.commit()
    
    print("Querying data...")
    cur.execute("select * from testclobs where id = :id", {'id': 1})
    (id, clob) = cur.fetchone()
    print("CLOB length:", clob.size())
    clobdata = clob.read()
    print("CLOB data:", clobdata)
    

    This inserts some test string data and then fetches one record into clob, which is a cx_Oracle character LOB Object. Methods on LOB include size() and read().

    To see the output, run the file:

    python clob.py

    Edit the file and experiment reading chunks of data by giving start character position and length, such as clob.read(1,10)

  • 7.2 Fetching a CLOB as a string

    For CLOBs small enough to fit in the application memory, it is much faster to fetch them directly as strings.

    Review the code contained in clob_string.py. The differences from clob.py are shown in bold:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    print("Inserting data...")
    cur.execute("truncate table testclobs")
    longString = ""
    for i in range(5):
        char = chr(ord('A') + i)
        longString += char * 250
        cur.execute("insert into testclobs values (:1, :2)",
                    (i + 1, "String data " + longString + ' End of string'))
    con.commit()
    
    def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
        if defaultType == cx_Oracle.CLOB:
            return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize)
    
    con.outputtypehandler = OutputTypeHandler
    
    print("Querying data...")
    cur.execute("select * from testclobs where id = :id", {'id': 1})
    (id, clobdata) = cur.fetchone()
    print("CLOB length:", len(clobdata))
    print("CLOB data:", clobdata)
    

    The OutputTypeHandler causes cx_Oracle to fetch the CLOB as a string. Standard Python string functions such as len() can be used on the result.

    The output is the same as for clob.py. To check, run the file:

    python clob_string.py

8. Rowfactory functions

Rowfactory functions enable queries to return objects other than tuples. They can be used to provide names for the various columns or to return custom objects.

  • 8.1 Rowfactory for mapping column names

    Review the code contained in rowfactory.py:

    import collections
    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    cur.execute("select deptno, dname from dept")
    rows = cur.fetchall()
    
    print('Array indexes:')
    for row in rows:
        print(row[0], "->", row[1])
    
    print('Loop target variables:')
    for c1, c2 in rows:
        print(c1, "->", c2)
    

    This shows two methods of accessing result set items from a data row. The first uses array indexes like row[0]. The second uses loop target variables which take the values of each row tuple.

    Run the file:

    python rowfactory.py

    Both access methods gives the same results.

    To use a rowfactory function, edit rowfactory.py and add this code at the bottom:

    print('Rowfactory:')
    cur.execute("select deptno, dname from dept")
    cur.rowfactory = collections.namedtuple("MyClass", ["DeptNumber", "DeptName"])
    
    rows = cur.fetchall()
    for row in rows:
        print(row.DeptNumber, "->", row.DeptName)
    

    This uses the Python factory function namedtuple() to create a subclass of tuple that allows access to the elements via indexes or the given field names.

    The print() function shows the use of the new named tuple fields. This coding style can help reduce coding errors.

    Run the script again:

    python rowfactory.py

    The output results are the same.

9. Subclassing connections and cursors

Subclassing enables application to "hook" connection and cursor creation. This can be used to alter or log connection and execution parameters, and to extend cx_Oracle functionality. Documentation link for further reading: Tracing SQL and PL/SQL Statements.

  • 9.1 Subclassing connections

    Review the code contained in subclass.py:

    import cx_Oracle
    import db_config
    
    class MyConnection(cx_Oracle.Connection):
    
        def __init__(self):
            print("Connecting to database")
            return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
    
    con = MyConnection()
    cur = con.cursor()
    
    cur.execute("select count(*) from emp where deptno = :bv", (10,))
    count, = cur.fetchone()
    print("Number of rows:", count)
    

    This creates a new class "MyConnection" that inherits from the cx_Oracle Connection class. The __init__ method is invoked when an instance of the new class is created. It prints a message and calls the base class, passing the connection credentials.

    In the "normal" application, the application code:

    con = MyConnection()

    does not need to supply any credentials, as they are embedded in the custom subclass. All the cx_Oracle methods such as cursor() are available, as shown by the query.

    Run the file:

    python subclass.py

    The query executes successfully.

  • 9.2 Subclassing cursors

    Edit subclass.py and extend the cursor() method with a new MyCursor class:

    import cx_Oracle
    import db_config
    
    class MyConnection(cx_Oracle.Connection):
    
        def __init__(self):
            print("Connecting to database")
            return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
    
        def cursor(self):
            return MyCursor(self)
    
    class MyCursor(cx_Oracle.Cursor):
    
       def execute(self, statement, args):
           print("Executing:", statement)
           print("Arguments:")
           for argIndex, arg in enumerate(args):
               print("  Bind", argIndex + 1, "has value", repr(arg))
               return super(MyCursor, self).execute(statement, args)
    
       def fetchone(self):
           print("Fetchone()")
           return super(MyCursor, self).fetchone()
    
    con = MyConnection()
    cur = con.cursor()
    
    cur.execute("select count(*) from emp where deptno = :bv", (10,))
    count, = cur.fetchone()
    print("Number of rows:", count)
    

    When the application gets a cursor from the MyConnection class, the new cursor() method returns an instance of our new MyCursor class.

    The "application" query code remains unchanged. The new execute() and fetchone() methods of the MyCursor class get invoked. They do some logging and invoke the parent methods to do the actual statement execution.

    To confirm this, run the file again:

    python subclass.py

10. Advanced Queuing

Oracle Advanced Queuing (AQ) allows messages to be passed between applications. Documentation link for further reading: Oracle Advanced Queuing (AQ).

  • 10.1 Message passing with Oracle Advanced Queuing

    Review aq.py:

    import cx_Oracle
    import decimal
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    cur = con.cursor()
    
    BOOK_TYPE_NAME = "UDT_BOOK"
    QUEUE_NAME = "BOOKS"
    QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
    
    # Cleanup
    cur.execute(
        """begin
             dbms_aqadm.stop_queue('""" + QUEUE_NAME + """');
             dbms_aqadm.drop_queue('""" + QUEUE_NAME + """');
             dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """');
             execute immediate 'drop type """ + BOOK_TYPE_NAME + """';
             exception when others then
               if sqlcode <> -24010 then
                 raise;
               end if;
           end;""")
    
    # Create a type
    print("Creating books type UDT_BOOK...")
    cur.execute("""
            create type %s as object (
                title varchar2(100),
                authors varchar2(100),
                price number(5,2)
            );""" % BOOK_TYPE_NAME)
    
    # Create queue table and queue and start the queue
    print("Creating queue table...")
    cur.callproc("dbms_aqadm.create_queue_table",
            (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
    cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
    cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
    
    booksType = con.gettype(BOOK_TYPE_NAME)
    queue = con.queue(QUEUE_NAME, booksType)
    
    # Enqueue a few messages
    print("Enqueuing messages...")
    
    BOOK_DATA = [
        ("The Fellowship of the Ring", "Tolkien, J.R.R.", decimal.Decimal("10.99")),
        ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.",
                decimal.Decimal("7.99"))
    ]
    
    for title, authors, price in BOOK_DATA:
        book = booksType.newobject()
        book.TITLE = title
        book.AUTHORS = authors
        book.PRICE = price
        print(title)
        queue.enqOne(con.msgproperties(payload=book))
        con.commit()
    
    # Dequeue the messages
    print("\nDequeuing messages...")
    queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
    while True:
        props = queue.deqOne()
        if not props:
            break
        print(props.payload.TITLE)
        con.commit()
    
    print("\nDone.")
    

    This file sets up Advanced Queuing using Oracle's DBMS_AQADM package. The queue is used for passing Oracle UDT_BOOK objects. The file uses AQ interface features enhanced in cx_Oracle 7.2.

    Run the file:

    python aq.py

    The output shows messages being queued and dequeued.

    To experiment, split the code into three files: one to create and start the queue, and two other files to queue and dequeue messages. Experiment running the queue and dequeue files concurrently in separate terminal windows.

    Try removing the commit() call in aq-dequeue.py. Now run aq-enqueue.py once and then aq-dequeue.py several times. The same messages will be available each time you try to dequeue them.

    Change aq-dequeue.py to commit in a separate transaction by changing the "visibility" setting:

    queue.deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
    

    This gives the same behavior as the original code.

    Now change the options of enqueued messages so that they expire from the queue if they have not been dequeued after four seconds:

    queue.enqOne(con.msgproperties(payload=book, expiration=4))
    

    Now run aq-enqueue.py and wait four seconds before you run aq-dequeue.py. There should be no messages to dequeue.

    If you are stuck, look in the solutions directory at the aq-dequeue.py, aq-enqueue.py and aq-queuestart.py files.

11. Simple Oracle Document Access (SODA)

Simple Oracle Document Access (SODA) is a set of NoSQL-style APIs. Documents can be inserted, queried, and retrieved from Oracle Database. By default, documents are JSON strings. SODA APIs exist in many languages. Documentation link for further reading: Simple Oracle Document Access (SODA).

  • 11.1 Inserting JSON Documents

    Review soda.py:

    import cx_Oracle
    import db_config
    
    con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    
    soda = con.getSodaDatabase()
    
    # Explicit metadata is used for maximum version portability
    metadata = {
                   "keyColumn": {
                       "name":"ID"
                   },
                   "contentColumn": {
                       "name": "JSON_DOCUMENT",
                       "sqlType": "BLOB"
                   },
                   "versionColumn": {
                       "name": "VERSION",
                       "method": "UUID"
                   },
                   "lastModifiedColumn": {
                       "name": "LAST_MODIFIED"
                   },
                   "creationTimeColumn": {
                       "name": "CREATED_ON"
                   }
               }
    
    collection = soda.createCollection("friends", metadata)
    
    content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}}
    
    doc = collection.insertOneAndGet(content)
    key = doc.key
    
    doc = collection.find().key(key).getOne()
    content = doc.getContent()
    print('Retrieved SODA document dictionary is:')
    print(content)
    

    soda.createCollection() will create a new collection, or open an existing collection, if the name is already in use. (Due to a change in the default "sqlType" storage for Oracle Database 21c, the metadata is explicitly stated to use a BLOB column. This lets the example run with different client and database versions).

    insertOneAndGet() inserts the content of a document into the database and returns a SODA Document Object. This allows access to meta data such as the document key. By default, document keys are automatically generated.

    The find() method is used to begin an operation that will act upon documents in the collection.

    content is a dictionary. You can also get a JSON string by calling doc.getContentAsString().

    Run the file:

    python soda.py

    The output shows the content of the new document.

  • 11.2 Searching SODA Documents

    Extend soda.py to insert some more documents and perform a find filter operation:

    myDocs = [
        {'name': 'Gerald', 'age': 21, 'address': {'city': 'London'}},
        {'name': 'David', 'age': 28, 'address': {'city': 'Melbourne'}},
        {'name': 'Shawn', 'age': 20, 'address': {'city': 'San Francisco'}}
    ]
    collection.insertMany(myDocs)
    
    filterSpec = { "address.city": "Melbourne" }
    myDocuments = collection.find().filter(filterSpec).getDocuments()
    
    print('Melbourne people:')
    for doc in myDocuments:
        print(doc.getContent()["name"])
    

    Run the script again:

    python soda.py

    The find operation filters the collection and returns documents where the city is Melbourne. Note the insertMany() method is currently in preview.

    SODA supports query by example (QBE) with an extensive set of operators. Extend soda.py with a QBE to find documents where the age is less than 25:

    filterSpec = {'age': {'$lt': 25}}
    myDocuments = collection.find().filter(filterSpec).getDocuments()
    
    print('Young people:')
    for doc in myDocuments:
        print(doc.getContent()["name"])
    

    Running the script displays the names.

Summary

In this tutorial, you have learned how to:

  • Create connections
  • Use cx_Oracle connection pooling and Database Resident Connection Pooling
  • Execute queries and fetch data
  • Use bind variables
  • Use PL/SQL stored functions and procedures
  • Extend cx_Oracle classes
  • Use Oracle Advanced Queuing
  • Use the "SODA" document store API

For further reading see the cx_Oracle documentation.

Appendix: Python Primer

Python is a dynamically typed scripting language. It is most often used to run command-line scripts but is also used for web applications and web services.

Running Python

You can either:

  • Create a file of Python commands, such as myfile.py. This can be run with:

    python myfile.py
  • Alternatively run the Python interpreter by executing the python command in a terminal, and then interactively enter commands. Use Ctrl-D to exit back to the operating system prompt.

When you run scripts, Python automatically creates bytecode versions of them in a folder called __pycache__. These improve performance of scripts that are run multiple times. They are automatically recreated if the source file changes.

Indentation

Whitespace indentation is significant in Python. When copying examples, use the same column alignment as shown. The samples in this lab use spaces, not tabs.

The following indentation prints 'done' once after the loop has completed:

for i in range(5):
    print(i)
print('done')

But this indentation prints 'done' in each iteration:

for i in range(5):
    print(i)
    print('done')

Strings

Python strings can be enclosed in single or double quotes:

'A string constant'
"another constant"

Multi line strings use a triple-quote syntax:

"""
SELECT *
FROM EMP
"""

Variables

Variables do not need types declared:

count = 1
ename = 'Arnie'

Comments

Comments are either single line:

# a short comment

They can be multi-line using the triple-quote token to create a string that does nothing:

"""
a longer
comment
"""

Printing

Strings and variables can be displayed with a print() function:

print('Hello, World!')
print('Value:', count)

Data Structures

Associative arrays are called 'dictionaries':

a2 = {'PI':3.1415, 'E':2.7182}

Ordered arrays are called 'lists':

a3 = [101, 4, 67]

Lists can be accessed via indexes.

print(a3[0])
print(a3[-1])
print(a3[1:3])

Tuples are like lists but cannot be changed once they are created. They are created with parentheses:

a4 = (3, 7, 10)

Individual values in a tuple can be assigned to variables like:

v1, v2, v3 = a4

Now the variable v1 contains 3, the variable v2 contains 7 and the variable v3 contains 10.

The value in a single entry tuple like "(13,)"can be assigned to a variable by putting a comma after the variable name like:

v1, = (13,)

If the assignment is:

v1 = (13,)

then v1 will contain the whole tuple "(13,)"

Objects

Everything in Python is an object. As an example, given the of the list a3 above, the append() method can be used to add a value to the list.

a3.append(23)

Now a3 contains [101, 4, 67, 23]

Flow Control

Code flow can be controlled with tests and loops. The if/elif/else statements look like:

if v == 2 or v == 4:
    print('Even')
elif v == 1 or v == 3:
    print('Odd')
else:
    print('Unknown number')

This also shows how the clauses are delimited with colons, and each sub block of code is indented.

Loops

A traditional loop is:

for i in range(10):
    print(i)

This prints the numbers from 0 to 9. The value of i is incremented in each iteration.

The 'for' command can also be used to iterate over lists and tuples:

a5 = ['Aa', 'Bb', 'Cc']
for v in a5:
    print(v)

This sets v to each element of the list a5 in turn.

Functions

A function may be defined as:

def myfunc(p1, p2):
    "Function documentation: add two numbers"
    print(p1, p2)
    return p1 + p2

Functions may or may not return values. This function could be called using:

v3 = myfunc(1, 3)

Function calls must appear after their function definition.

Functions are also objects and have attributes. The inbuilt __doc__ attribute can be used to find the function description:

print(myfunc.__doc__)

Modules

Sub-files can be included in Python scripts with an import statement.

import os
import sys

Many predefined modules exist, such as the os and the sys modules.

Resources

Copyright © 2017, 2021, Oracle and/or its affiliates. All rights reserved
python-cx_Oracle-8.3.0/samples/tutorial/aq.py000066400000000000000000000045631414105416400212320ustar00rootroot00000000000000#------------------------------------------------------------------------------ # aq.py (Section 10.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import decimal import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() BOOK_TYPE_NAME = "UDT_BOOK" QUEUE_NAME = "BOOKS" QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" # Cleanup cur.execute( """begin dbms_aqadm.stop_queue('""" + QUEUE_NAME + """'); dbms_aqadm.drop_queue('""" + QUEUE_NAME + """'); dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """'); execute immediate 'drop type """ + BOOK_TYPE_NAME + """'; exception when others then if sqlcode <> -24010 then raise; end if; end;""") # Create a type print("Creating books type UDT_BOOK...") cur.execute(""" create type %s as object ( title varchar2(100), authors varchar2(100), price number(5,2) );""" % BOOK_TYPE_NAME) # Create queue table and queue and start the queue print("Creating queue table...") cur.callproc("dbms_aqadm.create_queue_table", (QUEUE_TABLE_NAME, BOOK_TYPE_NAME)) cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME)) cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,)) booksType = con.gettype(BOOK_TYPE_NAME) queue = con.queue(QUEUE_NAME, booksType) # Enqueue a few messages print("Enqueuing messages...") BOOK_DATA = [ ("The Fellowship of the Ring", "Tolkien, J.R.R.", decimal.Decimal("10.99")), ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.", decimal.Decimal("7.99")) ] for title, authors, price in BOOK_DATA: book = booksType.newobject() book.TITLE = title book.AUTHORS = authors book.PRICE = price print(title) queue.enqOne(con.msgproperties(payload=book)) con.commit() # Dequeue the messages print("\nDequeuing messages...") queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT while True: props = queue.deqOne() if not props: break print(props.payload.TITLE) con.commit() print("\nDone.") python-cx_Oracle-8.3.0/samples/tutorial/bind_insert.py000066400000000000000000000016041414105416400231220ustar00rootroot00000000000000#------------------------------------------------------------------------------ # bind_insert.py (Section 4.2 and 4.3) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() rows = [ (1, "First" ), (2, "Second" ), (3, "Third" ), (4, "Fourth" ), (5, "Fifth" ), (6, "Sixth" ), (7, "Seventh" ) ] cur.executemany("insert into mytab(id, data) values (:1, :2)", rows) # Now query the results back cur2 = con.cursor() cur2.execute('select * from mytab') res = cur2.fetchall() print(res) python-cx_Oracle-8.3.0/samples/tutorial/bind_insert.sql000066400000000000000000000013441414105416400232720ustar00rootroot00000000000000------------------------------------------------------------------------------- -- bind_insert.sql (Section 4.2) ------------------------------------------------------------------------------- /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ set echo off @@db_config.sql set echo on connect &user/&pw@&connect_string begin execute immediate 'drop table mytab'; exception when others then if sqlcode not in (-00942) then raise; end if; end; / create table mytab (id number, data varchar2(20), constraint my_pk primary key (id)); exit python-cx_Oracle-8.3.0/samples/tutorial/bind_query.py000066400000000000000000000013371414105416400227660ustar00rootroot00000000000000#------------------------------------------------------------------------------ # bind_query.py (Section 4.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() sql = "select * from dept where deptno = :id order by deptno" cur.execute(sql, id = 20) res = cur.fetchall() print(res) cur.execute(sql, id = 10) res = cur.fetchall() print(res) python-cx_Oracle-8.3.0/samples/tutorial/bind_sdo.py000066400000000000000000000032641414105416400224070ustar00rootroot00000000000000#------------------------------------------------------------------------------ # bind_sdo.py (Section 4.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() # Create table cur.execute("""begin execute immediate 'drop table testgeometry'; exception when others then if sqlcode <> -942 then raise; end if; end;""") cur.execute("""create table testgeometry ( id number(9) not null, geometry MDSYS.SDO_GEOMETRY not null)""") # Create and populate Oracle objects typeObj = con.gettype("MDSYS.SDO_GEOMETRY") elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY") obj = typeObj.newobject() obj.SDO_GTYPE = 2003 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject() obj.SDO_ELEM_INFO.extend([1, 1003, 3]) obj.SDO_ORDINATES = ordinateTypeObj.newobject() obj.SDO_ORDINATES.extend([1, 1, 5, 7]) print("Created object", obj) # Add a new row print("Adding row to table...") cur.execute("insert into testgeometry values (1, :obj)", obj = obj) print("Row added!") # (Change below here) # Query the row print("Querying row just inserted...") cur.execute("select id, geometry from testgeometry"); for row in cur: print(row) python-cx_Oracle-8.3.0/samples/tutorial/clob.py000066400000000000000000000020441414105416400215400ustar00rootroot00000000000000#------------------------------------------------------------------------------ # clob.py (Section 7.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() print("Inserting data...") cur.execute("truncate table testclobs") longString = "" for i in range(5): char = chr(ord('A') + i) longString += char * 250 cur.execute("insert into testclobs values (:1, :2)", (i + 1, "String data " + longString + ' End of string')) con.commit() print("Querying data...") cur.execute("select * from testclobs where id = :id", {'id': 1}) (id, clob) = cur.fetchone() print("CLOB length:", clob.size()) clobdata = clob.read() print("CLOB data:", clobdata) python-cx_Oracle-8.3.0/samples/tutorial/clob_string.py000066400000000000000000000024051414105416400231270ustar00rootroot00000000000000#------------------------------------------------------------------------------ # clob_string.py (Section 7.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() print("Inserting data...") cur.execute("truncate table testclobs") longString = "" for i in range(5): char = chr(ord('A') + i) longString += char * 250 cur.execute("insert into testclobs values (:1, :2)", (i + 1, "String data " + longString + ' End of string')) con.commit() def OutputTypeHandler(cursor, name, defaultType, size, precision, scale): if defaultType == cx_Oracle.CLOB: return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize) con.outputtypehandler = OutputTypeHandler print("Querying data...") cur.execute("select * from testclobs where id = :id", {'id': 1}) (id, clobdata) = cur.fetchone() print("CLOB length:", len(clobdata)) print("CLOB data:", clobdata) python-cx_Oracle-8.3.0/samples/tutorial/connect.py000066400000000000000000000011041414105416400222460ustar00rootroot00000000000000#------------------------------------------------------------------------------ # connect.py (Section 1.1 and 1.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) print("Database version:", con.version) python-cx_Oracle-8.3.0/samples/tutorial/connect_drcp.py000066400000000000000000000012441414105416400232630ustar00rootroot00000000000000#------------------------------------------------------------------------------ # connect_drcp.py (Section 2.3 and 2.5) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn + ":pooled", cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF) print("Database version:", con.version) python-cx_Oracle-8.3.0/samples/tutorial/connect_pool.py000066400000000000000000000022031414105416400233000ustar00rootroot00000000000000#------------------------------------------------------------------------------ # connect_pool.py (Section 2.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import threading import db_config pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn, min = 2, max = 5, increment = 1, threaded = True, getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT) def Query(): con = pool.acquire() cur = con.cursor() for i in range(4): cur.execute("select myseq.nextval from dual") seqval, = cur.fetchone() print("Thread", threading.current_thread().name, "fetched sequence =", seqval) thread1 = threading.Thread(name='#1', target=Query) thread1.start() thread2 = threading.Thread(name='#2', target=Query) thread2.start() thread1.join() thread2.join() print("All done!") python-cx_Oracle-8.3.0/samples/tutorial/connect_pool2.py000066400000000000000000000023041414105416400233640ustar00rootroot00000000000000#------------------------------------------------------------------------------ # connect_pool2.py (Section 2.2 and 2.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import threading import db_config pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn, min = 2, max = 5, increment = 1, threaded = True, getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT) def Query(): con = pool.acquire() cur = con.cursor() for i in range(4): cur.execute("select myseq.nextval from dual") seqval, = cur.fetchone() print("Thread", threading.current_thread().name, "fetched sequence =", seqval) numberOfThreads = 2 threadArray = [] for i in range(numberOfThreads): thread = threading.Thread(name = '#' + str(i), target = Query) threadArray.append(thread) thread.start() for t in threadArray: t.join() print("All done!") python-cx_Oracle-8.3.0/samples/tutorial/db_config.py000066400000000000000000000031541414105416400225360ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import sys import os import getpass ###################################################################### # # Oracle Client library configuration # # On Linux this must be None. # Instead, the Oracle environment must be set before Python starts. instant_client_dir = None # On Windows, if your database is on the same machine, comment these lines out # and let instant_client_dir be None. Otherwise, set this to your Instant # Client directory. Note the use of the raw string r"..." so backslashes can # be used as directory separators. if sys.platform.startswith("win"): instant_client_dir = r"c:\oracle\instantclient_19_10" # On macOS (Intel x86) set the directory to your Instant Client directory if sys.platform.startswith("darwin"): instant_client_dir = os.environ.get("HOME")+"/Downloads/instantclient_19_8" # This can be called at most once per process. if instant_client_dir is not None: cx_Oracle.init_oracle_client(lib_dir=instant_client_dir) ###################################################################### # # Tutorial credentials and connection string. # Environment variable values are used, if they are defined. # user = os.environ.get("PYTHON_USER", "pythonhol") dsn = os.environ.get("PYTHON_CONNECT_STRING", "localhost/orclpdb1") pw = os.environ.get("PYTHON_PASSWORD") if pw is None: pw = getpass.getpass("Enter password for %s: " % user) python-cx_Oracle-8.3.0/samples/tutorial/db_config.sql000066400000000000000000000003421414105416400227010ustar00rootroot00000000000000-- Default database username def user = "pythonhol" -- Default database connection string def connect_string = "localhost/orclpdb1" -- Prompt for the password accept pw char prompt 'Enter database password for &user: ' hide python-cx_Oracle-8.3.0/samples/tutorial/drcp_query.sql000066400000000000000000000015421414105416400231470ustar00rootroot00000000000000------------------------------------------------------------------------------- -- drcp_query.sql (Section 2.4 and 2.5) ------------------------------------------------------------------------------- /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ set echo off verify off feedback off linesize 80 pagesize 1000 accept pw char prompt 'Enter database password for SYSTEM: ' hide accept connect_string char prompt 'Enter database connection string: ' -- Connect to the CDB to see pool statistics connect system/&pw@&connect_string col cclass_name format a40 -- Some DRCP pool statistics select cclass_name, num_requests, num_hits, num_misses from v$cpool_cc_stats; exit python-cx_Oracle-8.3.0/samples/tutorial/plsql_func.py000066400000000000000000000011441414105416400227670ustar00rootroot00000000000000#------------------------------------------------------------------------------ # plsql_func.py (Section 5.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() res = cur.callfunc('myfunc', int, ('abc', 2)) print(res) python-cx_Oracle-8.3.0/samples/tutorial/plsql_func.sql000066400000000000000000000015771414105416400231500ustar00rootroot00000000000000------------------------------------------------------------------------------- -- plsql_func.sql (Section 5.1) ------------------------------------------------------------------------------- /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ set echo off @@db_config.sql set echo on connect &user/&pw@&connect_string begin execute immediate 'drop table ptab'; exception when others then if sqlcode not in (-00942) then raise; end if; end; / create table ptab (mydata varchar(20), myid number); create or replace function myfunc(d_p in varchar2, i_p in number) return number as begin insert into ptab (mydata, myid) values (d_p, i_p); return (i_p * 2); end; / show errors exit python-cx_Oracle-8.3.0/samples/tutorial/plsql_proc.py000066400000000000000000000011751414105416400230030ustar00rootroot00000000000000#------------------------------------------------------------------------------ # plsql_proc.py (Section 5.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() myvar = cur.var(int) cur.callproc('myproc', (123, myvar)) print(myvar.getvalue()) python-cx_Oracle-8.3.0/samples/tutorial/plsql_proc.sql000066400000000000000000000011711414105416400231460ustar00rootroot00000000000000------------------------------------------------------------------------------- -- plsql_proc.sql (Section 5.2) ------------------------------------------------------------------------------- /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ set echo off @@db_config.sql set echo on connect &user/&pw@&connect_string create or replace procedure myproc(v1_p in number, v2_p out number) as begin v2_p := v1_p * 2; end; / show errors exit python-cx_Oracle-8.3.0/samples/tutorial/query.py000066400000000000000000000010321414105416400217620ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query.py (Section 1.3 and 1.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) python-cx_Oracle-8.3.0/samples/tutorial/query2.py000066400000000000000000000013561414105416400220550ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query2.py (Section 3.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute('select * from dept order by deptno') for deptno, dname, loc in cur: print("Department number: ", deptno) print("Department name: ", dname) print("Department location:", loc) python-cx_Oracle-8.3.0/samples/tutorial/query_arraysize.py000066400000000000000000000014471414105416400240650ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query_arraysize.py (Section 3.5) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import time import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) start = time.time() cur = con.cursor() cur.prefetchrows = 100 cur.arraysize = 100 cur.execute("select * from bigtab") res = cur.fetchall() # print(res) # uncomment to display the query results elapsed = (time.time() - start) print(elapsed, "seconds") python-cx_Oracle-8.3.0/samples/tutorial/query_arraysize.sql000066400000000000000000000015111414105416400242240ustar00rootroot00000000000000------------------------------------------------------------------------------- -- query_arraysize.sql (Section 3.5) ------------------------------------------------------------------------------- /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ set echo off @@db_config.sql set echo on connect &user/&pw@&connect_string begin execute immediate 'drop table bigtab'; exception when others then if sqlcode not in (-00942) then raise; end if; end; / create table bigtab (mycol varchar2(20)); begin for i in 1..20000 loop insert into bigtab (mycol) values (dbms_random.string('A',20)); end loop; end; / show errors commit; exit python-cx_Oracle-8.3.0/samples/tutorial/query_many.py000066400000000000000000000012111414105416400230050ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query_many.py (Section 3.3) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute("select * from dept order by deptno") res = cur.fetchmany(numRows = 3) print(res) python-cx_Oracle-8.3.0/samples/tutorial/query_one.py000066400000000000000000000012351414105416400226300ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query_one.py (Section 3.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute("select * from dept order by deptno") row = cur.fetchone() print(row) row = cur.fetchone() print(row) python-cx_Oracle-8.3.0/samples/tutorial/query_scroll.py000066400000000000000000000014071414105416400233460ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query_scroll.py (Section 3.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor(scrollable = True) cur.execute("select * from dept order by deptno") cur.scroll(2, mode = "absolute") # go to second row print(cur.fetchone()) cur.scroll(-1) # go back one row print(cur.fetchone()) python-cx_Oracle-8.3.0/samples/tutorial/resources/000077500000000000000000000000001414105416400222615ustar00rootroot00000000000000python-cx_Oracle-8.3.0/samples/tutorial/resources/base.css000066400000000000000000000015341414105416400237100ustar00rootroot00000000000000body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; line-height: 1.5; color: #333; background-color: #fff; } #container { border: 2px solid #078311; min-height: 103%; } h1 { background-color: #078311; color: white; font-size: 2.45em; margin: 0; padding: .3em; } h2 { color: #078311; margin-bottom: .5em; } hr { color: #078311; } #menu { background-color: #00C029; padding: .5em; margin-bottom: .5em; } #menu a { text-decoration: none; color: blue } #menu a:hover { text-decoration: underline } #menu a:visited { color: blue } #content { margin: 1em; } .logo { float: right; } pre { background: #E0E0E0; } python-cx_Oracle-8.3.0/samples/tutorial/resources/community-py-200.png000066400000000000000000000215201414105416400257400ustar00rootroot00000000000000‰PNG  IHDRÈÈ­X®žbKGDÿÿÿ ½§“ pHYs  šœtIMEáÛkþU IDATxÚí{\Õ}ç?çÜÛ·_óF# !‰‡в…±co#ŠÀºmAqlªv+áìªÄùƒÊVífÚTÊáae6ë5¸0Þ*0ÆżD,¶1XÂÈ–dÐû5šÑÌôûÞ{Îþq»{zfn÷ôÌôôt÷œoU—FÓsß¿Ïýý~çñ;`dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddÔL=ºc§¹ F “hwî¸ycµïº€¥@OñßÐD€$`£«îZÀF0 Œ#wܲq8ô˜?ÜÉ·l4VeYXï0ŠGwì\ ¬®.~.*ÂâE ¢Å]¾n=í! @ºøsH§€ýÀàí;nÙ¸×cY=¶ã%>só •@¬þøÀõEÄŒ®I7äÔJ{Ñ™ô˜RJívßÙzûæ_”þè¡o?ÍÖÛ7k3€4†Çv¼”.®>©Ñ›þ†˜u¤”"—M£”šü• üøðº”òô·}rÈ@ciíx銢‡ØlVêFXvCáðÉe3apLV xø1°cëí›_5¦g™;ùL1¯xlÇKþ;ðA °»n!8„À÷\²™Ìlvz8ü¯­·oþ6ÀCO<ÍÖ-Æ›@ªB±Ó€LµëÖòžë’Ëf±«#Àç€Ûºe³k`1€”’ìrKÔc;v~¸ ø³p»n-8\×%ß8*õSàg¶nÙ¬ $‹ÿwÇΘ„/ ¸XnÓ­‡‚|.G!ŸŸ¯[S(&õwoݲùØCO< `@YL€”ªÇvì¼ø^1Ï u×–ÍOn{âiãE:GwìD€CЄûíà9”Rd3ôô€ó GIܵeóic®bÝqóF4\ªá/ôx1飩þ]Íœ÷àû>¹ÖààÓÆT;;¹[ÝÕäOÉ<Ä\>zŽÛR<Ï#—©kèH³à(é¶=ñôc®Í—Ý$#ø“V«ÜBB.‡ÖºÕàøÐM0LŨÓAÓ×ÊpxÞ¼ôŽ7òÔ-‚‰^Fb½±ï7½- ‡úz’µgÉÍÐ8}ß'•N-8ZJT$BâôiVîù½ï"64Ô0HR¹ëΟûyTù¹9¾­õÖ›¯í%p˜`QÓ'ÅîÝÇôµ×"vïîL@ö½wté+¿Ü7äùŠu«–ãØvÃŒs* Gï‘üï;OÒ÷›(Ëjè\ß÷ñ•fIÌ)H!ô‚2pL¶Kxø[±{÷/ ”y±F3–vwqøÔYNž;Ïšå}Ø–œxŸÚ0¬ÒBà¤Æ¸ø¹±öÅÐB ìÆÞNßW(¥‘®}à(=‘<ðqàM}íµ_þQìÞ=¨{ötF’AeÅžD0bd,ÃÑÓç(¸þøCoG8,‹øðÿúk_xeÛèÙÖüª ‡ iÎö‚£R.Á2t_žÒŠ={ЀeIV,éAJA*›ãЉ3¤sùÄwzÁ<ǼŸ%‡¢"‘Ð3šËÇóü2B b1Â1Ñ›h}=ð’¾öÚ¥bÏôúõHé¡/éé¢'®Öóx÷ø†Æ2øzn˯ÍzC5»í¤§¸æëÿJlx¨¡-UiÜ‚‡*NªÒÅ—Ë"†£r ø-”ú–^¿Þo½Õ9¤¤5+–Ñ“ŒC͵æÈ©AŽœd,4Î4mFàžÙoüŒ%ï¾Cyh¯ÿ”Q×3ûh¥ð=Ïõ‚í•B”_€q†Ÿ¢šÓg®û˜s€Ÿê2¡iBÖ,_ÆŠ¥=¨â¨±LŽ£§yïÔYÆÒY,)ç”9Àa ¬yeÒóÆ'”œ‹˜ùþµr ÏSãeQµFafG9 ŸdBlÛ2pT߇|¸\¼õÖ/:I×Û¿¤›D,ʉ³Ãd ”Ö Wp9tâ Q'B_w’îdœˆÌï.Ï;o6€²,Výì§ASnå¾ê„#(ÖÒVJ‡ñ‡”rq'ä3Ñg€Dӫɧ4ñ¨Ã%«W0’Êpn4E*“E”‚‚çqrè<§‡GHĢģñ¨C"ê‹FŠû•‘μÁ ¤¤߯ÐÒªÛsh¥Ë+k…AQ ;¥‹¹)w6ûð›šAáü¢j¿Uz»t'âdóN 'Í#*Œ&“Ë“ÉåƒØ\ ,i‘ˆ;$œ(UNfKnÝ ©‹¯ø®Çñ#NU8´ÖãPLšþzóm«1C¥0ëªÎ¤ÞÖ)HÆ£\¶æ2¹Ï™ÁA²¹™\×ó‚m* ¸•êâÎ1¥•©¢³*!@L·¶‡£màh @ª½¡!Xˆ§¯§ÑX°òù‚KÞs)¸>ÏÅõ||_•‡nLLâ§Åb<,*1;æ ‹¹Ré!™R͇>§U~µÃ2ó0¨løÅfÔX4B,ꌛm&¿X]±²Éµ4䥲EJ{°e)L)%*CXböË1ÏÑvpt SS]L õ¤‡!X¢hÜrºŒ#$'Rza2(goàh2df1<æ¹4O«¼-; Ž& lm b^Ÿ¹îp8×ð‘–‚£)€(¥Fæå>-ÏÑI9G£öÓäE’æM×]ë8LBÞŽpÌ; ßÜñÙ|!ݨk[”phóÑٺЀ|úæ<âÓß/xðA›+®ÄãË÷!›…·ßV|ñ‹ƒƒ:6σ[oµøüç-²Ùð~óMÍßüMø’Ô¾«V ¶oÐÕEèùž< üÇgöp4Q È„ò¶BŒÇ¸ü¢ Içrœõ)—ƒ'B"¹Ó§±ÖѵkEÕã­Z%8sFO £”‚÷½OÔò#;wª¾jËÕµ@αàDOkÁÛ8îDˆG—àËŒæ .©lP=±àºEï '<@Á *0Nµ Y®ÈXúÛñm'Ü+a·¦à8(¥ð•ªÛ¨(epŒ À'>aÎì!ôô¶nµ¸÷^I÷*®”Nÿä'*ºvm¸/] +WVß÷Å ª,fêçr8ï¼£«æ'a`, MDÏôo‹‹ì”*»/éN‚Ö<ŸÑt–‘TŠ|.¬³QY,.äÞ`v)½ÔU}}¼L˜[x7ƒˆÇgwŒßý]ÉÅ ˜p0áºè7ßÔÜ~{õm/»Lò jJ«Z"Q=ro½¥ca¹MØõ6²Dk;ÒˆË,q‹FlV,í¡¿'ÉÊžDÙÃäò.¹B!ÇT &‡C“ÿ­fèãÏH[™d1Ä›ž/9±àŨu¡@~ÿ~ÿå/•ˆÅDbÃa¯^-¨ï÷ö 6l8àO¾ÇöÁƒÚÔv¸±ÿöo^¢P˜ì]‚ð«Ú9þêWJd2ºÕê¦2—©+ L;¶‰Ð› ntPÇ*¨¨èû ¯Øï *š[•ÒáýšòÒ ²bíH`YmÛáÝ! ÜwöþÁ?ÿ裾UlîÕ}}¬üÒ—ìîO|¢zF¼q£äñÇýÉÇ•GŽhÿäIìþþðíÞÿ~AXÑÓË—WiÙóû÷kẔ·mq8šˆn€¥ŽÄâ•B"mIÄ®ð>á?Ô>ób>âÇ‚žÿV„#ý *û­oùk“Im-續<òˆÿЇ„½|yøÅnØ ÃÎAjÿw4W_-Bó…•+ÝÝ‚Lfb>ÑÝ-X¶,ôXþÐò7¿ÑåÕzÃ[XD…ãÊ+9—LÕÓ|Æ›pk‚ÞC7RÏ<£.ŠFuTJl!°… Ë¶éÞ»Wù§O×nÑZ¹RLîü‹Xî+¯(]«Aâºë“Gc_~yõ·ÎùóZ¿û®® @#k· ¢½±ªaÔÍ€CëÚ§Ä÷ï×a ¥P{÷Ö>À5׈É=Û¶Q/¿¬ðýZ€È Û W^YõYûᆱísçt͇-8«±€xÇ\×üžÍ:âÓÝÜÓ§IxžÛÖ¶,ô{ïÕn—^»V–‡©TÔvŽÓ…ýû«ŸÀG?jQ(LÜîýï¯jO¹_TNesôäkœÍýë@~ÃÀhGyŽ «ÊÖÒu«†PÖ±cµ´zuè ÈD4Jú‡?¬׊°jÕÄm¯¾ºz ÖË/«jý#­<¾Y9È™y1cÝD8t ÂÈ\Q#’ccµwHˆÐóqô÷¾WÛû|ðƒž@²fMU8Ü£G‰¼÷ž®:ز…Õ@4|KW&Ê ú4>êÑÕ¿kA8‚&¬´Æóªú£Ó8ðþþp@¤$vâ„Ê>\ý„¯¿>È_<>ô¡ª€¤ð?Ö¡Ù•TšåA¤æ‚ÐmVµ¨Ë“Z#j ?¹\íƒÖÞžˆFÉ<ûlõç¶aƒÄó4žø€UíÏÔ~ä‹H¤±÷¢Ãf¶8f§Zc§[ƒÑšÃ!%º Ë–ÁêÕAkÖïüNxÿG&ƒ}à:Û¨†WSÙ´aÀ¾ ì5pÌLQËòÄ\êbÍqv_lpÂÉ“á_Æã‚+®tuCîC”yùe•¬¼ÉmV ®)€<ÿú6mxø pÒÀQŸb–åÚR¨šû°m¨5½»»&jlLׂÄUîÛo‡{‘X Ö­\sMU;R?ù‰²é9š XÓzÒ‹ü¸8nਭ¸m¹švñ¸À²DÕãW™¸T6àÑÑÚ^$•BWël´í`‚T•æ]åyX¿þµn·°j!B,6m(Ar X¼´p‰{ëÂ!@'léÚõÞ›®.ªö/(Usø9@Õé·9Œxûmå§Rá°fàc Ý{ø°Žœ=«Û¹npSÇbU@2Üü=p¢}ਲ਼Z+&4FçsÕ?ž[ÞwDà'm˵tÑsÔR,]gë¥÷ýÚ“›uꔞ¾Lú!Ðo¿­õØXø‰\r‰`` ÔŽôáÃZž9£çm´AÔô…€ÿùüë{ž¶?-î9ªŽçô¶«ø›Bçο$t\“eáïß»v©¨ñ-!ÔŒ|_$“áo\Ï«=€PasÓ'%ñöÉ“Úçm‡Íõ¸ä’ð¿J¡~ýkm§ÓÔ,ÐТžcÁ™ ›6 ìîzþõ=÷4ojE8êzVaóïs9¢_üÛê³æžúŽâ¥.™½A Ÿ&”íîfÿÕ¿wom@€¨m3òÜs*JU ÏT>zë-eKÙ¶p4=Äš ɤþÀ¦ 7ׇhØ@ÇFÁQoXÒç_š_]ù ˜ZzÛm)'z)ß:ò.¸ êfîÐöȈž®©WD"è_œY¾˜ÏÃîÝÓÂ×êj‰ù %XŠ ¼½iÃÀŸÿ|8ÖžC„tõïfëyf¤«¯–Ü{¯M¿`tR)Íúõ‚{î±éí­>GüÕW•SOG¢”DÒùƒë>QïÐ!m=ªg\"´ÅV¨²[‰ÖI¡×aà±ç_ßóÿ€àràVà6`MK‡UÅ7-„²-Ë"óJlÙb±i“$— ¼SWµàpŸ}V‰:ß‹1ôÌ3*zÏ=u¹„Ì‹/ª%3Í=é|Y‡^Åÿç³›6 ¼²iÃÀ6m¸¸ø¯¾SlK9À¥r®lÃá«Dñc TT /iI·Ë¶ò1Kº¶ÀoÚÍëï¬Y#X½ZLGþàAmO7™ªRŽƒúÁê³ô /ø3JÎ[´‡Ý¦ T‚ÿøßÅO>÷ïWW½XZô<]À $‹×,ë|V~¾G#¤A!vâ„Î?®£«W×¼&otkÏMOO[ÃÑ6€Tó.O¿ø2›?þQn½ñ÷N=É‹¥ïž|î¥DŽD”(/^s¨ ¢B_kí Z=’¤Â¾^wʉÇN"„+&7å. ²o¼¡U*E×M7‰i—(~ŸÙµKå¶o÷—̰…)!„ÛµKG·l©yñ™^PÝõ³kñ±YmÈdmþøG§üîÉç^âÖoàÖoÈ™Fï¿]}uãèëÛ¾¯}á žï½VïŸþ©5ݱƞzJý»¿óV „ÏèT•‚×^SlÙRsC÷ûßWS†··!mH˜n½ñ†yŒgfñ@‡‘§žR¢4ï»rw¶Môç?W1{öÁ‘’ñ8§¿øE?óÚkºï³Ÿµ¬þ~D©V¯Öè|^{'N0üðÃ~æG?RËâqâRÎøb9vLyÃÃÚ^º4|x{.‡ýË_*¦ƒ¯MFõv -§D‚³wß퉣Ð@ãK&go0ZÓ%¥¶ 1øôÓþágžQ‘‹/öòåå¡î™3xᆱŽÃªD‚Ä,à()žHLVu{ÙW_UI×Õ‡¤Òšµ==ºJÇ æo &.¥^ÝÕEA)=~œÂÑ£å·~·”$z{µ-Äœ›-Åu× Q£uÊÿéOµÌfçoeÞ&Cfi ‘&®é“’Ø2߇+¯”üÓ?ÍÈ>²ßý®ŸL¥ô”%ÚÐsTze£V…c¡½ˆWÿXQïèQÿú×}§ƒà0€8æqNþùŸûKb1ÑIp˜«à˜¯x¿ÎóðÏžåÔ½÷zÑüX<^!êɳgv) ‚ I‰Du fº>a½çaÛÔ§å8Aê‡?TÃ?îGQËc±úà(­éÞÝ-¸è"ɲe’x\„®ƒ^¹¿ñæð`°t.÷cÞ}wÞ«0–=»¹ê* ä›é9|Çáìe—Y~H߃⣣ºïðaU«>ïl²K—Š¡uëädåŽj÷àA­‹Ãê»-‹å–¥e=÷BkX¶L20`±bÅxUùÊíJÿ–Šb‡}¯õ+ÀM‘_þ²ñ ï9jí>ŸG½ù¦ò«GI‰vœÆ¬ _¹vǹs¨S§”žÑ b–EÔ¶IJI¼ú(æ‰ûÅW]eqÅV¹ Älà€×Ðú6qß}ib™œ[.ˆÅª–Î [ë½ç³,½:$ίz¼jס$“‚|$ÂòåA±ëÉk‚„…Va¿‡W€ÛÄ}÷ÔŸÿ<â_þÅbòbSc“;߯הŸÃu”<ÇÇ?!™‡cvaÕ[Ä}÷¥›GùÞµAkU«^K­í¥„Ø&‘SZ¬fV½ŠÖ› ææ4 ãA óº½Z·Î’ýý²Ü¤[Ý;T ©@ëWÑúvqÿýà q‹Œ1pÌËö©hïÒK-„à¨÷ãû•ÿß…RŸ÷ßLßsXGÀ1fÙ ö-³œ¾>Q6øz¼ÇÄß¿ŒÖ xàœþë¿FÜwŸĨý!ÍKÉÉHD,¹p•œ°þúLà(5å>ð@@Üÿ‚Ý.ÈLk6K×»äs+ê¸ F¡´&±¬í|^ÑIDAT_”û:JÛ„…TS¿ß¿M<øà)}÷Ý þÈ õpq饳߸ò-Ú pÏãuä…$--P§»K„z©Ixåï~ Ü,¾ò•ãú¯þ ñÀ ~ÛL+V³ó³HrŽrîaÛãÝêµó‹0h_> Œˆl‰[g1 yöO[6‚ Öä”P¬Z¿†Ö·‹mÛFZí1škf!VŠzx.B8J ú„m'ç“sŽàç](õ‡bÛ¶cúsŸÃÒÞ!ÖëÓzÝE Lª†\_α øñÐCgõ]w!¾òH›ë©š÷lÃ1ñâA&·ViýJÝ*¶m šr·mkÉn™™¾ dCÃ,ÇTQ-÷€WQêOÄßÕ[·¶ô7€Ôû̯¼à4ðmÀ1pÔ ÉÔ~“£õ-âá‡é­[=déˆÄ¾}àkT®Ìkà˜˜„„õ”Ãò J}Z0ͼ³å5}å•ÖÊÀQ™¡WÜŽRþ1¾ÿ×Pêv±}ûX;=oãAff!öíû´þZG|È\‡°4bŒRÅÕ¶ÊndêÀC¥þHlß~\ßy'³ôW”üÿî.æ# Sü¢•rް}ðínÛ·ŸÒwÞ‰øêW  Éþý¥}qàÀƒÀ-À;@`Õ*±¨à¨5Ý6h­úOâ«_ šrÛ 0ec#—_ÞM°úîÀz`ÁÒo‹ Ž_wuciRŠ+oø½W)n_ûÚ1ýÙÏ"¶ooËgk’ô¹Ã8p` ø?úòË¿I°o/Á:ˆºs/¼ú¥ !$B¼'¾öµÓí ‡‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ÑýCa"üñŠ QIEND®B`‚python-cx_Oracle-8.3.0/samples/tutorial/resources/cx_Oracle_arch.png000066400000000000000000003765131414105416400257020ustar00rootroot00000000000000‰PNG  IHDRˆ€¯ÃúU ßiCCPICC ProfileH‰•—T“É€çÿÓCB ‘NèMN)¡Pz•Jˆ AÁŽ,®àª "‚eWE\]Ь±`E±¡¢.È¢ < 6TÞ<Âî¾óÞ;ïæLæ;÷¿sËœ™sî@áJ$0€Lq¶4"À›ŸÀÄ h< ˆË“IØaa!‘éù¯òþ€&æÛV¾þýûU¾@ÆJD8™/ãe"܆ŒW<‰4ÔQDo¸4[2Áw¦K‘œàÔ)þ2ÁÉ“Œ¦NÚDEø lžÄåJS Ù zf/ñC CØF̉^ƒ°OÈå#ŒÄ³33³&xa3Ä^™Ž0+ùO>Sÿâ?YáŸËMUðT]“‚÷É$ÜÜÿskþ·dfȧc˜ ƒ$”F 3Ù¿ûéYÁ 'ÏfÒ~’…òÀèiæÉ|¦™Ïõ V¬Í˜2Í)"ŽÂO6'jš2¿Èi–fE(b¥H}ØÓÌ•ÎÄ•§G+ôBGá?O;Í9¢˜ùÓ,K ž±ñQè¥òEþq€÷L\E홲?Õ+â(Öf £µsgòˆÙ3>eqŠÜø_¿›h…½$Û[K’¦°d(ô²œHÅÚläpά Sìa7(lš/ð!È ¢pF†  [°,{¢Ÿ,I®T”*Ìf²‘'`rÄ<ëÙL;;[&îïÔ‘x1y/!Æé]Ö>ä(¿GîLÉŒ.¹ €æBÔÎèŒv@) ©'—æLéÐ@@@3`…äæÜ€’qQ ,< ™@ –‚`-(Å` Ø*ÁP‚#àh'ÁYp\7Á]ÐzÁx FÀ{0A"C4HÒƒŒ!KÈbAE@ñP” ‰!9´ZC¥P%´ª…~†N@g¡ËPôꃆ 7Ðg“`:¬›Às`̆ƒá(x!œ /óàx\WÇá&ø,|¾ ÷Â/áQ@)¡(}”Š…òA…¢P)()jªUŽªFÕ£ZQ¨Û¨^Ô0ꋦ¡™h+´:æ¡— W¡7¢+ÑÑMèóèÛè>ôú†ŒÑÆXb\1L&³Sˆ)ÇìÇ4b.`îb0ï±X,kŠuÆbã±iØåØØ]Øl¶ ÛÅáp8Kœ;.ÇÅeã q;p‡qgp·p¸x%¼ÞïOÀ‹ñùørü!üiü-üsüJ0&¸B |B.a3a¡•pƒ0@#ªM‰îÄ(bq-±‚XO¼@|D|«¤¤d ä¢®$RZ£T¡tTé’RŸÒ'’*É‚äCJ$ÉI›HHm¤¤·d2Ù„ìEN g“7‘kÉçÈOÈ•iÊÖÊe¾òjå*å&å[ʯ(Š1…MYDÉ£”SŽSnP†©ª Շʥ®¢VQOP»©£*4[•P•L•*‡T.« ªâTMTýTùªª5ªçTûi(š!͇ƣ­£í£]  бtS:‡žF/¦¡wÒGÔTÕÔbÔ–©U©Rëe & #ƒ±™qŒqñy–Î,ö,Á¬ ³êgÝšõA]KÝK] ^¤Þ ~Wý³SÃO#]£D£Yã±&ZÓB3\s©ænÍ šÃZt-7-žV‘Ö1­‡Ú°¶…v„örííkÚ£:º:::çt†uº^ºiºeº§u‡ôhzz"½2½3z/˜jL63ƒYÁ<ÏÑ×ÖÔ—ëïÕïÔ305ˆ6È7h0xlH4d¦–¶ŽéÍ3ZaTgôИ`Ì2o7î0þ`bjk²Þ¤ÙdÐTÝ”cšgZgúÈŒlæi¶Ä¬ÚìŽ9Öœežn¾Ëü¦láh!´¨²¸a [:YŠ,wYvÍÆÌv™-ž]=»ÛŠdŶʱª³ê³fX‡Xç[7[¿šc4'aNÉœŽ9ßlm2löÙôتÚÙæÛ¶Ú¾±³°ãÙUÙݱ'ÛûÛ¯¶o±í`é pØípß‘æ8Ïq½c»ãW'g'©S½Ó³‘s’óNçnÆÚȺä‚qñvYírÒå“«“k¶ë1×?ܬÜÒݹ Î5+˜»on¿»;×}¯{¯Ó#ÉãG^O}O®gµçS/C/¾×~¯çlsvû0û•··Ô»Ñûƒ«ÏJŸ6_”o€o‘o§Ÿª_´_¥ßÿTÿ:ÿ‘Ç€åm˜ÀàÀ’ÀnŽ‡Ç©åŒ9­ :L Ž ® ~b" iÏ š·uÞ£ùÆóÅó›CA('tkèã0Ó°%a¿†cÃëŸEØF¬ˆèˆ¤E.Ž<ù>Ê;jsTO´Y´<º=†“Só!Ö7¶4¶7nNÜʸ«ñšñ¢ø–\BLÂþ„Ñ~ ¶-HtL,L¼·Ðtá²…—i.ÊXtj1e1wññ$LRlÒ¡¤/ÜPn5w4™“¼3y„çÃÛÎ{É÷â—ñ‡î‚RÁó÷”Ò”ÁT÷Ô­©CBOa¹pXä#ª½N LÛ“ö!=4ý@úxFlFC&>3)ó„XUœ.>Ÿ¥›µ,«Kb))”ô.q]²mɈ4Xº_ÉÊZ²éH£tMn&ÿNÞ—ã‘S•óqiÌÒãËT–‰—]˵ÈÝû<Ï?ï§åèå¼åí+ôW¬]Ñ·’½rï*hUòªöÕ†« V¬ Xsp-qmúÚëù6ù¥ùïÖÅ®k-Ð)XSÐÿ]Àwu…Ê…ÒÂîõnë÷|þ^ô}çû ;6|+â])¶)./þ²‘·ñʶ?Tü0¾)eSçf§Í»·`·ˆ·Ü+ñ,9XªRšWÚ¿uÞÖ¦2fYQÙ»m‹·].w(ß³¸]¾½·"¤¢e‡ÑŽ-;¾T +ïVyW5ìÔÞ¹aç‡]ü]·v{í®ß£³§xÏçE?Þß°·©Ú¤º¼[“Sól_Ì¾ŽŸX?Õî×Ü_¼ÿëñÞƒÏ×:×ÖÒ>´¹®“× N<|óˆï‘–z«ú½ Œ†â£à¨ü苟“~¾w,øXûqÖñú_ŒÙÙHk,j‚šr›Fš…ͽ-ñ-]'‚N´·ºµ6þjýë“ú'«N©Ú|šxºàôø™¼3£m’¶á³©gûÛ·÷œ‹;wç|øùÎ Á.]ô¿x®ƒÝqæ’û¥“—]/Ÿ¸ÂºÒ|ÕéjÓ5Çkׯ7v:u6Ýp¾ÑrÓåfk×ܮӷ}ÔøxðëSÇçØÏÏÇ–~Á}©øjþµõ[ð·Gã™ãã®”;Ù  §¤ðæÒÇ@» qÁT=)ÐÔ›`’Àâ©|Rœ¨é j9!רQ‰´´ˆ ò.£ z7ÛÛ+Æ¿D–bo7å‹ä‰´&ÇÇßš€+àkÉøøXÍøø×$ÙÚr§úú ¡F^2,VH»ø»Lõüªñï3˜ÈÀü}þ'!=”ŸŠeXIfMM*>F(‡iN’†x ˆ €ASCIIScreenshotX¿ pHYs%%IR$ð×iTXtXML:com.adobe.xmp 1672 Screenshot 640 ÄG·iDOT@(@@ çî¥r³@IDATxìÝœEÚÀáw-+qwB È$À‚;‡»ë!‡sØÁÜqÀA r‡‚$XÐ$@ÜÝw7ëùêíÝžÌÎÎÌŽtÏtÏü›ßfZª««žªÝ„z·ªs6šMØ@@@@@@²F ‡QÖ´5E@@@@@,Dt@@@@@@ ËeYƒS]@@@@@@€}@@@@@È2DYÖàT@@@@@ @D@@@@@@²L€Q–58ÕE@@@@@Ñ@@@@@@, @”e Nu@@@@@@Dô@@@@@@ ËeYƒS]@@@@@@€}@@@@@È2DYÖàT@@@@@ @D@@@@@@²L€Q–58ÕE@@@@@Ñ@@@@@@, @”e Nu@@@@@@Dô@@@@@@ ËeYƒS]@@@@@@€}@@@@@È2DYÖàT@@@@@ @D@@@@@@²L€Q–58ÕE@@@@@Ñ@@@@@@, @”e Nu@@@@@@Dô@@@@@@ ËeYƒS]@@@@@@€}@@@@@È2DYÖàT@@@@@ @D@@@@@@²L€Q–58ÕE@@@@@Ñ@@@@@@, @”e Nu@@@@@@Dô@@@@@@ ËeYƒS]@@@@@@€}@@@@@È2DYÖàT@H—Àºò ëÑó—¯Ý·¾ÊêÏéûºý™®rò\@ðmJŠ]°Û´¬?ß«S;ikÒôêÜ®Q:@@¼)@€È›íB©@@_ hÇ-0¡+Öøº>@ > Y_&x´MŸnÖÍŽâ3$5 € à¶"·…É@Èà€ÐÔ¹K³² êT@ F iHF:ëÈžãí$C@pX€‘àd‡ €d‹€š4mNÌ¡²Òõ²|ñbÑϲõæË|Ú›ë|ξÆ' €@úZ¶jݨ-[×Ûçûm9кޥ[÷Fé"Ø£†n) ç@@\ @ä".Y#€ €™(0ÅÌÒ%ãt¦P´mÎôß­`ϲŋD¿Ø@²G@ƒF@ÒO E Ù"YÄ2tÙÓG¨) €¤_€QúÛ€ € €žÐÙBÒÙB‘¶eK›ÙAõÁ B‘”8d§@¬#‚EÙÙ?¨5 €¤G€QzÜy* € à Ë×È”yK"βƒB¿LþÖõ¡ €ÞЀQ—î="Î."Päv¢ € Ùˆ2»}© € @´À¾#H—#(”-7!€„4,Ô·›°ü\‡ € €€ˆ@$ @@ S40ô¥YFNß1ºél¡¯?o½W(ôÇ €8! Á¢÷Ü+ì;‹zuj'»nÝ÷9M € €€ @D7@@°40îC†è  €©ˆ(²—ž;`èV©.ÏC@2N€QÆ5)B@âÐYCc&þÐä&{9]RŽ @th hð;J¿[6z¼І™ÙDÛ˜åçØ@@ @”˜w!€ €¾XW^!ã¾ýµÉrrÌò}ÓR@ ã"Í(ÒeçØq+Ñ€ € €@|ˆâó"5 € áf éL¡)“¿“ÙÓˈ:R @Ìè?` l³ÃPÑ€‘½1›È–à@ˆO€Q|^¤F@|/0fÂMf érr_MïûºQ@ÌÐàPÿ-M èCUvYnŽw5"á@ˆ*@€(*@@ÌÐYC_N›Ó(8¤ËÉM™ü­,[¼(s*JM@²B ܲs:›HƒD½:·Ë *‰ €$#@€(=îE@|"nI9 çMŸÔ€b"€ ÐT@ƒDƒwØQú Ø2p‘%çì € €QEåá" € à54É|oãǾŬ¡`ö@|- ï&ÚyøˆFu¶u?ÙÕ|±!€ €„ @Þ…³ € €@F„¾oˆ%å2¢Y© €@pKÎ$ Å)@@ A€]@ÈPpÁ!–”ËÐÆ¦Z €–@¸%çzuj'Ç‚ € €@ˆ¢@@L 8” ­H@ H”ˆ÷ € €@6  ÊÆV§Î € ч2ºy© €@ŒƒwØQ¶ùÃÐ@ê6%EröÃÇì € €Ù.@€(Û{õG@Œ û–,[¼(£êHe@ˆU 4HÄrs±Ê‘@²A€Q6´2uD@¬ 8”ÍL%@â H'É@@ keMSSQ@Èd‚C™ÜºÔ @ YРѰ­ûÉ®æ‹ @ÈfDÙÜúÔ@2BàËisd’ù²·eKËøwÞ´ùD@#@ˆn€ €4 @ÔØƒ#@@ÀW –¯‘1”™àP€‚@šì2|/é7`ËÀùý‡n%Ûôí8f@È&DÙÔÚÔ@2J€àPF5'•AH@ËV­­™Dv¨MI‘`‚D½:·KÁÓy € à-DÞjJƒ €Ä,üÞ!fÅÌFB@,Ð ÑÎ{î%]ºu·$4Htöò\…ê#€$#P½`ÔUVÆœEna¡ôêsz"à–"·dÉ@pQ 88¤?ö-Y¶x‘‹O$k@2G@ƒD‡R BƒÌ2s:“ˆ @ óꪪDÌׯ𩫍©«“æs£ù¬œ1CôzíÊ•¢AÉÍ•êÅ‹¥nÕ* FÓ´X´Hòrrê¡6n´>kÍgý^l~zw †[4ªž=%§!ïܤ G«|æ|ž9ÖÀRဒcÊ•STd•/×|æäç‹´h!¹æ‹ xÅ£EZ@@À¡KËò@£P@ß ô0Pv>"PnÞG `ÈZÔ©0Ÿš+¤vÉ©œ;WêÊË­¯Úõë­ QÍš5"&PT`fÿäšZ×5Ô\ƒ=‚?z.8øS×pÞ$hHíÜG®x2YjIˤ›BRrV›`‘˜ÀP~;³Lª åµn-¹%%ÖWa¿~’×­›äwê$E›o.y;ÖgÄŸ„ á@ð²Àºò ýÞ¤@YZ.@Á €q ì2|/ ~ѱLj.9dž àmÉcm&HSg‚=¿ÿ.•ækÃ÷ß[³€Ä̲‚<&‘èq4œãBpÈIq;˜¤Ÿà’ n±…ï°ƒ5 ©hà@É5A%“Àz´ÎJbË>DÙ׿Ô@|,¼´Á!7$EGð„@èûˆzuj'$bCð–@ÍòåR½l™Ô˜åÝtÉ7] ®Ê,WkηZ·NªMÀF—h«Ñ€‘~¹Y|“¿7 ÿè ¥|ó¥ËÛ˜¯Ò¶m%ÏÌ2jaÞ‡¤Á£‚îÝ%ß,gWÐ¥‹äwîìÇjRæ8Å Fr@@ ]Sæ.‘÷¿û5ðx–– P°ƒ €@¡ï#b©¹„)¹pL@—…+ýè#Ù0mšl˜ Ej{¦‘^×™FuæKßs$’k¾t¶Qñ6ÛHë½÷¶I‘òá¼ù·í(9 €d™€.-§KÌé6åûïä—Éßf™ÕE@ÀÁ;ì(Ûüa¨•¹.1wöÃÜy¹"€4ØXQ!µfP­yІ)S¤lÂ)ÿáim–«4_ö¬ F7¥ë ÂC±0êl# Ušº—™Ï6C‡JÛáÃ¥Õ¶ÛJy÷Q^›6’[ÄÒ¬±Xz5 "¯¶ åB@‚Æ™™CSÍ "{{yôãö.Ÿ € ¤€Î"ÚëÃD?uÔ·›0t«$såv@p*ÿâ )33ƒªçÌ‘ª%Kd£ygPy޽D\Jg…+dè¹, ).Õ·¾¶Ö𥥳t‰:]ž®ÆïòÍÒt…ݺIQÿþÒÖŽÚ쾻䛀›¿ù«½(- €d¡€ÎÒÙCöÆÒr¶Ÿ €8'ÐÀ@Ùyø+CE¤¢^Û9÷rB²P`£ $˜h©33…Öüï²îí·eã¼yRk,ô]A¾ÙüTV‡Pµ}Ö™ö‹eÓ÷ià(¿o_é|øáÒùˆ#$ÏÌ,ÚhÎå˜À›wy·m( € ` Œ™ðƒ,X±ÆÚ_¶d±ŒçMd@@À½9\ºtënåÜ«S;9vøžB– €@æ T™™A•Ó§KÙ·ßJ…Y2®Ä̪0Á†jtÐeã|µù­¼àj•šöŠ·¥t†Q óUd‚BÝ»Kë!C¤íÎ;KÉ€ÖL#ŠF  r”ì@@'˜=ä¤&y!€ ]@—˜;ôø“‰ŽÝc³ˆì €ÑêÊËeÍë¯ËZ3K¨võjÑã܆ C¼†èOJáÕl ^]V.ÙM—¤Óm£ åKAûöÒÅÌ,ê|ôÑ’[RR‘?Ó.@€(íM@@@ÈÁ³‡æLÿ]¾š0>rb® € €@Ò» ßKú ØÒʇw%ÍI É55R½l™TL*k^{MŗÎ<©²gžø=¸â÷ò'Ð÷9Ë£4`¤ï™Ò¥è –.Ç+- ’]»JN~~,YÆD. ’% € à„@èì¡·_~AÊJ×;‘5y € €@fE€á4 Ð PWV&kÍL¡ÒÏ?—ª¹s¥À7Y:ÎïÁ¿—?Þêfp(´8VÈŠjZµ’¢~ý¤Ãÿ(]Ž9Fr[¶ Mʱˈ\&{@@ Qqßý*Sç.±ngöP¢ŠÜ‡ €@üÁï"bQü~Ü™'°Ñ,9V»|¹,»÷^©ùæ©6³„êL5Ã.ç÷àŠßËŸ@÷Ó¶Ôeå¶gùÅ{‹Î.jaf•˜÷õ»öZÉïÒEròòâ͆ô  J[@@·Bgû–,[¼ÈíÇ’? €àYDmJŠä€¡[ñ."zd¥@Õœ9RnBëÇ“Â3¤Ìj(aƒ ~®ø½ü ôR}ÛïJàÑnÙhìóͬ¢ª0@:|°´5£¢þý¥ãÀYDÎz’ € àˆ³‡a$@`QÂt܈ P½p¡,¹í6©4A¡¢3<‚BÁûêú=¸â÷ò"ö:Sçuf6Xº7 ovË-*’V&X´Ù-·Ha¯^ÁIØwH€‘Cdƒ €8)ðà럲cöP€‚@R&:‹è쇥ìÙ<H‡@]y¹TL*«^|Q 'O–u55¢„Эé“"LºÐû<}ì÷ò'€«ïZïÁàPhUrͬ¢¶fVÑÆv’'Ÿ,%ƒK^IIh2Ž @” ·!€ €n L1ïzß¼H·²ÒõòöË/¸õ(òE@(Á³ˆŽÝcËÌE±âø[`õ /Ⱥ?”ÚéÓ­™BáCZC‚Cþng»ôº¬\iß9d—#tæ}>Ügž Yï*ÚrKétÀÒý”SÂ%ã\œˆâ#9 € à¶À˜ ?È‚k¬ÇLùþ;ùeò·n?’ü@@ Œ@ÿeçá#¬+ƒúv³ÞE&§@ ˜Ù#ëÞ_–Þ}·äTW‡-Ô\Åâào.¯´\Ï™Cº œfEë:«H e³ë¯—ûï/9¹¹Ñ’s-Š¢(8\B@Ò!¼¼œÎÒYDl € z–™K½9OD÷ôBkßzËú*˜3GÊœIBpÈý¶rú :s¨ÌÃÎsúaQòs¢ïèl¢]z®éú§?I§Ã—Üââ(OåR8DáT8‡ €¤IàËisd’ùÒmÙ’Å2þ7­}þ@@ô/3Ç,¢ô´OEçJ?þX–>ð€Ô®^m-וh À‰~çj•`NY6{(“‚CÁ-®"íÇíÛKÿ«¯–öûî|™ýf5Äe@@ •£ß›$ëÊ+¬G~=á™=ý·T>žg!€ €@ˆ@ð2smJŠä쇅¤àð¾@ÕìÙ²ìþûE~úIÊÍŒ¡d7߈²,8ä•eå´ß¹ÙwŠÍRs…C†HŸk®‘b3³ˆ­yDÍ‘@H™@ðòr/~˜2w‰¼ÿݯV©Y^ÎgGq@2Z x™¹c÷"½:·ËèúR9ð¿ÀF3K¨â‡dÁ%—X•It)¹P‰T ð‡>ÓÑã, eËÌ¡p}D—žÓ¯<"­wØArÌûŠØš  jjÂ@@ -ãLphª é6åûïä—Éߦ¥<@ /3Ç{ˆÛp„Þ¨YºT–%uŸîÈrrv Ùþø¬5Á°2óåTp0™Z§³ï›ÀPÉðáÒçòË¥E·nÉT##ï%@”‘ÍJ¥@@ÀÁï?ö-Y¶x‘«A™@È8.Ý{È^fÕ‹÷e\óR!2J`ý»ïÊb]V«NçŽ8·¥s€ß‘ZdÙÌ!]V®Ôá>h;x¥ïä˜÷mn–[ìxÈ!‰V%#ï#@”‘ÍJ¥@@À¼È­F™@²A ô=D,3— ­Nð—@]i©¬xê)Ù0fŒTj0ÄÁ€ˆWøn-.C o$8;Ǽ›¨…ùjÜqÒëÜs%¯uëð ³ì,¢,kpª‹ €ÞørÚ™d¾tãýC € à)ÞC䩿 0 $P9c†,¾é&‘ùó¥ÊáY#‡‚ }°«Á¡2Ó²}Y¹hMÕÂÌ$ÊëÓGÜq‡-iV\#@”ÍL%@@Àë¼Èë-Dù@²]€÷e{ þxS tüxY{óÍRV«¡g7‚CÎzº›.*¸Þáa¢eöCß)1ï&êi‚DöÞ;ÑjfÄ}ˆ2¢© €ø]`Ì„dÁŠ5V5¾žð‰Ìžþ›ß«Dù@È(à÷õêÔNŽ>$£êGe@À+}TÖþç?RëÂj~àÚb.˜D}^š/2s(±È3KÎu=õTé}ñʼnew Ê€F¤  € àÑïM’uåVEÞ~ù)+]ïÿJQ@2H ø=DmJŠäì‡ePí¨ øM`á•WJÙ¤I®›à+¬®eÊÌ¡äiÛí¶› 5*ùŒ|˜"6EF@ÌxðõO•zyôã}v@@À;ÇŸ}~ 0—5"°Ï *º dáWÈÆ_~‘ –ó}pH"‹féÌ¡RúA¢ýÙ¯ý§ÐÌ$*Üv[øðÃ’[R’hõ}y"_6…F@LX°|Œ™øƒU¥eKËøwÞ̤êQ@2F`¯C—.ݺ[õ9v!Ò«s»Œ©AÌ=óL©ž>]ê\‚øup¿Q˹àÒ(èÒ‚¥©ïFuñHYân¢†rçš QÑ–[ʶÏ?w~¾‘Ÿ[²#€ €@F|9mŽL2_ºÍ™þ»|5a¼µÏ € à-àÑþC·’múvóV) d¬@íúõ2ïŒ3¤véRW‚C çû‘_ ôZf%€é– ~£A¢]»Ê`ón¯¼6m"Ý‘Qç eTsR@ð£@p€hÊ÷ßÉ/“¿õc5(3 €/ÐÀ@Ùyø«žƒLpè$bC\0KˆÍ¿ðB©4Ëʹ1sHËOpÈõVtì*3}šµãX®‰gäë¾²4HTb–›ôÄ’“›kŸÎØODÛ´T @ü"0î»_eêÜ%Vq¿žð‰Ìžþ›_ŠN9@È*DYÕÜTOl¬­•ygŸ-UfY9·¾à×V 3Èï‰Æs¡‡DÒorÌcŠ”mŸ}V$/ÏÁ‡z/+DÞkJ„ €Y&0f²`Å«ÖãǾ%Ë/Ê2ª‹@f ôíÕCþzáYÒªU‰TUUËS/¾._~÷cfWšÚ!¡]º÷½>̪]¯NíäØáC2´¦T ¼"°Ú¼eå£JK"8ä¬ Ù²¬œƒ¨Q‚CöStîP¯‹/–î§ŸnŸÊÈODÙ¬T @ü$@€ÈO­EYˆ_`aCåÞ¯ Ü8Úˆžx~Là˜ð"ÿ´%E ֽ󎬸ë.©‰a0;‘úJD-=÷0sÈA÷8¾ŸòÌrs}o¼Q:VÿË!–Â3Y òLSP@ÈVÑïM’uåVõß~ù)+]Ÿ­ÔKàȃö•Ž8H šˆl4ë­WTVɲ+eÖÜòáÄ/eÚôYMÒyé"/µeA 9–­ZˡǟdeÒ¦¤HÎ>pXrr7 A zÉ™{Ê)R·aC„É&8”œ_*ïfæƒÚq‡ì§æ•”ȶ/½$…ݻۧ2ê“QF5'•A@?  òc«Qf7Þ}áqiß¶MÌøuÆl¹åþÉìy b¾'• ¥R›g!à®"w}É6 ÌÿóŸ¥ê—_¤6íM¹„ß#8ÞÅkgõ=8:{¬Ô…>h]}ÝwtÔYD%Ûm'ƒž|2Q6OßG€ÈÓÍCá@@ eC+SÇx>|e´´jYÏ-RV¾AnøûC2)ïö9êà}å¢3O’²®´L.ºîö¨Á)Dq5%‰ð´"O7…C cV=õ”¬1_n,-çë~máùýØ9jL¡ËÌìy¯l¾î;Iö›|$êvÞyÒóœs¼ÒŽ•ƒ‘c”d„ €$&ðàëŸn|yôã}vÈVÐÑSæ=µæŽsss¥M«–Ò§gwÙqûÁ’—§¯ŽÝ´­7Áš£Ï¾Ô Úl:ëüÞ_Î>YN>êÐ@Æ—Üp§|ýÃÏãÐD¡"#à_Dþm;JŽ€_j×®•9'œ µëÖ9^d_ð«F’ƒüŽƒº˜!ïrס~“ߦlÿúë’ß¶­ƒ…KVˆÒß”@²\€Q–wªßD 4@´ûa'[¢à„Ú·•›¯¸PvÙa»àÓòÂïÈÃO>ßèœÓˆœ%?ü%püÙç |ùQ#ûì €N,ºþzÙ0a‚ãKËr¢uR“‡Î+7_Só¸fŸâë¾ãPpH‘t©¹¶#FÈ€{îiÖÌO ù©µ(+ €d¤¢ŒlV*•„@,";¨°Pþõ÷eЖ›ž¶xér9ò¬KÇnì rC•<ð"ÿ´%EÀoUóçËò“O–²Z?âÜæë~›ÁÁ~;K/~ê‚rë=´¬œù¶ÿ¸ÐgJòòdÀ+¯HQŸ>^ì> •‰QBlÜ„ €8'@€È9KrÊ XDZÛƒ÷.7]qA âú?°#Ž:]*«ª¥µYŽn‹~}¤ ?_Ê+*dêï3¤®®ùßÅìß§—t43”êL^³æÌ—5ëÖ[Á¨Ý:[Ï9ûÄ£eŸ=†žùàãÏÊ7?þ8Öÿ3aণ-1W\T(Ûo³•l»õ–RYY)¿NŸ-¿Î˜•Ð2yùùy²YŸÞ2`³>f¾¢KîÍš·@fÍ/K–­”/ÚNnnŽl3p ).,’Òòrc63¼°°…l?h UÞdËÈ”|(@€È‡F‘ðÀFXrãRúÉ'ŽÏñí¿Ýn. ôÛY{éÓkïRßö—úLŽ1i¿÷Þ²Å]wIŽYþ:6D™ÐŠÔ@|-@€È×ÍGá]ˆ'@Ô»G7óăRÔÖÖÉa§_(«V¯•.=OÛ¯ÀµGž~Až{õ­Àq¸­l&Oº#piâWßÉÕ·Þ'—wšœð§ƒç›Û¹ñî‡åƒ _ZɈþóÚ[ríÅçÈÞ&Дo~1t{ëýñr÷#£¥¦¦ùß Vƒë/9O¶´eؼ4ïÒ²rù×3/Êëc? }T£ãÝwúƒÜ?ò¯s§\t¬/+“+ÿ|†ì:tˆä®Ù;ñ”Õ¾‡Oü,@€ÈÏ­GÙð®ÀÆêj™uè¡R[Zêh!};Ào+¸4Ðogï•Oý_)3‡œkûM^ëÖ2tÜ8É)(p®¼ỉQñy4 €  ˆè4ˆ'@ÔÆÌzÿå'epØiÊò•«åÌŽ”óO=.pmö¼…râWŽÃí\qþérÜá.½>ö¹ç‘§äÖ«/–ýGì8ßÜÎ}>-¯¾ý¾•,4@ôñç_™™>½¤_ïžQ³ùiêïrÉßî” 3³(ÒvȾÃMðæL)).Š”¤Ñù/¾ýAnõ˜@kt¡á ´¬ú>§³O:JZ–”„K8KY‰ÙAÀçˆ|Þ€ ¬yã Y÷ÀRá`€àG;¤XúΡ2!‹éÐ×}ÇeËB3s¨ûÕWK×cމÉÒë‰y½…( €d¼¢Œob*§@<"]òìñ{Gž Ëªíwü9ÖqÏn]äµÑ®éÎé—\'¿ÍœÓèœ}c^<ûÖÿ=":´·OÉ…×Þ*“ž&çœt´œsrìÿ¨¯¿ÿÙÊ'4èȼa§Î érváfÝÿØ32æ­q¡·XÇ'q°\zî©M®i~ ÓeòòÍòz¡›sλúæÐÓÖ±[e û0N"àSD>m8Š€Ç¦›e«Ìz³Ž-/çë~m+—ù½Ò48Tn¾š_9u%öußIA¿ÑeærÌ»Pwúì³Ô5Š‹O"@ä".Y#€ €± ŠE‰4Ù$O€èÎë/“½wß%À3ùç©&¨s[àø±»o–!ƒ· ¿ðÆ;¢³bÂm;l7Hþu×KúÞž#Îü‹u¬Á£ŽíÛYû>íx9t¿=én}àÑ@0HOÖšK¯^».p=RÐ¥¢²JF¿ðª ½/ÕfY™¡Ûm#·üõbiß¶MàÞEK—Ë1ç\ÚäÝIú~¥×MðK?ímyæý[¾5ïCÚPQ)-̲ºdÞ_/:[6ï×ÛNf}^}Û}2qÒwÎéemòN àsD>o@Š€*ÿ]žu–h°À‰Í×ü àƒ–næQk2gY9…SØoòÍÿlõÜsÒrà@+ž¬¥Ç§"€ €D v°b ºßùÛeç7R{ú¥7äñç^ œ;lÿæ]D›Ò,_¹J?ýâ°/ܽÆRŽYN>êPûP.¹ÁÌú¡~¶PàdÐN¸ ‹®yLý}fPJ±:Áï@Ò‹Çw…Ì[¸¸Qº‹ÏzŸôïÓ3péê[Í2s_5^f.4@oYo¾÷Ÿ2î“ÏÏ`L @”‰­JH¯ÀÜ3Ï]f.™Í×üZñ4 ò'cžÈ½ú¯µróïVf%¢æž4ö›–[m%ƒÍ2s~ßù½)? €ø^€‘ï› 8, j.{9ôÀãÏÊß|6éNCË?î¸!p­¬¼\:éÏ¢'{»Á,UwØ~#ìCyè‰çäÅÿŽ ï$ ýâëòÄóc‚³l´ªY:î"³„œ½Ý÷èÓòêÛïÛ‡²ÛŽCä[® Oøò[ùëí÷Ž#í\uÁ™rÌ¡û.2u|)¤Ž¡¢dËx;d¢ jLª‚€Gfv˜Ô¬Z•pi%L—Òuæ¾sˆàCìi i :tÆs¨2éˆQúìy2 € `  ¢# ÐX –Qmm,Y¶\þ7îcyáw¤¦F_ó~Ó¥éþ÷Ì?¤K§Ž×Ý9JÆþ•u¬K¶½ûŸÇEßÕ£›æ}øé‰ÎÌ ·¹ :âÀ½åÚ¿œxth€è¸Ã”+Î?=pý•7ß³dvÎ8þùóiÇ®ŽykœÜÿØ3c݉7@Ô\YeÎ"@€(C’j à!¹{ì!• .9æûà¶CšúSÑjL58¤ï®ñÊæë¾ã>Sh–¨òõ×^i΄ËA€(a:nD@œ @äŒ#¹dŽ@h€èšÚúåÓV¯Y'ó-‘…K–F …jh`D$ööé—ßÈ5·?`þqçä¾›¯¶/ÉW“’KoŒ¼ž¸Û¢?°·\wIäÑEgž(§sx ¼£_xMžøÏ«ãH;¡á–‹7@Ô\Y#•…óøY€‘Ÿ[²#àMÅÇËúÚÈ¿ì©Ô¾à×Jy`?’­Sç5 ¤sÖËMû2sÈ!Uô›Vyy²ÍWõ¿pæPÍÒ’ ¢´°óP@@`“¢Mì! ¡¢Ý;Yjü­Z[´OÏîòÊ¿ëBz®ººF>åϲ¾´Ln¹úb9`ÄîvRyÿ#òÞÇŸŽCwÒ :ùèCå/g(V¸™@‹A;çr¬œuâQ3TÒàRðF€(Xƒ}  ïÂYH\ ‘‘ïƒCÊå‘þÄ[®ù;uæPY’ÿŽmþ)ñ§ðmÿñPŸ!@¿ã@@0ˆÂ p*«Ü)è÷Ý"Ûn½eÀö®‡Ÿ÷ÆO”w_ø·”Yç7TTXï'ª¨¬ ¤ ÝIw€h¯Ýv–»n¸'P¦É?O•1oŽklyoüg2ò¾GiÂ턈®y|ñÍ÷á’Z眺 ܼŸ<ûð¦%ðÊÊËåS.”hA-}ÏÒŸþ‡têÐ>PÎ3/»A¦MŸ8Ö§ËÚ(sÈDÒT Ä òõ¿ºgIpˆ™C“y¬ß r¸}É@ÈVDÙÚòÔ;’€[¢V-Käç•Â-¬GëàÊÔßgÊ6·å²›þ.“¾û1pnç‚ÓOÓûSàÒ­<*c?š8Ýq:è¢õP£àíöQËÛ||ªÑþÁû —›®¸ p®¢¢Ò,±w”oØ8§;N—µQæ !ˆ2¤!©ˆ'@DpÈC ¡(,+&™Ó iU%Ó Ü‹ €  @ `KÀ­‘f~Û5—È~Ãw +½rÕ9ìô ¥®.úB Ç~ \qþé<Þz¼ÜñпÇ¡;n]î¼þ2Ù{÷]Z³vèL& x…n[m±™ÜóÕÒ±C»À¥§_zCî•À±½ãFYí¼ùD SeJKR¼#°|Ï=euMM³"8Ô,QÚr¡ <ÒZ¶ÍÏ—­&Mr¡Â©Í2Çü`‰þ?©-OC@²N€QÖ59nFÀÍѰ¡Û˨[¯ [‚—þ;VF=ñ\ØkÁ'Gì¶“üý†+§ª«käî>)_ÿð³lÞ··ìô‡måÍqËœù‹¬4n]úôì./VŠ ëgMib &uöeMfé57ʪù²!Iˆ2©5© ÞX=b„,¯®ŽZßáfÁtµ©£.+—µ%SÑ×}ÇÃý¦SAlþå—©oP‡ŸH€ÈaP²C@â @¯é3]ÀÍQnn޼ùì#ÞÅc{ž~ÉuòÛÌ9öaÄ϶mZ›÷ù<,ÅEEÓÜxÏ?äƒO¿°®»tùëEgÉQï± á.Ô™A‹;z\Þù0ü’xn•5\Y8‡€_ùµå(7Þ(Ûk/YXU±€¾à×Zyx?"z4 d‡â¸-%I}Ýw<Þoº›QD)éÇ<@ÈhDݼT.7DZœ‹Îi­5@4´eK)øôSR›%¢Ôzó4@@ ‰¢&$œ@ÀU³N8¤>èO¨½ÌM5¦~º¬œssÜ-Iãû|Ýw|Òg´ÍÛååÉfQ>¢Æ#@@øÅoÆ$* KýñÔÃÒ­K§@w<ô¸¼õþ'cv@hˆ¢ép 0¢Úêj™YUeÝîëA~­Oúj+s“‡tY9 æymómßñYŸéoÞ?ÔÞ¬$P0a‚׺@ÜåaQÜdÜ€ €8+@€ÈYOrC šÀ®C·—o½6¤Ò Ä|òŸ¥¬|Cà; €@4DÑt¸† ˜‘ÔÔÈ<óï’ !ËÔ&”_:oòÙ@ÞæHkz=ÔÛÌÒ~@€(­MÁÃ@@Ì @”9mIM¼-У[óï%//×*èôYsåŒËnZóÛºl €@¬ˆb•"Ä, ª6–Y ï"Šùþt'ÌààÒêÌ¡2–•s¾—ù°ßô5ï*hhcf|ö™ó.)Α%æR Îã@@PD¡"#àž@Iq‘´,)–u¥eRYYÿ"h÷žFÎ ‰ˆ2±U©i iI›YDký2‹È‡ƒüñ´¶ìxçP\E@ÀKˆ¼Ô”  é`ôŒÊJ©ózõ|6È/g¹¡Ì£3ÍYV.ÞÖL.½®?п°°~édž~O€(9SîF@h @DW@@À?ˆüÓV”ß„ˆ´Üú¾}‘g·L™ú•ztÁ¡ÔWô4KË›÷™>N€(@Á € €@2ˆ’Ñã^@R+@€(µÞ< ¬ Òz/«©‘•fKýÛ=$4Hî¡R9R*S?–•s„³q&>ì7:‹¯C^žtÊÏoÒŠ jܼ!€ €$(@€(A8nC@ ˆÒ€Î#Èt"­ö|3‹ÈSÁ òÇÓ}jLý˜9XŒi}ÚotÖPoóÞ¡º0å·DŸ}#€w“å˜)i›æEy·œ” @ÈXDÛ´T @ e`£R%Ò-%@¤3fWU‰.<±y¥c0sÈaÐàì|ÚgòMp¨ åF(?¢àFfßQ óº²òrÑÏõ¥e²nÝzYWVfío¨¨ò z­*â3‹ [HIq‰IëV-¥MKóÕ¦µµ_d^¦Õ²¤Dô“ pËW¬”·?/e¦Ïåä$2‰y£¸õO–óƒ¹}Û¶RlúxË–%Ò¹CiÓºµµßÚôó’âbSfý+ ð—"µ¥EÈnDÙÝþÔW¢ˆôyµæk¦'tk¼%æ:E(ù~'¬6uóÔL­ +_ÏïðiŸÑÑÅ~æ½CyAíºËs¡"'$ A ef@~á’¥2wþY´t©¬Z»VJM`¨´Ì‚L@¨Ö¼­ncÔÖêï äHN®ù2ÿEÚtx~cþ•±Qòòr%× òçåæJ‘ µ2ƒê4jgÙ{tí*ýz÷’žÝºJ—N EͲóÕf}Ûï~úEF=1Út¡\óîµØÿùQÒô‘ûgRœ&ÛæûAË”k¾òòò%ßôñV&8Ô®Më«oïž²YŸÞÒ»GéÖ¥S‚A®¤JÉÍ €@܈â&ã@Ò&@€(mô<Ìh&@¤¯0c! ÌrsµqŒÓ8 –®ç:Z‰ð™éì¬23ÞäÕ-ž±9OÕÁ§}&Ïüòy3s¨P =Jyª·ù§0ú ­³¦üö»Lúî{+(´jí3;¨Üêpfè;e•±†ðMGoݪD:¶m/}M°hØÐ?È -·°fb¤¬ <È“¿üú›<ôä³²Æ,cÙÜ›7ËÓ7¥±úµ ˆ¶mÝJºwî"Ûo³µüq—¤« ‚2»h“{ à-DÞjJƒ €@4DÑt¸† Ä Ò|«Ì¸â\$ ÷>”„žëMQÉcÍ«é¼ë7vpH—•³BBq”ß }öY’`é¿=ÇüF}Cªé/°WKPYUm–›!ï~ø‰Lž2ÕêP͆´.ž\±h H“það6rȾ#Ìt[˜åç ¼Ú,”+Œ@uu”•›w^5·¥Q®#@IDATîÖdÚîe6ÈÃO>#3fÍ ô[Ïô_-iŒ}تTÃ$ÊÍË3÷ÖÊQ({î6Lºu霄}@ %ˆRÂÌC@pD€‘#Œd‚Á ˆìÛu&Ñúf~9ÝNógkp¨ÌxÅ8,3™S }=TïÃ~ÓÊŒyv73‡¬aÅ8ËO€È©^ïó|jjjdòÏSä£Ï¾©¿O—*(Š%0¤ÕöÌàzë0(4‘ÕAÈ>{ì.;l»ä›o&6ï ¬^»Vžó†Ìž7_ò48æ9i›—› Ѳ+­;<Óµ4 ôáFÕ6"ý‡A—ŽeÄîÃdï?î&Û·k”„@ÀMDnê’7 €Î  rÖ“Ü@À$ R¿u&à¡"G‚q’û¥ýÔFßßTît0ÍA‚CbÆUW3–ÝÚŒwZ[ýžQ È™žd¡yÇÐ^Ó ÕÖÖÄU]Ï ®';°njŸ—/;l7HN>êéÉ;ŠâêéJ¼héRõï§d®y7V]mmÜÅðLÿÕ’;ЇƒtVQÏn]åÌŽ1ïÞ|‰}@À5D®Ñ’1 €Ž  rœ” @ É‘jðC—œKj˜$Ar¿4ËʹØR>ë7,ìc&>˜1@kK°üˆ\ìS^Ϻ´|ƒŒýðcycì8©5ÈzqU…öÌàzRc4®pމ¶æ™oª#9À,=·´*)nœ€#Ï ”™~|ߣOÈ/¿þWÙ<ÓíR;Øí,5H”W/#†í"G´¿tíÜɾÄ' àŠ"WXÉ@W¹ÂJ¦d·€"Ô!’¥fµ£5æ—ãžM”à ¹Žà‹­ä£~£ßmÍvgû}CÊ’Dù­Ñĉ.â¦&kÞA§ó÷f9¹×ß}ß¼oh¦õƒ6Þ©ž\wiP]³ÝzÀæräû›YEƒãÔ%yªV¯]'Ͼüª|ñÍw1ý†‰gú¯ åB?¶³ÖO õéÙSŽ6ÏÝv|‰}H³ÀÜ‹díºRÀíh}¥¹8I?žQÒ„d€ €@Ê¥Œš!=ˆl° fÐ{^e¥}ØügƒäÍgžÞ‡\ô÷Y¿éÕ¢…Ù³†”%Éò3ƒÈžåÕ¬Ÿí¿òÁ'Í{X**¢§×]X/..’ö.§{DBVÜ”: ó†—ÿû¶ŒýèSóþ¬ÈËÍyªÿ*Ë}8ÐæWnòÌoèL¢ãþt¨õ.¦À5v@ åßÿé3Úö%f\¯‡™5Ôäû É: ŠÞE2êê’eËåñÿ{Q¦LŸ!x_‹bxfp½Éw‚{M•“—'ƒ·ÜBÎ?í$éÖ¥³{"ç¤jÍ_–¯þo¬¼6ö=M'+M hmC ÈS¿Ä‘è®ÖÇë3‡´n‚t‰V6]÷9\qºø:ô­í¯A›æ«ÀürhØápËO€ÈéVôP~u&ÂüÞÇŸÊsc^—šÚħ"zfp=ìwCjÁõ_¾ùæ<íø£ä ½Gð—ÔòG}Ú¬¹óåž>*«Ö¬kô”gú¯]zôc-ŠöeС};¹æ¢óeóþ}íò‰. Ì_´DŽ=÷rŸ>ë–%Å KÔ™€‘‹ï7"@ÞŸ³ €xQ€‘[…2!àsˆl¥3ö¹ÌŠÖ™@Q&‰48¤Kêyy#8ä|ëèak³êO'ó‹ŸùfÌ.âæ`pHŸA€(¢´¿/Ôš€Ðæ=,¯g½ã ÑoZÏ ®{`PÝ6ÔAõ<óÍzôaÊчdöóüÝY2 ô3çΕûÍËÞW¬^Mp(†ö´û²&ÕþÜ©C¹ê¢se‹~‰bà# I TW×È'ž+å*’ÊÇ©›õýF›õíe¾ÿû˜ÏÞæ³·™”øû9Õ2äƒ €€ûˆÜ7æ d@*DAƒä "ýÔ°J”¡uÏ7I©—.+çå-x\ÉËå [¶ ~özŠOê·.¯ïê\P Åæ3jë»P~D)nôT<®Ît”_Sþ7îCÑYD‰n‡6É…ûÁ—››#G¸ŸœtôŸ$7'}ï{ØTÊìÜ[±jµÜjÞ9´h)ËÊÅÒÂõe½¯g·®róU—š`QûX²! $!ðϧ^ç_{+‰ܽÕ~¿‘Œþ|ÚqÒߌbÝÅ*E:@Ò/@€(ým@ È8Tˆ" ’WšqÐÕf6‘~iÈo"f¹ü¡ß¸üÔ°Ùk`H¿Ú™•ªÚš/ 5»¹T~DÍÊû/Á;Œ—g_yUê´—%±y"@”d’¨~àÖHêš@ÑÎ8þX9lÿ½éÙI@Yy¹üýáÇä·Y³C=Ñwƒ<е8Ñúr®™7p‹þrý%JË’’àÒ³ è,¢kîx@¾øæ{‡sv6»}ö&w\{i\™ Š‹‹Ä €¤U€QZùy8™)ŠQ ƒäúëò‹««¥LßQäif¹ÜP1ô—KÈ^CA%f®›™1¤Sbê£.–ŸQ i2cçÓ/¿–=ó|ý`y‚Ç3ƒë1}w¸ÛnÑÔ­'›‘Î$ºø¬ÓdÏ]wq·0äÞH ÚüEÿðègåëï~4ÁÐM3å<ÓíÒz kQšíË&‰v:D.;ç )0I±!€€{ú=9þó¯Ì××òëŒÙ²pÉRów·G~`˜jo¹Y?ù÷½#¥¨¨0.Dqq‘@´  J+?G 3ÜÅ9ÖYmÒëû‰ô«ÂìëÀ| ó4RÞ6Ìr™<Î~ãtiôÿôõ«ÈŒ#·2³…Ú˜ñ·fÓhf3Ot¹üˆšñ÷ÓåÉ?M‘QO—¯\íDöqåѾmyæ¡;¥kçŽqݧ‰ ÅMÆ  €¤M€QÚèy0™+àf€(‰Arˆ×åçVÔÔH©ùôÒ‹"4ˆUžÄ+BRÑ™bSJEYâ~Fý&îg…¹Aû^Kê¨Ëșϸ”)(?¢0 çÇS‹—-“»ÿù˜,\´4áâ×Çc¼•I¸ ŽÝ˜È¾žÝ»Éµ—\ =ºvq¬d^`öüùró=£dCÃKÞ=Ówƒ‹ëo%-N"}¹¤¸Xn½öréß»wpØG4¬/-3¢ù2ÓŽ4`¤Ÿ<ÒónlùùyòÈ]7Êöƒ&”=¢„ظ @´ J ;E ³Ü 98H®³uVš@Ñ”ÑàŒßÄ=hïP+êóËL9ÒõüXª‘ȸR,ù¦$ƒý&ÖòÚý©ÀÌ*6_íòó­™C1Ï ~PŠÊO€(ݧûÕæ‡š‡~žö»Ô™)“‰nž™=äõD~øåšHðv[´‚D曟Íý-û‘÷Ž’Yóú»gú®]eôá@QøËDûòææ¥ô·\s…¶hagÅ'x@ ÎüÏÃ7?ü"ϼü_ùþ—iŽ—èúKΓÃØ+á| %LÇ €¤\€QÊÉy ™/àF€(qX kM¾•ækmÃt:€ŸÊ™E,+K+%‘Æ¥~©Dvÿi´„\¾ %:ÿä /˸ñš)`WÓ3ì ר5Iþ3‘àýÔóÀˆ=ä¼SO´Oñé°À^ûŸ¼ñîû\=Ówíy Š’ä_&G|€œzÌvv|"€@f› ø»O´¾ÜZvîøÃ4Ë¥žžT- %ÅÇÍ €¤T€QJ¹yÙ!à£Qhƒ¬7¿Œ§KÐU›O·‡vü°¬œú$3Fê›Òã$ÇÃâ)«ÎþÒÙBÌdV KÈ9ÒRXDñ´¸Ó~÷Ó/òÀcOJeUuÂ¥óÄ»#ß9 X7:õCOg\\uá¹¼(¹æ{·¾Ä}ä}Éê5këÛÌõ¿²Ã#òIôc-œS}YßCrËÕ—K¯Ý"×™+ àšÀÚuëåƒ _ÊØ&ÈÔßgºöÍx§!ƒeÔ­×I^^r¿3G€ÈÕf"s@ @ä('™!€€ 8 Já ¹Ý€öÌ]úMß ¤Á 8µ œ¿ý<¯~:5¶”òú¹ØgtØO¿ìåãJL@¨H¿f 96,èbµ¢p*>9·výz¹ýÁGdîü ×wZǺnâj(‚Þ©|:‹¨ŸywËMW^,mZ·NÜ…; h ó©ƘÙr¬¶òD`3¸„éÇZ$'ûò{—sN>ÞäêÔ?…‚ÑØGP³¼Á¤ï~”w>œ ¿úVjj_:64ïHǽºw•§¼ÝüÕ*R’˜Ï ЙЄ €¤]€QÚ›€ yNˆRk®¼cf ÿ™¬^»ÎáÜ#gWR\,£¸Mú÷é9QWÅER@Ò,@€(Í ÀãÈD§Dm8Bm‚åæúJÍ×S¾ó¥Ýb èÌ!‘KZGÊ›`&NýÒq‚OöH&{ˆOƒ@ú†y!Ô²áKóÕw ¹¶9PþDÊF€(5Üóëô™ò·{F™ðebÝÒìöw\:=Í7ž[ÅÐ Ñ×^![ Ø<5̘gßÿè“òÕ÷?Jmû¿Mš[(®B87k(ô±¹æ/Á]†n/W_p^è%Ž@ I•«×ÈûŸ|af }*3æÌK2·øoׯ÷Ýt•ì¾óñßáD`8 €€y°Q(~p"@”¦Aòxèu(HDú¥KÑÙËÑi é¾Òë^Þ²18¤mi í™¶,6c`ºlœ„ò4@d¾tö” ý¥±ß òòwe„²•oØ w>ô¨ü>sV‚³‡RÒ­#”>è´Šáæ>xÛróÍäo—_$úÚl‰ Ìœ3Wn¼ûA©®öØì!ôa[Õ;¬³ˆn¿þJÙ¢__ûq|"€@‚UæçØg_O–±f ¹/¿ýÁ½ûEßè¶‹ÎÜgùír:õI€È)Éæ3aÒ×òÈSÏ%4¨äÞ|™8Rþ]¾ln¨ÛOÔÙ—œsº ¶³}ŠÏ8êÌ_ªÏ½ú†¼ùþGqÞérrôcó/Ž”ü&Ãáì+§{¤hŸfCø¦ü6CÆš%äÞÿô Y_ZßqÀˆÝå–«/v8WDŽ“’! €®  r–ŒÈ^dDi$OªÑ”]—ŸÓ™C+Ì»‹*öõ8xYºà™FI=?É›S1Fšd#ßnLÃmzÖ¾¢Kŵ0Á fLËú4ûº„\¡ù²ÓØŸáòrõ\„ò»úÌÌ …€xý°¢²RFÞûÌœ;/îÙC©FŽA0mßq›Ê–ª|‰п¯Œ¼ê2)**ÜTöb(-/—[îXf™>ï™Í}ضHU_Þ¬oyõ¥Òª¤Ä~4Ÿ ÐŒÀ²+åÝ'šÀÐD™»`Q3©¿Ü½kgY¼tyÌl=`3yìž›¥°E‹˜ï‰5!¢X¥H‡ €@ú¥¿ ('h€Èƒä ·E3e_k~ñ¹Ö¤±‡’ôSßeTnÎU˜k:ÃÈÞÒ0JÕ¸’]GG?ƒì6)ÖƒôÝA%f\¶$/ÏZ&NŸ›ߨõ *Ôt._$@ä2°ÓÙðégòä /Kmm|KÒlú1ät‰âÌ/ø»5Î[Kn¾ùRY qqÞ©'Èþ{îáX²)£_g˜÷mýýþ”¶YTßTvž¨qï½C᫉ÞqÝU¼S+稨¨”O'}k½Wè›~‰û—9‚²ŠºÛ¦UKÙoÏÝäà}†Ë˜·ÇÉ{5½}±cûvòô¨;¤K§ö)G? 9ÊIf €¸*@€ÈU^2G ; yd<¡‹¡ìv€(8;Pa}š F… U™,ª4A#õ­3ûúný ¶ïÎ3‘}?‡B‡át]›\c–k\òöuV„ Ìq‘ å˜kºéŸ¡÷[¼ðGC½PD^h…ËP^¾A.¿évY½vmÌN›¾b|ˆ›É<ð™Ž|:‹¨}»¶ò;F2‹(þõì+¯ÉÛŽ—æ/È´oèöAªûrNnŽºï>rÆ GÛEàôûñÇ)¿É;}*Mœ$å*\±É3ÿà6t{9dßá²Ç.;JAA¾üçµ·åOý'¦çäçË£wß$ƒ·SúDˆ¹Ÿ{@@ =—5"=æ© Yñˆ<4HwCÄXöp¢àg5Ù;@¤×ì}]š®Æë2uU:+I÷ͧŽ;êfßk„9¶ÏÛŸ©W²Ÿü\w=okÙt ¸<óUØøÉ×}ó¥Á! E Ú÷çïÙ}S~/mˆ¼ÔÍ”å}3{è‰ç_Š98¤Ù¥v®L” x ß;ñƒÏ®FèÝ(5·.éëóO;ÉÌ"úcsI¹$Pi^æþ·»î—ÙóçÇÕpn×n|çrL8''úr¼×>¼YŸÞrûõWIaþN,Z²Ìz¯ÐX³Œœî»µmÑ¿²ÏžrÀ^»Kó ööÅ·?È•#ï‰ùçãÍW\(íãîlVDvëð‰ €€¿ù«½(-žˆ'@ä±Aò¸Lã({<¢XÊ c’ö¸¤~Z#SžJ³¯³t’aÙA$³Ûèÿíá­HcKöu½/ÖÍ.O¤ôÁ×퀖x4ø£×¬ÀOç…tFPh9ÇqØG*OZÏ{°üˆÒÚ#bx™yËmþSfÌžóM‡6QEú¡·)Eô=ýÛ¡°…l×¾­‰^‹ü¸j­¬ªªŽ~SÈÕýûËW\,­Zò—šˆ‡ /–KoºmÓ¯DLéò…ÀßB.?'†ì“íË1<"j’‡o¿Izõè5 Èd23›÷ãÏ¿’±Nï™æZU5´ÿˆÝ­ÙBú.»ÐMßitÖå-O,ÛIG"—œ}J,I“JC€()>nF@ mˆÒFσÈ,lÅ9À-@”ÈpSp°Åî<¡ç‚õDÒMÿÔ÷!Ù[ÙßtTVg,§±ÓFú´—y ¾®Ï×?öfÍöi8Ðó›®4ò -‡õTöFçýràÑò òIšúÛtyß(©«‹úm¨Í¦ooû[Nï³÷5™}lçg_³ÏÛiô3ôš}O¸k!÷y ã'; ®?,·m×FNèßKµmcMoüvåj¹oÊt+¯5ŽeË1QñÛ®¹Bm¹E,ÉIcÆŸ £_zUjk4g÷C¥±û ž î³ö5;mh:½nߣûº§ Þo¸æ>lÓü‘l_¶óIô3Ï,Ouö‰ÇÊ{ï™h܇€/êÌ´ýoœbÍÿÅ×RY©«C;¿épÜy9Ø,!·ëŽC$߬n[_Zf‡æ/Zîr“s»šeéîùWÑwâ¹½ˆ^ý¸Û#@HB€w%Ç­ ^ Ö‘‡ÆZÂW$ÂÙÊ)@dXExRØÓö¨UØ‹N6yNuˆujOûµÜÁJ­¢àFòðþ#OÿŸ|j¥t*Ú¶)0¤©ìú£ xß¾fÿˆ¾¼.}O¸kzN·†çy Ó'; ®ñ¸{v•Ó6ï-ÅAu:òÓfÊû‹–JA n*¢s#vÛEþröé–4/pߣ£eÒäÉæýC*hoºÜOí}½zMÏÙ×íkÁyé5û88]Ã}èÃZÝ’íËõ¹$÷§9w:D®ºð¼ä2ân|"0gþBš(ïš%ä–¯\åZ©m¹¹¼Ïp³ énÒ¦u«¨Ï©­­“+FÞ-_Mþ)j:ûbŸžÝå©oOÙìUD¶<Ÿ €x_€‘÷Ûˆ"à;XDk‰Û7²‡ Ù#Qñ<ßµŠçž&ÏI üñ<ÏÕ´~.»Âx¸üˆ\í¹Îd®ï5¸öö»eCEE³ƒÄ›Dö ýQ`ïkyB4Ø×‚Ó§Ñë±^³ó7÷x Ó'3 ®S){ÉñýzɈîšTGÊÿ7w¡Œž>GòÍ~s›%h‚JEÅÅrß×Hn]›»…ëFàÒo•‹—õ'»o†öKå ½¦çâíßv> ÷y [%òH9Ì›¥W÷nòð7k±ØÈHuëKåÉ_Ê;f ¹)¿Íp­Ž;vƒöþ£ê×»gÌÏõÄsòÒÇÆ”^—4Õà‰Rµ J•4ÏA@ yDÉ’„4 òÊøFH±c:L°ì¡"{ô*¦g6$²G·â¹§És,<Ït-­ŸË®(/?"×z®s¿üßwäµ±ïJù­áh[¸àö?ûå_õèöÀºædÿxÑÁûöS촡ׂí}½§!túDƒCZ ÷ìб½œºYoéÓª¤IPN=W™å…îüé7™nõåiÑ6ÍÓÞrórå˜C’<Ì>Åg¤½úÖ»dÅªÕ )Bûi°{è5½Å¾ë5½§!­ú°–F·DûrýÝÎÿÙ±C¹oäõÒ¶™YÎ?™pO ¦¶V¾úîGyç£ 2qÒwR]SãÊà ͻìö¶£y¯Ðž²ÓÁq/ùööŸÊí£‹©l¹¹9òÀÈkd˜Y^.•¢Tjó,@’ @”œw#€@h"µ„)yôSI”=8@¤£NñnöèV<÷5yNåç¹®¤õsÙÄå'@äJÏu6Ós¯¼NV¯Y§=*lÆ›CzÙþ±Q?Ð]RR"eeåRS[-¹9¹: !`d^f6êÓÕglçß8¦×BÓiІ|<ÐéP×h°ç¸~=å°^ÝÍ’r¹MÄÕl¾ñ|dÚ,ùÝ0¢m¶Rã49ÒÞ¼Ïè©ïn|š£&ó-–›îyP4Pè_Vª†¾¸Ã–Ö~¼¯ ‚Ó_ Þ·3 Jë~l•Þ#åÒÀÐm×^)½{¤nF‚ý|>pZ`úì¹2ÖÌ÷Éç²jÍZ§³ä·ý6å}ö”}ö&-KŠçãÙùyÚïrᵷżºôœSÍ/#Ï#IK€ÈF2A@ %ˆRÂÌCÈ.H"oÄÕ(I–ÝÙ#Qñ<Û¡çž&ÏI²üñ<Ûñ´~.»bø¤üˆï¹Îf8õ·érëKMMmÄŒ7ˆì›ºÏ>ó ),,’òò2Yµj•,_±B–-_n‚FeÖ—.[Wkò®«« šid´ç§vl®y Ó'ÒÚu3KÊ]8p3ܾM“YZóJóþ§I+VÉc¿Í‘ æ·Ës­›Þ~kò¹!Y¾y ùÈ«.•m#g-é³çÈ­÷>$啿8¸ïÙûšÌVÖsÁûö5;­}MÏ7“ÖýØ*½GÊ¡e±7U,)*”›®ºLnÞß>Í'¾Ð@ÐûŸ~a†~Ÿ5ǵ²wïÚÙZ>îà½÷žÝ“[VtÙŠ•rÆe7Ȫձ±Ùw¸Üxù®Õ-Zƈ¢ép @o  òV{P2B \€Èƒã1[;Pv Õ$=¢sYMÂàÑ/뾞Ïó\Mëç²+ŒÊO€ÈÕžœ\æìxåÍwäÕ·Þm°°sŽÒ4çs¶›÷Þè–kÞ•c/7W^^.åÊMÐhµ¬\¹R–-[&KÍWii™ÔÖšàGn^Ð #½;Ü ºž×=æš:}"Á¡:Sî<ã²g׎Öû†ºš Qh>j¶lC…¼bÞ9ôé’Rm~°'R1ÍïØÃ–Ž84Ðzž­±ÀÏÓ~“;~Dªªì¥žúZ ™ý×^¸¾œ68Þ|-äØýØ*‘GÊ¡eÑÍÔý-ZÈ —^(Û ÚJÙð…@uu|öõdk–ûâ›ï¥Öüwc+1‡ì½û.r° ÐüaðÖŽüŒ¯0Kšþù¯#å׳c*òà­È¿þ~£´((ˆ)½Ó‰9-J~ €¸'@€È=[rF kBD߈«]*ûóÿŸú®óx6‚CñyÅc›’´q¶wJÊå!ˆ¢à¤ûREE¥ÜùпdêïÓ›eS`H/Ù?6ô›'x_DçX¢Ð Gý]9fà×Üc:­¾¡²²R-^$³çÌ•éÓg˜ùÊ"½[·†çy Ó‡«_}#ÿ©Á¡6fíÜ-ûɰΤÀnBüh0çÇUkåÑ_gÉ2ãÓÜz¤ôÛ ÜRþvÙERdfc°…°D¦ÿWUW‡I`KÇ ¾ÇÎRÏ5|Ïx k©éËvmÜø´Õì¼[´(0}÷bÙvëö)>ð¬ÀÔßgÊ;f ¹>ý\Ö™_€pcÓ¿'vÚ~°´Ï²×n;;þsý¡'ž“ÿ;6¦¢wîØAžu‡tìÐ.¦ôn$"@ä†*y"€ àŽ"w\ɬydœ%¡öp°ìñˆì‘ÝxÊ:vã…_ä§üÒ:hß(ßTø°üˆRÕ9xÎò•«äÊ›ï´fú„Þ¾)@dÿØè¶×ÿhˆ ÍSu–‘P¯_¿N~™2UfÌœ)k×®³fÒù›g{ Ó'2 nÆód‹V­ä<Ú¼M«&ƒòªZj–Þû`ñ2yiö©2Ñ~[Úª~„?šü@Ž®¥y7Ôƒ·Þ ;vŒ‚Ó›DU#XßVÖsõû›º¡׿_KÛØÞrÌû·ì´›òªOk¥Ù”A /Ô…Ì0ÐA`··Dú²“e²Ÿo×Õ–~¢` ö½(°Ì,úÞøÏÌl¡OeÎüE®±o¯f ¹=ä ³„\—Nîü,¯¬ª’ýO8×ü‡þŒ¾šàíã÷Œ”­l=¡ËW ¹ Lö €8(@€ÈAL²Bz;@4Îâ;‡ËO€(‘‘§&c7—?¥íçç²+”OËO€(¥½<¾‡Mþé¹û‘›wÙKlÕßkpHÂí%æìßXK ÄyyyfɹR3£hŽ5£hÞüùæ]Hºüœþ¸jø‘åŽOÝtÖPI~žÔ³›Ú«›´73x6† Ì5¿iþŸY äk3Ш±ü€nò9 v~~\sñy²ãöÛFI•Ý—6ˆ‚gÙÊ9’oÚ±Ð,wVd–P,03Áté³|Óg ­™oöì¬|s­È\«ÿ¾±[Ró Þ¯·®®®•*3SLÓVTl°ÞÏUaŽ«Í,&a§³úôKßÙ¥ý®®Nó1©Í¾ÕO´¯$HЧ/'Ò;4û+¸¼º_b‚–­Zµ”Ö­Z[ï)[»n]ØG ËÂÉ4 è2l¾üFÞþðSùæ‡_¬~îF‘Z›ï‘}÷ØUô?º”›ÛÛ¬¹óå¤ ÿÓcnýë_dÿ=w‹)­›‰¹©KÞ €8+@€ÈYOrC# ¢°+ÁøDÇŒ›8½Å ²G©ây~“ÒºPþxÊ“TZ?—]+îãò·1F >ÿ<©æóÂÍ9fгÉ÷„ –LþñÄ32ñ›ï¤Î,ÿfoõƒÜzdÿØÐjÛûz¾1C¼3ˆ4‡p›/]ºT¾ùö[™3ožyyŸQ¸„)>O³×˜Áüî%Er¾™5´]ûÿgï:ࣨžÿÞ¡÷"MŠ  Ò‹ {/ ö‚Š(¢‚ v±¢Ø»þôçO±!J/Òk-@Hï=á?³É^ö6{·åvïv/ó>»ûʼyß÷vï2ßy‰†Fqw´IÌûëH’C!ßÜÖšärÔÚ… ‘1rè¸çæÔª6ÙòÆ‘ˆ2­¼cеK3z4Ea‚相Ç)I‰é¹P(ü'Ê“äH#âÚªÁûˆ "…jjj¡öX-” qZRR ùH¢”AQýuii zß•ào¡J7ÂHÚ¿ô¼¡gúi¬´\í\Ú^<§£ø!¢,I 4pÇFÇ@BB~â¡E‹ÂykD Óç÷… awêÅ.™ R„…3„À†­;`†[¸tzÛ–Y¢íS7lÈ@8I¡ÆøuoŸŒÌl¸àú»TÇ5é²óá¶IW¨ÖóG&ˆü2÷Á0Œ#À˜ƒDæàÈRF@‚Àøñ€o—K2tê£]ÆÓHµDžÚkÍmAZëÛªžEØûmŒNÖu'¢ˆ+ü—UAD^ S›oòç¸ Ç äÁXg$oLQ>%26÷ DÈþ#Ã6Ë÷¤í+VA!Çõð1Òï ¸'íÇu…”hôR¸qË0¤ÜG{ŸGqó8wdeP¸]1ë–mZ¦ÀKfNôtqÊàN‰(‹ë gpÆé§ ä1¸D™ÆZÓŠd­'"QŪ 7/_ÑX€ç´§y«Ñ~_Õ¸Ö(„}èž“ÒºËľèšÎɃŠV))„;DŽaF! c‘JLL„„ø8HJl‘è-ŽeämE‘H£>¥ý’N¿üö;D©b×nG&ˆÜàà‹ pøhü²h)C‹áPF¦eôèÚ CÈ‚3Æ ‡äæÛÓçÊÛ¦ÁÞéÇ9I«çêzy¬è§&ˆü4wÃ0Œ#À˜€D&€È"FÀ§D vB÷¿b‚H; ±WéÙ¼b§Ž¡^o&ˆÌ[ ¦JÚ¶}öE4&× r•É!* !:'ƒ·h@¯Ë7bޤyJ¡¡!Bè¹7á>E[Ñ{¢è k&©A[K¿­Ð8>{p_HF/ i[%4¶oÇ}–>FrhK^„ë‹/CâøôŒ g·.Z†Ðäê4D¢'—¸¶ë ð òeæ”§BNàеôS…iUÕUBˆ:ÚO„Â5VWU y$±ª² I$q¬ûBèE„ ·9‘AB(=ì#2*J {¢"1Ć.ŒÄ#=ßEâGz¥Jï1O<2A$"ÁG;!PZV.[¿ü¹ÖoÞn™jÍàt$„ho¡^Ý»ZÖÁk7n)<5HÞÊS.à9!)-/ Ø5Dƒž;fF€`t#À‘nȸ#À¨!àD‚Èbã¾?"ovµ) h¹ÅØûelNƒDo&ˆü²RôwòóÂEðÑWß a­”É¡:¨N²hì è eæDb?äÁ¨Û1ÈC‰+VBêž=.ý£Õ×ÂÈC¯{|,<6°°ÿØ¡Tƒ7Ã×ûÁ¯‡B!ëky>6 á¹Ž· 37é² á¼Ó'x«ÖdËÜ "š)âÍê=ˆN3°î¤rü¯”HrPòp–kæÖN^¨ÒV¡ºÇ,&ˆ`ž T_T#/< ù9CÈrâ a/3_äYÑvõ¿›àù¹ï»<¦èy0vøPxàöÐC0ÁŠ. Ëd‚È0tÜ`F€ð;Lùrîn22®º m ‘R1`/¶3ô·š òfK2CKeXŒ½¥º“p§ê/Ó›¢°eË ØX-GXWAbnÎó`Í¿„ýNÉE*ƒŒÝžÎÌÚƒÈÝ8ß Qˆ¬ÿÝëÖ¯³Ì€GýøòÀ‹ÃÐZ ì=1Ü–˜h¡÷€eGs\ŠejG3(2ð <¦ßy‹ZwM²¼Ž z*‘¸s_u÷1"3f΄鰉J#a‚H Îó'è)DŸÌì\˺îÓ³œ¤Ði£‡C¢ä»Á²}Lß{öö9ëÔ¡m@ÃÞy DÞÐá2F€`FÀ^0Ad¯ù`m @ÀiD2C¹s`%Aä‹­ÔŠ±ê’éìu飷²SõWЛ=ˆôN¾ê“ö·f¡a ÷r¹(‘C¤ŒH‰GQÁ: ´9‘º5›ö=9tè,[¾2"¾çèß§—çJM´¤!Ä\"а¦ÅsíDVÌœI±‰jš3A¤†—ûŠ@ê¾)ôë_Ë 7¯ÀWqÛìÛKØWhÂÈ“!6&Æc=.0&ˆÌÅ“¥1Œ#À0V"À‘•è²lF ‰"pýõiiö¼C¹Š›MÙÉVª/?c¯[?µNÕ_Eï°îÝ!éóÏÕFoûò !ˆè&_¸t9ÌûôKÜÛ§­Ê" DsAy¢Á\)_¬ãËDb¿$K ììl$:|øˆá7¶íôÐó e Cq°›¯¹N5Ü4o+åžœ—ÛàAD‘ˆ¾¸Þ6‚HlàñÛD -(0A¤%®£¼‚Bøýïå1´sÏ>½Í5×oÛª%œ5n$C£¡CÛÖšÛqEó`‚È<,Y#À0Œ#`5LY0Ëgš Ó¦¬Yc߫ʭPÜL‚ÈNvRÝX{Ý:zkàTý5èuÊ)÷Ê+ÞF "ˆ¾üïOðÝO¿Â±cDQj0Š7\“ÅÙS~]-*7bÎkv³fÍ ¤¤6lÜk×®•Ò|´ËCÏ4”‡M]zÎYpÅ…ç2A$ƒ¨ ª¬/¡õÞ0êQC]™hÿ^ÚD ­ƒf‚H+R\O ªªjXþÏza_¡ÿlÀ—jÔš*ŽŠ„q#†ÁÄñ£apÿ>ü,5„¢y˜ 2K–Ä0Œ#ÀXDV#Ìò&ˆÀ{ï|ò‰=®ÁPn…âfDv±“Â(@ØÒU©‘SõרwzþEÝz«ÒÈ•4QMm-¼ñÁ'°xŪú ’@JçT,ÐîÆs‘<ÒO™oÍ&ƒó–­[áï¿—ÔÉûÁN<óÑh<ö1§ƒ;oœ¡èuÅ©Ï!æ¨íAÔÎ8ýtÞiþ˜¹]=žÙD ú)0A¤ géB`Çî4ú =† ‹Ä½ôt‰P­Läú}q_¡Ñ0vøPˆB’ˆ“=`‚ÈóÀZ0Œ#À0Z`‚H J\‡`ô P†Q´ "†r=cÕZ× ‚ÈN¶R­ãvÕ ö.|9qªþõ&F!ìŽ; ñÚk}AÉmƒ† ¢7®Ÿ~õ ؼc'zI±¥ ‘‰"*óÅs:ŠåÇЃh2DGG£,7aTI!i©£ÐLCVn¾ÿø{ñR8zô¨£~ mºjèÔ„*Ö!R§9iÿ¡Gp¢pÜŒS DÒsT^wxö ²zÖtT=³‘*ªºÖW`‚H+R\OŠ@VN.üö×rÚ{ ]ZdêyÇvmÐSh”ði’lªlfL™ƒ#KaF€`üDþ@™û`š‡|Ú¯_z·ÉØ5ÙD­ÓÕW‚ÈNvRÝ({ÝúÊ8Yºçz*ô|ùeùèw4QIi)Ìxz¤gÕHêxŸ«›oœ 1š"ë-ÙD†ÀêÕÿÀ¶íÛ빬úaØé¡g="Du4ƒíÚ³OãMÔeK™¢Ù¯¾ •Ubˆ9÷ DyšÜƒÈ³æ®‡Ç+©âQG… úýwØ•º[¡ 2" Í;bS±g6**+añʵ¾BkþݵµÖ,ú¸Ø˜0êô…ëî¸&ƒ¯SÊ‘SgŽõfF€hŠ0AÔgÇÌX‹@ÚÕWCË}û ÷ܶEÒa$·J__";ÙIuãcìuë,màdý5êNfùŒüUÜ­ øê+éèy4Q~a!Ü3ó) ËSdÊDh#ˆ¬1êy=ÜÖoØ+ñqO ;=ðü‹@b|¼úÔ,HJHðY“̯#ˆÞ@‚ˆ<ˆ§žÝ‘ r 1çï™k¬“+ÇFª¸tÒxRGý†Qªb ò|T ˆz+–sfð#°iÛ.$…ÃKVBIi™% i'(x :åDˆ`KKp¶B(DV Ê2F€`k`‚È\Y*#ДH?Žá~ä=#m\£¡ÜÊ9k’‘ p÷yN:zA´§¢BââàÄeË|†,Ђ† ÊÎ̓›§Í0 O;D4¸Üog/¾Q°lÅ ÈÌ̬ ЇFvH°í¿ÿҳܼ¹†oôD˜5PÙHzÍf‚È+PC·e¿Ú ¢À­™FÀj5RÄxDƱ ¶–¥eåð×òÕ¾Bë7m³lxI ñpú˜á1Ô»GWËúaÁþA€ "ÿà̽0Œ#À0f À‘(² F€"p÷2)ª©B̵ d$†réÌ>7B9–"ðl„½¡¹tªþ:õ&kêQ$‡ ‘$ŠÃÇ£}Þé)h¢õ›·À³o¼ ÕÂjé(Ï‘ ,Ùõ*ÔÔÖÀîÝ©°hñßPnm ‰‰F~a>tç-0d@½ÓÔõµD§ Þh¶"‹ÈD˜ 2LŠB€"ôóŸK`ѲÕPŽÏd+RXX( ?i°°¯Ð©' ‚°°0+ºa™@€ ¢€Î]2Œ#À0`‚È pÜŒ`<" D5XƒÂÌ…âEp†rƒ1©@/AÄäIÀc³µ£yô®Â6{)¼Þ£LiFÚ?ÿX¼æ}þTW+ﻢW 'D4¦Pd*ó `ɲ¥Bè¹*$ÈüIÒ¶O†Ñ[®¾N=Bïôu}u‚¨»l¢ÂÈdò°™ 2P‡ˆ;€Þ«¿,ª !w4+Ç2­ÉChâøQpz %¢ç§àC€ ¢à›S#À0Œ@ð"ÀQðÎ-Œ"ADýG á¹«¿÷"2`(·+=“CVφù6\;^´m(2¨÷~Œ^F$%&ˆà´ÅÙw?ý_þoÔTW›¢2Adk¶‚ D1´wÿ>X³v­°7`%Q¤ †)¸ëBäØçŸ—ž;QO³ ¯ë• ÂXÏ=ìAÙa™¸˜ 2L›‹*.)……KV¢·Ðbؼ}·eÚ¶lÑÎ;\ðê†{ q n˜ îùåÑ1Œ#ÀL×|òh; %ˆHŸö¸Qœ¿ö"2h(·7­“CVÏ„ù6];^4®+2¨w †•;"‰^Æ‘*Òþ­ðúÃâ•k ãuš‘D6°fkP¡YÌÍ[¶ Qôeaç4¨aÆx—7rDcO=îºq’÷ºM¬Ô#ATÿð³Ad‹EdîÂ`‚È\<í&­¦¦Öü» `¹Å+×B¥ä™ºF`èÌѧœ(x =az‰†˜)žeÙ&ˆl<9¬#À0Œ# C€ " |É0># 'ˆè/ÁîQQ`ù_„ å>Xƒ-“C€´ªŠ×Ž×!Ô›L™{Ñî^+iÏ‘W¤ý_øêûÂÒÕk-"ˆl`ÍÖ¡B¾aPTR K—/‡CééPT\Œûœ3Í£H‡*Ö,„ú‘¢Ñ'…)7]oM?•j{‚(à Èš‰e‚È\-5mÿAa_¡_-ƒœ¼|ËÔéß§'z † £N¸ØËúaÁöE€ "ûÎ kÆ0Œ#ÀÈ`‚HŽ_3Œ€¯d ¹²¨HQhß댞D–%‰¡Û²>|¬F19举6µùÚñ8<ô>ˆäP…¬}nÒ£y9=5Û)(̵¿ðlÞ¹ÈHkFªó Šˆ3äù$ÃÀ ‰áåŽfež´4ØØäçç ã!Éh2 ŠÑ®”ÛI–+£ï^ðø÷*×m¢¹" fI@=ˆ¾€¬[L)c›“›´OOB|tëÜÁ4¢Z¹7sró á÷Å+o¡©{ͪ ¥M«–pÖ¸‘‚·PÇvmjpVSB€ ¢¦4Û§êoPïfFF,Ë”¸„QK$ˆz0Aäu¹øµð'Ÿ…=±`#ˆL0¨ÓC³¼¢R÷¤ÂÎ]»`ÿ‚‘V/Qd‚*¾­ ÙÜ ¢n:Á‹ÍðMnµv#ˆd˜ÑPF|Y;ÑL¹ã››_ÏÍ}ñýã* dú“aÈÀ~®<»œTãýòþ…Ÿ.Ž5&…+•/:*Æ&ì+4x@_Gfò1ðµ50Ad ®,•`F€°&ˆ¬@•e2M’Q£ mwJ©#zÅøð²w#™ ¶¢Fulበbr(€“ãµÓ!ô.ö‡Ð{H)µÃ{³Óš5JEŽÊ "ó ¢!&::°D&Ô‰"#hVv6lÞº22Žáçªð Ñ*zÉW±ÉªÈÅ«_+ÜÈL)ÃfK‚(à H+3s™ j@³¸¤&O ûjȬ? …¹³AÇ÷nTˆ ò¢}…~û{9Y¦‘AçLäÐPˆÆ8Òœ9LÉákF€`FÀ¾0Adß¹aͧ"P=z4ì)/WT?´Y3肆è0<úœìk>Ë´H@ÐDÂ^qJ¬¿AÝ«±…–«Q kd$´^½ÚC©s²™ Rš+œü›'O,Ad¡Aˆ"úAvvdfeAvN6dá17/O ‹ˆyüòI#ñ¨™§þ<¬Z·Ñãl×­ |üú3Ë­. °w¿þµ ÷Z iûÓ-ëŽ<¦ÄrNŽ#à &ˆ¼¡ÃeŒ#À0Œ€½`‚È^óÁÚ0AÀر°£´”( 2«D =ö#"²Èpò`_3,Ïâ†r‚H0/9l .ˆœª·8'ëoP÷l—ŽN•xTºëh=öÅ€W­Qrì‘ "ùÔÕ/š€D~6¨qCÆmò.ªDR!=Œ²pï"Ú³¨ ¨ŠKJ ¬¬ ÊñM4ªÇL‰0ËE2(6.b¢¢!&&š'5‡¸¸XX¥Çõ®~>äÓD×L)¡õÑ\œË*Å L)Ââs&Du¾:ïøâ‡ªxþüé[Ü áË—K‘rä9DÒi“LzÀ"kl–ÒQªžK½|ȨJ!èªéƒ{r”"QTV†o4à—Rn^¾ÛWnšÄM3ˆÃ$ßlˆÄÍôBÃÂ!7íŠÄ/±R|âwßQÕA¨ ™¥L)¡â‚ˆ‚4Ïa!¡‚G˜ôš¶êÚ¨­®…Z•9Tù¹´^CCC}Å_R4RO GQßcHørû1AB¨¶'^zKÓ~3ïe «Óæí»ÐSh ,\²ÉîRKº Á{aØ à¬ñ£`ô)'B$>û81z`‚H/b\Ÿ`F€L{î™Z Êų3ñåmOÙ,â0"P[ò$Ò „Mì3zÔ–DÞˆ3=2Rר»áäTý êMžCt£3…·{±%Úº)ôcøŠnp9ñ"x¢§ž…=û¢‘·ÖØ<ÈMS&ˆ¤J½„0زø¯ž Òúâ[8â7—hr§kªOÓÛïÍ“6Q>—͇R%&ˆ”Pزc<ñâ«P‰¤a.Ÿ'_<ˆh£## ½$bð‹Œƒ| Jû¨Á{°¢ª*Ѓ©¸¬ŠÐ(_U]ãVGYsssI×pÜï&uŽB]Ã!"<¬ŽÐªïŠêT‘çê[ZV…¨kYE¥!]=DÔ}"°ÿYS§@ÿ޽̨M¤mÙ±n{ð O5•bp>~ÿr„áÚ±"edfÃ/‹– „ÕÁÃVt!ÈìÚ©œ¤Ð™ãF@ËÍ-ë‡7 ˜ jóÌ£dF€˜ ŽyäQ0¶B ¢Z´O¤RôÅ¢$¢psšÚ$œ˜D‚ˆl*ŽMNÖ@wªþ>èMaåÊUø"Žhÿ¡$t–ˆ`‚È>·çO"AtÀ A¤°hüN9øY§uh&ˆæC©&ˆÜQ!¯Ü‚bضgü¹t9” krNvä»íûd„ ¢åŽ8­’rˆ¼q¼%q9Ó—¸ W~ä{kbzYrB´HŠòlCÆÇ%¿áÌ•å:¡ýsˆ$ÊÌ+@½=¿)àj 9Q"ˆ(/11 ’SR .&C ‡¾=ºB‹Ä¸:½$í|š™ ×Ý3½ 4 ãÚKχۯ»BS]­•ÊðGôßËÿŸ.†u›¶jm¦»^bB<œ>úTêݳ›îöÜ€ð„Džá|F€`FÀ~0Ad¿9aÇ#€îý‡Ð8]Œöo‰l.ahç µª'‘F›·þUFQµŠ¡>Pºiê×ÁØ ãsªþõ¦»î`EІÞl‡„ yòµÁ¨Y LöI† "‹Æ¯‘hM·œ–h¢‰ ò0J 1AT‡ ‘0™y…°yÏ$ˆJ <eîˑɩz‹`;Uz v@¼ïŽ A«e½Qì²NhÓ"O>&ˆÄc“ãO ÑAD^ DæO¬*Aäe>”´¢ŽàÅÇg(7™¼=é°~ç^Áh]ƒ_žd4aN¤íµiãصs»@uÆn^Eb}ù‘BÆEEC‡V-„}¤¤²åuéº¡çÆ¥!¨KaIÉÎs…¿k\ËxéF¤CÛ–ÍÑË)ZØÿHª9D=‹õ 7 =w83ŠKË4‘8D-øí7صg7ôêÕú1Ñ1!Fû‰‰dá@{{ éÓzt°~±o+Ž3Ÿ~_¬-ÞjJrsøà•Ù>‡cK?r §…‘;‚áä¬J½ºw‰BîŒ1Ã!)1ÁªnX.# À/F€`FÀ90A䜹bMÇ PO‘¾Ñ^PªâE$W{$‰È£Á-IlCnù¹ +Šc "‡cßÔÂÊ•9„÷œÖ÷ZG´?QÄ‘VÔüToæó/öݩڽTnV¿D vc?!¸n¼D*ó¡¤5}ëOM¿_©¸Iäí:x¶ çP-jä ûö¦A~nœ>a‚&‚(&*:¶JÖMëR&/Ù@{™™Bq=tjÓ¢QgJR}ôCR(<Þ¡£9PªÁ3…ðýmáBHJn ]ºvkÀWªˆD8Ý!!Í Îp\Ƕ’çœ~ôõá­¾Ô¤0‘wïÌ™}zv×T_^©¤´.]…ÄÐظm§¼Ø´ëè}væØè-4ºwéhš\Ĩ!À‘B\Î0Œ#ÀØ&ˆì3¬ #4H¢j´‘¥a¨+æ„FC¦z)¸Ço²¸Ï¯[#¡Π19’ r:öNÕß Þ¹h÷ËÁÄµØ é– z] E›%&ˆìóßkó?‚%«þZ- »†Ec9A¤õ)oˆ}ÖÄt‚½0Æœ2 îž|Ϻ9Q@9ºÿ½v+••kVŸH¤ÊòRèжµªW p[µD/˜:ÂÅS'F–r†šËÊ/ô$ÒP~JR¤´HlôJí!¯¦iY<š­J>¶3ŽBz\ÑZ’šp¬ c‡ôƒ( ß礴lÍz˜öÄ ªÄ¤8¦Ç§Ý)xâˆ×ZŽ´'Ô?6ÃÏ.%+ÿÁÚßèÐ"_¬CäÕÈ“‡ÞB'ˆ^²7¯ÄŠ|d,D€ " ÁeÑŒ#À0Œ€É0Ad2 ,Ž`$Áq=òÐÆ©fÓBI/Îâß·Áð-™SGi°7KçËvçNÕ߀ÞôÊz:ÚU+u´¥5™„¶h"cÅÄ‘ˆ„MŽï|ú,\²(Ėפqâ™ òŠ¢¡B‘Æ9‘w‚7åé£G­×^%/j×»†©ÐUF !A$:2I ÜÇKªE™mš' {ùP˜9OÉs‰§ ùiöúq‘) EºÎˆ˜¡1uëPç…#ÕIË)i}¥ŽI¿l$´2róÂäyK…HÖј„¤&Ø%¨ <®3ôêÔΕc÷“½ÒáÆûfBiY™&U¯½ô<¸ýº+5Õ¥J$ÿgôúõ¯eˆ}žævz+ß»§°¯Ð„Q§@|\¬Þæ\Ÿ0&ˆL…“…1Œ#À0–"À‘¥ð²pF i" #ˆÐX©¸¯´–=Qä€Qø+ ƒåÝ‚!oe¯k2©8Š Òi›³Ú¨“õש;íóuHGH9q®Bñ¤;î1N6S11A$"a“ã·?ý_ýoÔ [˜Ç$™@uê ,%ˆÖ‘šAUÞ˜ B |À"Û+Î?.=wbPá¤u0?/_å•Õªž-ryQaÂ=ò|éu’oj„…S'B´·…šS 'ÕKéœÖ…–‹COéCZí‡ý¥2ví? rÎ[ÊÇ=–*èKF‹ðzAä©EsrΈ½‰¶MYAQ1ÜpÏÃp(#S“NÃO:æÌ¼¿!䞇V…EðÇ’1´}wš‡Z¾g·NI†³Æ¼…:µwfh?ßQ` vD€ ";Î ëÄ0Œ#À(#À‘2.œË0> 0n€Ì¦YvÌýèå ˜<Ù4ÅYeåqh×i‡v3©]ÃíüÞ”Æì‚H†½ßÁòµC'ë¯QwºÈk(ï±Ûžœd{$â5B¼ßê+ ÑÊ•JM•× ³:L™öÛÒUkàµ>…j SÇHo¹i2†ÖŠöÙx톘ŽþÝÚÉ…H½5o^݈|\zaaá0eò$uÊÐ AHû0 ˆX¶y7”–koT_3*"Ü+ADË´µà=ïñ3–2=zöÉÒ´¿·AÒ>IÚ¦¸y"iù¤6© zþgåÁQô"’æËõ*()EÒN´˜Ø8Ù¿'$ÆÅÈEÚêšB¾MyôiX»q«&½ºtlï¿ôÄÆ(«¿˜W¬Ý 0„…¬«®öNÀiêT¡RTd$Œ~’°¯Ð‰û¹­…êœÅ&ˆ;wÊ0Œ#ÀB€ "C°q#F€ð†€ADÕ)ÌÜQüÛ ¸[‹Æj¥²úÚШ‹/¦:-ÑhAyÁÞ1˜;u :ô.E¯¡Ã9ï3Ek‘ÂÊQx9yb‚HŽH€¯·íÚ ³^œ‹Q}x'©>:ŒØŒ " óŽn‘9‘kß7ßû8yQÐ_oÙsRgB¾I¢–jñ!˜——‹ÄFµ ±ÑžCÌÑ~,ƒûö"^”’ÂO¥jšò2²s`Û öþ®O®Ð¦e²[ÞHª¨6¥ö´Ñ¿Ûv@e•/E\ÓEå’òcмy UÏÒ'7¸ëپ߽3]Ú6½øö‡ðÍÿ~Ó¤_†l›ÿÊla¿+yƒ]{ö¡§ÐbøíïåžCV¥Áýû §Ðh7bþž×¼Uý³\F@LéA‹ë2Œ#À0E€ ¢ÀâϽ3A‰€‚ˆÆš†í%¯ Ù?‘ jîH±¾$½mO™`Û øT8u ô&Ûyáå!ÁZˆ¶Q%[Ÿü‰j÷’-‘ "-ú±NFfÜõè“=ˆ4,%5M%ˆ”VR§Ažç"ˆÞ}×”‘’Ñë³gA»6­L‘ç$!Ë6m‡ÌÜ"ÜsËY! D›7m„ÔÝ»„\šoÅvëÎ& t1©ÙK9mÿxsþÇPID—øB‹´R”®é#*-–ãu+·]-tëÜIÐO¬"\(ü'6U(rey’AûíÌ}ÿ#8xè°«®ë„~d!΂ªõÏœ={€'h"ˆBCàU‹9°K¤ÝN~üí/xú5m÷n(þø|åɇà¤AÇ»†‘“—/B po¡Ô}¸w–E©=>&N-„‘k×:Å¢^X,#`>L™)KdF€`¬B€ "«e¹Œ@FÀ AD¨ì©¨€j©S${¤y^à#[Gs4t“'„ÙWlMiÄÝÖX;u ô¦õž…Ä«Zlžæ‰î–.•ÆSb‚È2ÊÏÍχ[|ª¤o÷kX0žÔ5 òezRΩùøåEFö·M"ˆÂÑÓåçgCr‹$§"bHo Ãõ÷ú­@á̈üÑ’ÈÓhéÒ%qDÜ د/ÜvÝÕ*s´b)g¡Ñóo¼ù…uZˆHYʯ%åt{'%&ÀwÞ)õDb5Ù\—bsW†ìÄ[ûüRyóƒO`ãÖm²Vx){Ö´nÓF+X+7Ρ}ˆccaì‰ý€ö²[Ú°uÜ9ã)Í!àî»e\vÞ™èMUKW­ƒŸ1„Üêu¡FãzÕ;~ :aäÉH ‚AýzëmÎõ[ À‘-¦•`F€`4!À‘&˜¸#ÀèA@… "{‘D5dÐI‰jŒ0lÛI¢´CˆïéŠåv:’®LY8#2;–…=™+Ú‹ÞdÓ#+)…“ËDrˆUov>5ňê„ä·û„ "5ý\^T\ ÷?þ,dæäÔõìeÁhQÍ‚ˆžfœ0™ j•œ />>âãúhg´ÇÍŸk6A’>Z·#¯¡ ü!ñïúu°ß^áœH ¥4bØP¸ñÿ.Gï$÷ý`¬XÎD>ýÊë@€B¢N¤OoéµT¬C·x›V)0ãÞ»o'i3¥qI›+•«µ'ÂìýϾDÂc{sT„dÃ/ Hüâèܵ+ <Ï£tÍO4zCM:¢"ÃÝåø*#3®¿çaÈI<}Î=} œÆ8 !·.] EÅ%*-Œ‡„4C¥þ¸¯Ð(܇ì$Ä-˜ nÅØ&ˆl2¬#À0Œ# &ˆ4€ÄUF@* #ƒ÷~|³ Š{iì‘lQh'¢°s lCEYVt´-AD)''§êïEo²p ]ŽÂÉQX95Ÿ·é£Ù Çû£#ÚéÔ^áf‚È’(++/‡Ys^B‘¡Ö×ä3Aäðg…¯øÉÛbˆ9S<ˆðFíÙ¥3<9ý>ˆŽjZ{‹”UTÂ_ÿl†RÍá_Mq7óóàÈ‘CpèÐ!(--s½x"ÎÙ°Á'Àÿ]|‹ ²r)—#iõÆü ýp†Ø½¦£°ž°&yÝs˸_’÷5 6µ/jOaÓ¾ûéX²rµ £ ¿tˆ¤‹‰öíÛCÛví!!©9„QlR/_\òAÒÜÄàϸ¡ ÚFDG9î©tÓ´Y°;m¿\e×m1¬Û‘£õ„ŸÇZÆ ºtlg£§Ð™cG@Jr ジ%#`3˜ ²Ù„°:Œ# ÎÚÁ·ßqq1>¸ æñX‰Äœ`D€ ¢`œU#`4D‚†hkH%O"Ô%»F4Ú:Ú¢ #ívJ¤›- "¶;áéÒÅ©ú{ѻˎ¢½³ù3V1‘B]½„•sa‰'LIѰÁ9y;Ìyk¬^„xY4ZUõ‰ ,ÇZ{ þz"¦DxË<¼ó–F¡Ð‚Q"ˆ!A¤ÇƒHŠITD8ÄÇx&Tš'ÄA‡”dàçNÚÞÌs 9¶÷ÐQ¨@#‚‘‰céÚ¾µ@Þxj¯6-_$ƒÖïáÌÈ+,ºR’[„„yxéM$›<ˆÆŸ„Q”=&Æž8"е;Ðéïk`úì—¢FÏnÑSh4œ1f84Ç7³91M&ˆšÊLó8Š@—ŽíàÉ];kÂÖ©{.¤9ª¹“+2AääÙcÝõ"À‘^ĸ>#À¨" FÉ å¢¼Ch(/6`Û+ÉÒD!ç’Ñæ‹ö ÚÅ߉ì1¶!ˆ<`ïoL ÷çTý%z“×\)®ó\´RH9å×Ä #$¬ó¶ /©Id‚H ¡”/_³^™÷¡°ÏНÝë&ˆ¤–d_;‚öJp˜IÑ/÷Þ|Œvb ¥oD†.ýw+d–@­bE ŠÄb—¶­V^'L錫3Œ€:Þ"‰¡\IPÚ†Ž Qj2‘Cv"‡¢ðÓí1øÂÍ()arõm ‚H{“‡m¾8‡êOv;úãÚ¦0r8Ž*üh±çé±e¶F;h­m‰ " YÝdwÚ^xì…W…à¾öÅ‘qå†uQ’™QLt<>í^8®{WQ|“:®Ù¶eå#ª¿5‚(´Ytj›â—Pg9ùE‘“oØY ŸãÐ&9 ’“â]óïiý¹*à‰ÚŠ’ íwàH–×P1F ¢°°p ë׆öë)UÓïçù…pý=ÑÌlËûG’— Igc¹aCyƒqbš2L5åÙç±ÛÛ&]Ž^ë4Rqïtø}ñ 8pèˆîø8ô€íÛ³;ôîÙ÷$ŠiTŸöõûséªFùÁ”ÁQ0Í&E &ˆÔârF€Ѐ'‚ˆ …š£súãÌhŽUH Ñ«(ÿ~7ÛƒC®õp‚H#örÝmsíPýéÅø$…rðc)$έávR. Pmw™Ø²áÈQ¶9ËÍχûf͆‚Â"ŸuÒL]A>khOÞà0“ JLˆ‡Wž|Z$%Ù‹µJÃ}{¶¤‚Š ÏáÎ<©à•  ¤Pj­š'A«äDÞ=žú–æS?û‘p)õÑS%ßæíŒ„­/oëOìÛ9D²3s Ÿ1ͼÐKF ¢ÈÈ(èß½=tk×FTÓïÇj ]x×ÃO û&XÙy¿^=0„Ü(˜0òH@¯)NŒ#P‡D¼û!Ð:%¾÷ŠÛ>xôûå•w?†¯~üUQá(ÜSpöCS`øI'¸•ï;x®º}†ÖòkÅ­©c.˜ rÌT±¢& À‘ ²F€pG@‰ Âßz…Þ:ŠÛ—`(.5Û‡¹ÒºD@‘WQ†ž£ðsô±‚,¢‘” Ò‰½#[œ;DZ§ô¡É)„\)’B´~‰²b]ÑÜÐÚ¢u›‚/.é©ï.# ‰ ¢,luvë´Gàh¶ïo¿3A¤ZÕn(3 ¢Ö-[»/>­_É iQo…,Ú° Š‹ uÈ#A$™@Úשw—ê®6º{¯k@k¡°¸âž>f¤Ž­[BB\ Ð[Þ’Ú$­±`ǾtUùF ¢¸øB/ˆÄ7•ž›û>|ÿËBKºOIng)C;´³¤Ê8&ˆœ>ƒ¬0"ðð”›á\ /'&z™âñ—Þ‚?ÐsÈ[ Å?8§ß}œ{Ú·j¿ø&ü²h©[^0]0AL³ÉcQC€ "5„¸œ`t# 'ˆTìžä“]ƒBr¶ 䜼OѸƒ¿}h¿¢(<š•h#ˆ boÖØM‘ã€1Ðú)¯ßW¨ 4çô2–•‰ì†RŽˆ5¡=˜ Ò‚Rê<ûÚ[°fÃ&ŸTio¼ŽâŒGy—cíš zƻԅYÉz ˜1å6ã AË_Vþ+xßÔâCTOR$ˆdH䔤ô"²ÆC‹äïIÏ€ŠªjŸȤzdxtëÐFð"ò„…Úƒ_›˜L ƒ—•_èU>50B…à¨ØÈ8kø·>ýyñÝÏÀœ7ç›Þå‰ûÁµ—žt¤qrbÏ0Aä.qÚ¶†î]:A·Î€ˆ’œ¼ض+vîÙ×h ½ºw¼IÉ«&3;ÎhTGÌ Y}{uǸQP…D É+-+‹M?vD¯Þ/ß~ÑmOÆÞú¾ýéwÍ}}øÊl 9×ÍUÿБ£pÙÍ÷5 WÒ ÈÖÆV\ZŠxíÚ§íi£NÖ)-…1§í?KW­ƒJ4øHS(þ{<¶ïÖ¹#´hž1ø7LYy9ìO? »÷ò^Ò›ôÌ£(Û(ADã¤}½ztÅ~bQßð çwê_ƒ.NŒ€`‚ÈŽ³Â:1GÀ$‚HDöl¡}‰(ôœš=DlcôHöú7íåƒGò2 ÃÑDò˜ 2ˆιݒ¸ªQ7ò"o¡Büו åêMˆÐ^Z­¢£Y1Ad·•V¯Ï¿ý }ó¨5øÇ®áš«®ÄpáÊ£4k)Kw\®V8Ì"ˆBðËæúË.†óΜà8¬ÌTxËž°óÀa Uâ#AäaÉ ß¡U2ÄÅD™©¶ ‹—ìßCAJk‰„VJ‹Di–ë\ü"reÈN<@ Ô*.-‡ôÌM8%ˆzujý{t–iåŸËu›¶ÁÝÏnd¬2«w2útGƒU÷.øŽð¼ƒâþ fõÉr'"À‘gu–"0ú”“à;n€d S«”ˆ™÷ù·ðë¢eBq—ŽíF¬K¿ÁoŸþ¤ÇP§¡GÎùgŒ«Ã»Ÿ~ó¿øëÚì“éwM† Îï[‚ÄÍ9×ÜŽÄK…+Oí䂳ÆÃô;'»U»ñ¾GaëÎT·< G÷âc¸ò®¾ãAhÛ&¿ÿN }7¥é½Ï¾…÷>ÿNÈêÚ©\uÑÙ¶õdˆFRÈSZ¹v¼Œañh¿$µ¤w¥òôD½‘zäÞ[¡ŠJ‰ˆ°ÿ,Xsç†ûn2Q¤„ç&ˆ‡=÷Ì-R‚È$C9Y‹ Ñ>z_®qá-ì+ÍP"ˆˆ0"z~ô¾2JrB™„½Å0{o#ýÅ5GáãhO¡"´_9DaHGa‘ÞÔ}Zc89+öÎb‚H:‰6:ß¼m'Ìzáeôü1¦ýq“®¾""˜ RCQÌfD$ç‰ï…}z«©ÔåÙ…°tÃvÝ0»y©L`$ÞpŸpü27#áÔAQI¹ZŽî53­‹j.>–<ÿ$ûBU¡1â@FTTº¿©Û ÝýÌAŽ_R#õ"¸ügdÂu÷< …EÅþî߆NvGôÆ3‡:£±0Â1ïw ¹CFÀ¿0Aä_¼¹7óˆÆ½¸ãF!”¨©"ÁA^/_¾ý§Ž˜ö8Wßù`#¯‘ýzÁÛÏÍryòÒË1×Myv¥í›š~|ïÅ'àøÞ=]r¿ùßoðâÛº®µœ¹óó§o¹‘7?û*ü¹t•[óFÄ @×_q¡¢÷í›~ ó_ "göô»! GhI¾óSÑ£H)G©¬Fã@oyJéª Ï†Û&]á¶¿“R=ÊÛ¾{ÌxæU8r4ËSÎgüŽD~‡œ;d‚‘ ’4L5…ÕJÇßÎKb.1IºLŒL±¿h| ™¼‹hï"ÚÖ@‹­Æï‘LwÙÈìiýiÎiÍ‘—P~(|%sn¡Þ´îÚâïeZsV$&ˆ¬@Õ™Ù9¹0õñ§¡¨¸Ä{x8/}Ñš¹yòd|c/º± ñÉæ¥}S)Ò …@áÛ—oÏ›g"’‘/<6h_“¦œˆ¼X¼~ ä•è‚ÁEi˜@ª!;¶mi ITŒäС¬\ôV±æ-ÐÐPh—Òâ$¢¤öø÷A5¾Ýp #Ê**UåáF¢æñ±0fH3‡„uQ;––•Ãä©BÚþtµª~+§ðAÛ·Á03]`܈a‚ñ‹îyNŒ@S@€ ¢¦0ËÁ9Æ)“¯†+ÑØ/Oô=CDN\lŒ[Ñž}áÿî¨ó–¡¤sŸ~Ä­üÍ¿@äGWIŸÎ}È[FL_þ÷x=b¬L¿|þ4Olxyãz$W¶ïNÓÝå³ß cNêj7wþçðéwÿs]Ó‰œXq+”]|öÝOð:zÔ<Šž7gOíVš‡/‘§yñáÚ¤iýæm‚—–4O<÷eEòq¼ï ºò‚‰0å¦kÄf‘öפ°rE%¥Ðµc{hÛ:Å­|ËŽÝø»e¦[_0D€ ¢@¢Ï}3AŠÀر€o[:8 镇}”¢A^¯G&żúÉþBú ŸB|Ñ~E‘øÏåùS]¿D^tGU쟠?Í}ÈÂG! +p}QHÃJ<Ò5ÑBb6þ}3¼±{vî s{ˆgØ}ðlJݯ)ü™˜@ÅD‹—ªGòô‰Š ‡6-š£‘G4U‘@ó~ìX-ääC&îC`¶ç¼{ê¯ÆßONŠÃ7C<öçi SûâÒ2ÈÈ΃rô¢k­I/ADœZî8 1çÏDsðàS/Á’UkýÙ­î¾F|"<ýÐÍoGëî€06B€ "M«¢"!¾xkŽÛsšÂ§Í~õ× m[µ¤‹&NêI "êhÖ}·ÃYãGºú,ÇnWÜv?ddf y´—Ýí×]á*?š•WÜ:UW¨7Wc'äMó×wºÕ>ýŠ› yÜÞwË$¸ì¼3]²”öý“+bezYåÓoÿ¯Xƒ„I+2 ¯à}ôï–íðð”›áÜÓÇ¢©~úc1üðë"Ø‘š&6>üê;üCiQ£ðzÄ]|á…Щc‡ã²îU®§7gÕ5 Ú¤§Ã·ßÕÅM72jÚèüÓÇÃuW\b¤yе!/¢¿×m‚¢²JÍë=Cx%"Ñ£7ÑÊ øpJR"†_ k¸7¼"§á’ò2ÈÊ-Ä£ö˜ý^Dj.ŠEãNJóÜë¦1©¥´†i}VVVCV~à DfëMz"Zˉ1‘è=4ÀïÞCd¢½h³ëCfÎΉBìÜrÍevV‘ucLA€ "S`d!~FàùG§‘ùbÚ°uLyô¨@ï[yê{\w¸ûÆ«aóŽ]ðÆ_¸Š“âá«w_Bñ8W½ÀðÀ“/B»6­àó7çà‹* $Á´'^€¥«×¹êZqBaO?}ã9—hÚ gÔ׺®õœÈ ®¸Ð}³d“,9±Byô2ÇÌç_‡?–¬¤ËF©g·ÎpÒ ãaѲÕ.2­Q%ÌxûùY0¨_oWÑC³_†¿p’&3æ‘äÉÇ¡DÝg^NLD~MG”ÒåH¬Ý‹›˜ˆ|¤=œ81v@€ ";ÌëÀcÆ +ùbø'‘Å# I"ò*ªÆßu”ŒÁ¾M2ô „Ê EûL ÚLÈ…È¢0üå‹zê·Øx›Iº{n}¶úK1&‚Žæ…ÖyQ˜B"†h¥R=±®îš¨7­Z#D.¶¤prxmÚúPD*²xÝÆ-ðÌÜ·¡tFÓ„qca@ÿþužþZUF•õc;_  o‰›6ÁÂE‹ kŠ7úÃwßCö7,#ØÎʃ•[vàZUŸªAQ’‚ˆp#cí™Caш, ÃØ±8¯ÒDuH—êªjÈ),†"ôơأ†¿4¤ÂuœÓX)Æh<DÉ q¡HrÖ—ßr?dçæy¬cVÁé£O…'¸Ë%î0zÛ^tã×µž¹̦m»àæi³ÜDȉ•©{áÆ{Á=}ÿ;DNv}ôõá­¾tõoæ<ÊÇ!'ˆZ`àŸ¾íê[‹GÐ+OL‡“‡ tµ9oÒ™ëºæF P0A(ä¹_F ˆA$"J6ò(*@RFó/“ ý¢ò#y7)…˜#])Ñ‘<ŒbÑމLjú#•‰–ñHynÉbÝÝú²â€þRÜH% 'îD$!‘…"^âÑtÕ è­¤QƒDÎ$#9d™®JËò˜ ’b§KúòÑç^FcæAÃj׳'œyÚi¼# txCñ!âË0ÈSã×ß~ƒ»wÓµSG˜ýÐT|óÐ}Ãcäa†rYöï6(@o"DäIšcA$—ïäk)6¾ŽC ADäPBLŒ|2ôéÙ Î9m´«•œ 2sÕÆ1°_/xçùÇ\ºÔà‹x?þ¶Èu­t2qÂ(·}ˆn{ð  =˜81F€ ¢@Ï÷Ï!&ˆDD)”3ä½C^$_¤U°=‰2Ì:z"ˆäòE»ŽpD½Â‘("¯£0¬HGòw¼êóˆT"R!?nmñZLb¾xm§c3ö"ABÇ*ü]Ž4—ô‚7ÉbCGеEÇ*œ[|£ÛE®ˆ2°Øº„ýú’h,ä5F{UÅã‘æQ3™éKÇ^Ú2Aäœ@Qˆ¨×æ}„›˜þcX•¶mZÃù瞇1Ï# Ë–†¾Ý¾ (”ã×?üø#ÉP#ÑPÓóÙˆa'Â=7ß „9ó\«i–dåÂòM;Ï9)I$Ÿ¿(ô¢IÀs~yøÛx*丘¡ªADäPz2Ø÷rJ0£K[È 1ðÍñ=ûëCÔÕHôƵ¯i@ŸãàÝ÷U ·glD¶Ÿ"VP†À%çœ÷ßv½+÷³ÿü¯¿ÿ™ëÚÈÉä«.†Éÿ×xŸIò4™€â5Õˆi]ùùèSN‚‰ãG ^6‘ {5Éë‰×r‚ÈÌyT‡R(AQ/­Ç{g> +×mÔZë1–!À‘eв`F é"`‚Hœ²™‡ÉŒ¸BG7’†~±µ£V‚HMŽX.Œ¡^wq<âGâ‘È$ UFöòJҒ̰/ië 牲ùUâG$}ÈóG }êÇ&ÕGz®e,–Ö©×Oo4š‹VhϤ£V¬ôöc¤>DFPóSºQ¾ÿåwøìÛêöÒÝï1ˆ‰…‹.¸ßÂk¡»u050óA’““ÿùþ{())1Q3 vÍ%ÂEgŸð°e†à‡Fy¸çÏ $‰*ª0NkM]üXy·äµ,M9™¹®¥8—BE%½§Ñ8…††ATD(œÒ¿´H¤·zƒ?Ñ×{qÛÿî’É÷@ú‘£º{yéñáTÉ>JO½òüôÇßnrÔˆ·Ê’‹wß ç1V’£~*'ˆÌœGµqLºì|¸mÒêJz¨AÑοîNÈÊÉóPƒ³ÿ!À‘ÿ°æž&ƒ€Í")îåhgÍÁ—ñÅdTæ#½Ù2+Òa¹Î¥¹nçõõ‰< Å•DJb¢<3,lå(GŠ)yøDyfH JÒ:žÎ…ŠvûÏöJjŠs@D}šÓCx ´·’®L)¡b£¼ÛvÀS/½¦;ìQÝn%¸ ÞìgŸuôèÞÃoo+Ú>Añ†4C/bÞSSSáç ‘v4/aø@˜5u ì×Ç •‚VFIY¬ß™™y…ÖRo"t DqLY2ÿJ­}"7[%%Â>Ý 6ÚŒŸ–¨ï7¡âþF{ÄPux$òHº¿Q·Î`ú7Á€¾ÇùM/îˆ$L}îÛD¹ ¦/X¯ÌûD¼Ô}¤=ç¿ôôÆhòDÄÀ÷= Ûw§É‹,»–ïGc£1êIÍàÇÞp ›wûô'`ýf÷ijÄŠRŸ÷Üt \qÁD·¢ŠÊJX¿iÐ~¬yèY^ƒ¾=ºvò2“œ 2sÕÆqîécàá)·ˆªÀCGt‘ŠÖvWÚ>W{>a‰DDŸûf‚D"âä¥BdQ>þÆ(DˆÂÏII ±žYGS "…šþR{)_z­ÖÖS¹\Ž"®&ŽÁ“–äkÔ›p¤0rB.?Q¸¾(4 øZ2.Ê‘UÈš$·½Tn¾ï!]¡ DrˆT ?¨N6 Fž:\Xœ&©å1fß|ô¥±tùrXµj¾I|»ö$ÎKTT$ÌùyˆCï.NÞ RhïáLغ7]ðh¡káËç!:"¬É’f¯k·Y@á%eåPNDˆ7õ%¸$ãžýºu€.m[#ñ¬øï&¦©^ÐÍÈʆ¬ìþ ‹ŠÅ,á8tPxmö Wžœ 2sÕÆ1¸xóÙ™.]–ÿó/L}ìy×5Ÿ0NB€ "'ÍëÊ8DR$É›ƒ¼ŠòñC¿Î¬ø…fW‚HŠƒßÎÑfâȤAo²”чH!ò"«±SFË‘Våý=o¥Ðä$’â°ÈX™”˜7^ƒ!QŽVÜ„ôìûóçC~~¾®ðp⼡½{—ŽðÒã T:[ëLë8» ŠJÊ„û ÷ÔÂh¥°çà&>Mž "A{tl!øµVZQ‰ë >6Z"ÙAë—#À0Þ`‚È:\fG(üçü—Ÿr©v4+.ºán¨ADojÛ:¾xsÐË@b¢pu×]~Ûwèkï} Ÿÿ³XÅÒãÕŸ wÞÐ@îPgäÅDû!iI¦îëw_‚öø‚ˆ˜<…ÊS#VÄöâñ”!áå'¦‹—è‘´ nŸþ¤ëZz¢F™9jãh’ ä™%¦#G³à¢§hú[MlÃGFÀ.0Ad—™`= BÀa‘ùRüýWB|Ù¾ íP¢Á_ZÇȹi‘’ˆ~~kãTý=èMvg²WF#!ö²üÝLZ7Vؤ­œ'&ˆ¬D×$Ù?þú|üÍ÷‚77‘" !¯C‹õ²K/…Ží;4’ÈŠ‘È¡ƒééðõ×_»ý‘/Ç[~-ò:štÙÅpÁY ›üÊëóµ6 ŠJ`Sê~Cm=س–k[:R2 ìÑãc¤Ù|Î0Œ€&˜ ÒW²I ñðëïºiôȳ¯ÂÂ¥«Üò¤)É- C­îO?,͆WŸ|† àÊ[²r-<ðÔ‹Žìf8÷ô±®|ÚÛîÊÛ¦AFf¶+Ϫ“(|©æû^ '&Ò{ʣϨö‰ÞÃO?t :Xl Õh0¹òÖûáàá Wžx¢F¬ˆõÄ#g·^{¹x }ý¼õÑW®kéÉi£O…'¸Ë•%÷ 2sÕÆA/Ì,ùþc·{jkÆ¥8Ÿ06C€ "›M«Ã&ˆDøÉîBûåPº<üй/¯ËšB¡ŽNNÕ_Aoš òJD/¡D´¡Ñ¾N¾¬;Ì+Dv˜R÷î‡éO=QµÇšRB^‰ÂÌ >á?v\“ ˆ¬zdAôç¢E°~ýzÍáåäóÏÍœ=»v‘O_ëD€Â ý»s¯`¨ÐÙÔ‘Õ­Z×r0ÂÄÜ«k“ ß'ǃ¯F@LéËkÛ·0\Ø 6LL´§Ì”Gž†# Îåç w GNVv.\<ù± œ9n<6õ×u9zá^yëTAF"’Pä…“ç*_¾f=L}|ŽëÚÊ“ÿ»ø¸ë†ÿsë";7î™ù,¤î=à–/^ÄÇÅ ³¦¹…§£²ÿýþÌ~ÕPÛ¨+b=ñxáYàÁ;o/áïk`úì—]×â É}rÚ]nžYr‚ˆêš1$GË8½÷V8{Âhª.$okF¬CÇŽíÚ{+UàúàÄØ&ˆì0 ¬#dAä6#hü/C»jz•ã§ úPÒJ øL)n:Úý©ú£Þ¢-Žö Çí'‡öá´‘C†Xn÷)PÓ "5„lP^‰µN{üiáM=Z|ò¤¶©MBB\qéeÛö½iŒ1c×%¸Ô_~ EEEš<ˆäóBovêÐ^œõDà™œ|C ¦¦ÖlÝ U¸WNHV­k9tøÄÐã{B8~ÙqbF@/LéEŒëÛAÇ÷†·Ÿ›å¦J^A!¼ƒ{álÛ•&xñ×½ ÐþB=»uêí=.xÑ@_½ý†unðÒy瓯Âˉ邳ÆÃô;'‹—Âñ¡§_¿–¯v˳âBÉ‹ˆú!kËŽ]°}wìHÝ ^<† ìgŒîæu$Ö¿I/OžOZˆéøúõêï¿äRîµ÷?…/X ìÑD!ûÎ7&_u±Û>E$C‰ òuEÝ´Œ#%¹9|óîËn¤UqI)PøÀu›¶Âa ;Gƒ………BïÝà” ci‡cz÷Óo`þÿ»ã##P˜ (üÜ9#œA$™%²ËTã÷;}ŠêI£J<'¢ÈYäA„ò¨?!N¿ãˆ"2(?D‘§P•9zB”•g‚HÛåÎÿìkøyÑßP‹ i’“Ò2é9‘ãǃþý޹´,Xέ¼A ¿Í›· ÑŸšðSšò@:sìh¸u’ûœÁ‚ Ʊnû(FO"%â4úXÕ§•k[ª3­ó8 ™3¤Ow¯?n¤møœ`)LIÑàs'!ðÚS3`è ý5«LûÎ\ˆ{Qš5õvÁø/6&o’ÿ»ý¨ÂÍŽÅDß±D†Ð^9bÊÊÉ…Ëo¹JËÊÄ,ËŽ½{t…'0D[§öm õQRZ÷£ÇÓ¿[¶{l¯…X‘6Ç—R>kŽàU#Í/,*†Ø˜ôا?Á•“AD5}™G±'­ã âjòÿ]"6s;Và ~ä%DžX4÷ÒôÙw?Áëó?“fñ9#0˜ ôÜ1#¼1A¤4id¥-$²?H*}JnÃ1L9\qÃÈAú‹$_$Úncñ·[<¾4M¯öÓ\ÊçÓmŒArÁ‘C&rÖmðäKsñmº‚¨ñ#Çû`ºvégŸ5ßd ó^Ñ¡¥VÞ°ÕøþÏ ÀÞ½{UÑñ4/!!¡0ó¾»að€~ª2¸‚6vî;YE@ó¬ÉÊu-ÇŒž ­ã¡W—öò"¾fF@Li‚‰+Ù¸ØÜ+è;|¨ªvdüìýö÷rü]×Þ|æQ·6´¿Ïêõ›Üòè‚ÒrñœBò‘Ç‘è¥å‰ 2:b?tÔ:ŽfpÅùáæk.ƒ¨È©¯çsÞœßýü‡×:\Èø &ˆü…4÷Ã4!šA$ÙZ¼¨À0täUTV,Çó<´YQ%÷×Fêòÿw¹âýEûÉ2dP’A6ŽŽtMùJ$Ÿâƒ(“ "‡LæÑ¬l˜þäs_X(h쉄ð6œððp8ÿÜs¡c‡ŽAçq!ÞäÞÆo´ŒÞúKOO‡þû_¨R gæm^’0ÌßœY3 uJK£ªp;G²ó 5=Cý"+ ŠK+×µ@¡ø†DÏm mËæJÅœÇ0Œ€*L©BÄlŽÀ%眗á>CÚ¶v#rHm !¶hÙj oöÊÈFrùùgÁ½7_ëÕïHÍœ3×u-?™2ùj¸CÕ‰iÅÚ p߬çÄK¿Çž:.˜8N8¾1Î@IDATDàßJ©¼¼ÖlØ 4ž…KW)Ui”''Ë(Ü`j©KÇvpûuW¨“OlTuúaøá—?á ;GXDQzë£/…0sÔgèG©½ã µr®þ½{ ᥲÄsòŠúgÃøõ¯e°tõ:1›Œ@À`‚(àSÀ 0Á‡@&ˆä“I6úd¢-‘¼ˆh£büˆÞ*òú®k&‡\P˜q"Î…‹‹"¡zbˆd»vNÇݰ˜ ò<6¥ÐO¿úlÅ7ø¼‘ÞtªÅ‡P·n]áÒ /v yá­ÝËüa@ƒo¾ýÒÒÒ ¤ø¨ÍËñ½ƒGî½ b¢£¥ÍøÜJÐx±n[jËg=Èý±¶¥ÐJ\†ôí±ø†1'F€`Œ À‘Ô¸ }{ztí$E%ø;<+;Wð ©ÁßÓòDûöÐïÅÊÊ*|™«H^Üè:!>Nð8©ÆP$ù¸×Qm­¿¿õëTŠD¯—.¸?&í¡”„Ò‚ôÉË/ “W©òbT£aFlL´ð©@, 4`!•AáØ:¶kíÛ´‚ !LžKD¬ˆ‰öóiž˜(¼”›_ f{=ê™G© £ã »tjmð…0 1—‡/÷eâÚ9\O(JûàsFÀ0Ad‡Y` C`ìXܨ'x£¼è-á÷þæ«E›•HFTáy)~*ñw%…¥£_—´¯Õ©Á4‰m¤y¶>—éo¥®îHÐNÚ!hÛ¢ý‚Ⱦ‰Gò ŠÁ#í'D‰ÚÈÛ B¡ÇW•`=a‚È!3KäÃgßþßýô+ž_°D]~ɥбcGüc´ñ¸Ã¥¦q$\"¼žÐ¾A„¯¾þÚ+9DBÔæå’s'Â5—^äúBðÚ1jF`ͶÝPQYT^DV¯k%pÉ{(*" †öí©TÌyŒ#ÀhB€ "M0q%F€`FÀ0Ad‹i`%àB€<ˆˆ ª7È»''äåbEi=#u¤íE™<’}' "9ñC*‰äYg©œB›Ñ9yUâ±¢ŽBÖ‰uåG¬f¯„ºšD‰Ò£.Œ¼°Cò "Rˆð ÅuCGúˆmðÔ{²@oïÚ«T ˆVi‹`/Íݵi†7‘æ9woêœ+ڇ艗^ó‰Ø!˜:tèçs.DFh•mG”ü1á•cþ¿?þ(„˜“o0+b¢F Q=jûØý÷À ýyÿ!7³Ž{g‘ܨ¬¨0Kd@åøc]+ 0ß”n×"º¶k¥TÌyŒ#ÀhB€ "M0q%F€`FÀ0Ad‹i`%àB`ôhd;pÿt9¹#7ÛÊË 3êÈe]²ñx"ˆÜT“è-’@T.’äiD^FD ¤’GD&‘|Ñ$.Ú“Ä#µ÷k’ŒAo¿â˜]Çz’‡Hò÷ "ï ò qûñiÌ>è-öïô#DšAºá¯ºu ”U”û¤5yÅL7úõé£Iõ©GóûtãkUŸ6Û¶nƒ…þé‘”ÓBQwÑh|ÿòݹQ¤µ{®§ ‚âRX¿3MørÐÖ¾µü²®½ p¯nã¥1Œ#à&ˆ¼ãÃ¥Œ€'(qh(ý ì{¢ýƒª8´‹ï@²F  ÀQ˜d"#àoȃˆ"i’à›9DG• ’ã#ÅNr.’'”%$äy$x ažà…TÉ# gGué(ÚœˆdÙš…:²9¡ÒþÄúò£Päѧ^ɤnÒíÉã‡ýOeÔN¨ƒGú,zýÐQ'É£ºb’ž‹y>IoNÀ‘ÃÁkï}¯\µò‡­Îq´hÑ.¾àBˆ‹‹s1Í:E¬º?n]zˆÃwÿùäææ*޵áѪXìÊ ÁÐ]cOSnºÁ•Ç'æ!PU]vÒ2ó„@’?Öµ·aÅឃŽëá¸^91Œ#`&ˆŒ"Çíš2'ì¯Ï~Øíh_ð(+/‡ë¦< ûÓû"†Û2Œ@@€ ¢&0ÉôYˆ^-ÓŸ ™Ù¹>;tO<óLèÝ«·Ï²|’æ¦þ2 9´cûvXð믊Ær}äP3hÕ2^|ìHˆÓ¦–K¬¨¨„g^› ¶îľ|33Sx©éwÝ' fÞô Ëõ×Úo#ÓÚKC½îÚ@§Ö-aå?ë­Zô®¢d”¢¶ïÜsDFDÐ%'‹È)(‚-{ö»ýް¸KCâý½¶=)I¿±úwï ɉñžªp>#À0º`‚H\\™`F€(L~îœND‚HNêÐhåFz¥:òzò:r6GÑ r˜î uªþNÕ»ÑX—Á‘uØZ*ù»Ÿ~O¿ûÁÍËÅH‡Ç÷î3¦Ü1ÑÑ‚¬ÔƒGàpv^íÀÈotü¢!¿‰¶-›ÃqÛ ^%¥eðäK¯Áö]©>‘Cä‘qí¥ÁÅçœedj¸ªªk`Kê~((i¼”q–4ñÛÚÖ }óøXè×½„#Q̉`3`‚È Y#À0Œ#à˜ òÎÜ #Ф ‚¨þ…k·qËmŒrâG¬,­'¯#-ëÛüè"ˆ”0±¹înê9{A§êí¾õLY±%=dåæÂM÷NÝ0ØChh<>m I$MûŽdÂĄ́­©õ™€’ÊÕzî/:8¡¸p‡VÉеm+7õþݼžxy.†*«rË×sAã˜ÿòó@›sòyEŰ--ªlfÎ_ëZ+Òa^îø® yBœÖ&\`U˜ R…ˆ+0Œ#À0¶A€ "ÛL+´‘bN:*¹¡^NþP]µ:òr©|›ž Ú§j¨» RÖÝE°ž0Aäà™ùü˰yÇN$æëB¡éJHH( ê×fM½»Q3Ú‹çpv.ì9tŽÝH Ž Ñ›…„@ö­¡]Ë‚{/ÉÓÌ^‚[ßúØ©ò ^®CPvÿ>½à©§z©ÅEV!°sÿAÈÊ/j‘Dþ\Ûj¸9Ô*)zuî¨V•ËF€Ð…DºàâÊŒ#À0Œ@@`‚( ðsçŒ@p"0v, 1Æ}lr‚¡‰Cí—„™c "ùܹϬ½¯œ¬»Ÿ‘e‚ÈÏ€›ÙÝ¢e+`îŸ 1¯ŸÀˆŠŒ@ï¡{¡wîU:‘ ûdAí1ÿxùË€.x5 ÎmS S›–Ç¿iû˜5çeü^Óo†ìšrã$7âTò¹À:*«ªaî4(«¨ ˆœtdþZ×Ò>½ÓúŠ ‡ÁÇuƒˆð0oU¹Œ`Ý0A¤2nÀ0Œ#À &ˆ=wÌ/r"¹‘¾ ‘CäE6!&ˆ°Üåë.*8©K&ˆœ4[2]³srá§žœ¼Y‰÷K2;a ë•yÀÒÕÿèŽá¥¸ýf:x¦vè±ûÀ!È)(ê“{¤™É\iÊš)F©ebôèØ¢"•+Êr×ü»žû6T!zÒ¨“‡Â½7ßÊ‹SàØ“ž¹8Ö‘›ÞFçµí­yY8¤m[$B÷mäE|Í0Œ€)0Ad Œ,„`F€ð Lùfî„hZˆ‘Üv('~DT¤õäu¤eb}§ëuw,AäTìªw€×5Dž_»ÿmñxëÃO5{¸QÒµcGxjúTˆñîA#Õh‡2³aßá, ϳ’¿ èáò­K»hߪ%4ÞmÈóh ‹‹áá§Ÿƒ}éGo–ç¡a|ûõ×À™cG{¨ÁÙþD`ÍÖÝP^Yeh¯.£zúk]ëѯî³Cûy+©G×eF@ &ˆ”PáEVÑi cµµ’”Û´€„˜šûÞûò8†aö´¤„¸8˜3k´kÝJKu®c1å••°i÷>$‰ªýFY½¶õBF÷=yÍ ìÙE³÷œÞ>¸>#À0„D¼F€`ç À‘sæŠ5eÙ&'NÜ,½A]9ñ#–Hùò:Ò2±¾SŽ2ÝGÉôw ì´ß'ãA¾r%ˆ¸ŒK lËfö¬I®„|è>óÚ›°vÓU/"šäˆÿgïLÀ㸪|ÔRkßw¯²-Ë›,Û±Ûqâ8!;0@ !xÀc‡@†$!ó€À7! 0 `>’G À#g!‹ãÄû¾Û’eY^dYûÖÚZzç_RÉ­VuwUuuu•tnÒîVUÝ{ÏýÝ[ÕÕç_çÞädzê'¦š2›ü,ºttóÚDµç¨O˜@ûÎJ`Gx-š3ƒ²3Ò)‘äfÓ·ñîÏ~‘úX`ˆÔV0^µ¬Š¾qÏç)‘O.IÎ ÐÖÕM‡kÎpôÛPÄó$Z‹c?¶Yˆ1éMôPeyåFXwËXÉr´B`"ˆ&2‘-B@! œJ@"§öŒØ%\L`ãÆËQ°ð£6+Ð|Là>õx·¼kØ‘k"ˆ4ìwz·Úí ¸Aä Î0kÊËol¡Ÿü÷“Äáa‹@DÍ{o¾>v×aÓ»óíýÇ©¹£“ÒR’Y ñ螺-–t”ígÀ××O…YY´~ÅB½Í {ÜÿyêiúË+¯EŽ˜b!ê‹û'ºùºkÖ';í'ÐÜÞÉ‘Du¬†Ç¦îXŽëh,Æ÷äòŠ2*ÈÉŠ¦É+„€ÐE@"]˜ä ! „€Ž ‘#ºAŒ“‹Àµìƒ2”ó%Й|Là>·Q a;|E®ˆBØïønp«Ý›ÆþìŒíÛf•qs¦lPõñZŸøêýÔÅQ=á©ÒRSé¡û¾Jóæ'”õ¼´c?µvv“‡/è)Þ$~y)9)Q‰œ eG,œèˆ@$ÕÀ ŸY Pߦ¦¼ì ºyírKÂãŽWŸ¢y”|½½A$.ÿ ;23ÒèW?üJáH-IÎ#p±¥ªë/Ä$’(c;‚I,ÜΟYJ¥yÑ%y…€º ˆ@¤•(„€B îD Š{ˆB`ò¸å"^žB3:óÙg1.î·Ã„±ÝQûMß­v;j"/—’÷Úk´Ì˜ISZ ªÇó[Úôúæ°.•ô¯_ùe¤§£«qt?‹0¯ì<@=¾±½pH'òË›”¤¬sâe±kŸ¨b‘•tÔ5ÄO$ °0ÔÛ7@…øÂ Ö£²¸7®YFÉ,^E›º¹ßyì?éÐñ!‹B„Öm×o¤Ï|ôC!‘ñ%€1ØÔÚNÇNŸçñbÝtsVŽm+áüð$xhñœéT”—cE‘R†B@ˆta’ƒ„€B@8‚€DŽè1BL.ÿ8QuõÄ6;óÙo1–‚÷ípÁ‡¶Ã_äè¢ö;ºÜl»ÃÀ&-X@¹O=å0«Œ›3å¢#'ªéßyŒ£h&ÐÃù’Àÿ}ð½ï¢¿ÿ=ö›ÙÐåë¥WwT¦r •ëþ ²‚QÄ"vX^ÿCå µí€SpÀOýþAêç5°R¨„©ïÞ±ºŠ2ÓSCbhû“ü3ýþÏÏ¡ @;¿óÀ¿Ð’ó7Ëghç…òts~Ž4 šê4aö+B­'–ñ´r9F›$Ç ! ¢" QTø$³B@[ ˆ@d+n©LL _ÿ:Ñ›oŽok #?Ø1¸o|.çÿ¥ÃvG D:ìwl'¸ÙvBM½þzÊ|äZf̤)/utvÑ·~ð#ª>]7ÎDÑ<ôÀ½´x~ù„ýf6´´wÑë{)<áòÃùq ÿ'z•)¯ yØ Á‰ÿäðä -§AÓÅAäµ…üC~âÝŠSŽðp L×­\Jù9™áÓ½ïèÉúÆÃß'DOi¥…åsé›ÿrO3'Ny->NÛv¬î]hjã‘g^æ1Ÿ3v4pÆM/Ì¥Ee3bW‰”,„€A@¢`d³B@È"& ·@ÂÏ~v¹ÁŽü`_^ðþË9ýI§Ýð92‚H§ýŽì7Ûî@ ð®g|ñ‹”úÑ:Ð:c&MyB̯ÿúó¦—ÇEDàœR6c}ïÁûëY‘.¶´Óæ}G”iÞ–ƃ®È;Á_ …±ña²©ùBÔ†K¨¤Àš)¶°þÐ}ß~˜êÎÇ•ƒñí·ÝL»ë峦A²ÑQ:C'Ï^¤Ó9ÚÌhŠfl­Kïñ=½ýÔÇkq-˜UBKçÍÖ›MŽB@XF@"ËPJAB@! bN@¢˜#– „À”#Ðýøã”ñä“—ÛèÌöÃùŸ Ø ß‘Dv©öÖ:©‹Rüó<5dÁç>çúvNy=xäÄÉÑiæF"\Ôskã|à7ÑGîx¿e}ú|#í8r2¼€g:øšÊ š3½Ø²v?ù‡?Ñ3Ï¿0a­'L/÷Ðý÷Rå Ëê’‚bKàpm=«;Oƒƒƒ„é³ÒRu‹{qÚÀ@ îäi},%ñX\T6¢YŽ“ B@X(Ö„¥|! „€ÖÈ:–R’#ê¿ô%šµw/¦þd ŠCàH(¸oÆ÷”sÿr«ÝÎ%:fYÓ•WÒ¢Ÿþtìo·~ˆ{n€Ý÷ça:UW¯L˦v&„’Ǿý Í+³Îa|„ëkΨUL|ú˜x€=[ªæÏ¦%s­k÷©º3tσñþr=4¯¬Œ¾ÿàäå©ü$¹ƒ@ @‹“XHÍLK¡ä$/ E¡Ûp¹çCcרÒ?0@]¾>em.ÌÔ(‘]ô¥! ´ˆ@¤EE¶ ! „€p&ˆœÙ/b•p3Ú»ï¦ìÚZ*à‡WÇR°“ÅÍŽ~ƒ¶Ãoã¨"ƒöõa¼?¸Õîxs‹P?ÜŸÍ~?ùæÎ¥åøC„£¿[¢Ñ>ú/¾B¿üí344zâ@¼(-.¢ÇùŽ¥½¸÷D-¬¿0aª5¥§xйí³¦ÑÊ…s-mûÿøÊ}ÔÔÒ:Öv0þć>Hï½å&Kë‘ÂbK X BmèËù2x*Fo¢'@±Å1C›Íà5¹º9j¨×ÄB‘šD RIÈ»ñ Q<¨KB@! ÌÈ7É%„@h'Ö¯§~u~J ža…£eüÁþ‹ñ;\ð— Û%™°ß½âV»/¼Ÿ§úúˆ’“ií®]ávÁ^ˆF;©³»›>uï×yª©^Åiìñ$Ò»n¼Ž>õá»,íÆ-ûÒ^‡hˆUÆq鲟zÜæxü‘À!Óòsx¢Å–Vÿ‹'Ÿ¦ç_yUY ‚Bjj2=ñè#”•‘ai=RXl h DjèWL;—žšB‰£73NÚ~þbìáˆ!__ÿ8aHµ]"•„¼ !"ŃºÔ)„€BÀˆÌq“\B@„&p~Ý:êd_á,¯—ÒÙ/7.¹ÙÑoÒvÇD&í×ñøÃ­vǃ•‰:{††è º™|®VajH—'ˆ:ð±Ÿÿ’ÞØºC0<}í³Ÿ¢«×¬ 8"º~<¯í:D­]ÝJãJsˆf íyYtýªJeú°qvFñÇ[;vÑ#?ýùˆ@Äu\wÕúê§?E‰’5 Dª=ž E^Ž*òR"GyX,²{ˆãYDú9b¨·€zûÈ?<¤š8á]¢ Hdƒ6ÈFØR•B@( ˆ@%@É.„Àª@”Îþ²Y•0–ÜìèÂvGDQØ?ÖñøàV»ãÁÊDÎSÏâD¢ ˆ–‰@d‚¢ƒ³ì9x˜¾ûÃÿRÖ$ÊÉ΢‡î»‡æÌ²nž~žÒê•ûyÝD0Œ:ªíöš‡á¯š‚(ŒôTºiu¥${Ãä0¶ëô™zzð‘G©­£SYïåÁ{¾@+—-5Vˆwz"‰ï#D%y¡c ÂQB¯D¥6‹[ƒ ¥áᆆ¨E!¬340àW„¢HUŠ@dqgHqB@" ‘!\r°B@¸(®ø¥r!0) ¨¼…³Y Ê`¡(fÎ;Féø'®kEi¿ˆ5ëp«ÝšqæFCˆ ‰@äÌ>ŠÊª¶öúÖ£?¦šÓghþ¼2úÖ½_¦¬ÌÌ¨Ê ÌŒ©­þúÖîgµzÂFòZÃÏf@ BÄÇ»¯Y¥LfUµ]]ôoÿñ#ª>UKåsçÐ7ïýåfg[U¼”c]Qà€ ° EÉÉIäMJ´4: U r(öÀ Ÿúûy}¡€ZùžjÜ_Úˆ@¤ÍE¶ !`ˆìá,µ! „€°‚€DVP”2„€$  DØ–Ä>¹òÀ(¢ÀÝðYõyFa+ü8qˆ,°?Ц›ÏêV»Í·8.9±öºpŒDq邨Wú«§Ÿ¡ç^x™6¬»’îýô'”éÖ¬ªµ£»‡^Ý{Œúz}#EêñZ[Uy˜r´ÌHIM£V.¢ìŒô09íb…õÑÇŸàiü¶Ó{o¹‰>iñúNƬ‘£ÍˆF B?¤Daê9/‡bB,´†JtËï XŽ1h-Æ1[y°"?ð%„1Qh€Å!L%‡mJþ± úÄ!.Q4ù(„€íD ²¹T(„€BÀ4ˆL£“ŒB@„ (!r¨˜°Í ^‹(D^Gm¶H¤ˆ›@d‘ý¶÷‰[í¶”ù áªleÿã¥ÁÁ±BD C1¹>Ôò4h_þÆ·é£|?ÝñîÛ,m\Csí8~š|=Ýú½Ö–Z0±0-qG¥¥gÐÚEs¨´ wb¦(¶<ûü ôß¿{–~üÝoÒÜÙÖMß…I’Õ °Q¨¢åpþS£Öð>òÒ΀ï»aþ/¬/„wÎNOÒ.Hc«DPd“¶È6ÔR‘B@¨ ˆ@5B)@ cüœðoEJŠ2u¿²Á ÿŒÚn…©h¿íDÚoCe¸ÙvC ßÁ~f\Óß?ΙɼWíÛ?£,ª9¬#W‹ œ Å<ø½ÇèöÛn²|}œ#µõtâl#Gõ:S¸ŽONI¥E3‹iÉ^7«_> #‡› ÈOâäåÒ´’"Jæ'óœD rB/ˆ B@! ôH'9Jý×­£– éòSØù<‡§š 5ÉŠþÒm8Òb÷2|:¶ DÛoñËU¸ÙöË­pô'ŒÇ3|~ö±F”ߢ½{m»ãD Ò ´eÇ.ZºpåæX»>ÎŽÃ'èì¥6ºàk˜óM‘œç^v˜Íä衵U ,µë<y‚¨¹‰Ž $ÐÁÔ,buy¤%z”m6kÎ0 BU}Téå )¡‚ÅÑlkEb1HU#ñCÍmmÔÐÈ+ÖÌbaHR|@ ÊÎÌ`q(‹õàDÊÊÈ ääøF‰@Ÿ± µ ! „€0C@"3Ô$O(ðÁ#ijè¾ê*ªçY%‚¦µšÅQDÙN#ª(Øø(þ†oG¢cÀ=BSrw'ûnα?gÜUz”ý °˜½gë¹H‘M]çàß¶ì¡L!G§ ^ç9œgéüô®kV*O¸Û„Iªq%¢áÄqÞ¶•0nyÉ)¢¹EäQ¿ˆô*Ú)C,-o¿DKø…è¡a>Ö]E´`¡œ6ôÝd¯¢·¯Ÿ.\l䨡6‰r@g«QFz:á³×›D™éJ$ÖA‹G(Ô¥N! „€æˆ@dŽ›ä!€i¦ßغ¶íÞKç©° Ry­™ÞÞ>JNM¦âüeæžg€ óò(=-|½>J㥊 Ÿf%(Èϥܬêñù”{Ú".÷¶Ø‡û\äÀÏxÊ⇣°zoÿ@¿R¦X‡0…µ}½£Ñ)ðU!¿šàˆ×½±jÃTzºúj:bI ôÊ#Éqú­¶TßP؃Œï„Ç(Föo±ÁnµÛ`3ãy8<}ÌSËó7°/gßyñîÝñ4Ó’ºE ²cäB|ì|që>êgÅQqºGÎbùãs„Òq̳[×­à›äGËîÉJ`øÈ!Þ¾ýr´7tL 22¢bO(Zk -k»D•,&D%¬]G K–ð–ø8U[äݽðí\ÃEjá)å$9ƒ@ @‹E¢¾Á‹G(Ô¥N! „€æˆ@dŽ›ä"zeó[ô£'~Í"ñ´ío ÂðÉüsb ~u&@¤á¸ÃÃCÊgïò+Ç&&D½ûýƒÊ±w†yv'at=Óòpfld§&ÊMOÏ`Á©Ÿg4ð+Ó+§òýngO¥x“y]ÎΗ@=üw!/3Á‘õª¹¼âÂ"%?—“Ë‘÷9ŽŠÁöÌÌL%/§Ìô4®ÒÉÇ"R"OùœŸ›«ˆE]Ý]ü9rXtòõõ*õ—p”“W)'›ËÈËÍ¡þþEƒPaI‰šâˆ©‚ü|E|‚y,h¥¥¥Rwwr_Èû D¥°MŽL7Ruw7 8 ƒíD"‰.Çyaãßaì´ÂŠ˜ D1¶ß še¸ÕnÍÆ8s#®‘‡94È;ð3ïOäóñ ˆ¼;v8³!¬Ȭh=ߨJo8¢<M9fòšvžó±aÙbšQœo¦ZÉãrCÏþž<]]c7‡jsŽdM1§îˆÓ»éñd¯–@„Cpc;Ä7¡ž;>”Cþ‘ œçiåFÖŠïÚs‘-ZGh D  ˆDü3‹ŒÀv&ˆì¤-u ! „€ˆŽ€DÑñ›Š¹!š¼÷cÿ“£zâ7«ŒÓ¸+dŽ:\EåjDœâ((~`¢^¸G‡P…(*%ÚŠ÷qØ“ò·tâ7; ?¼S9EòG%)Bo€PQiæ´Rºón£ë¯¾J¶Å„¢6ŽÈº€È¯0†AšË"‘Wmd˜cc¶ cœPCÌ"ˆl°?&xÜjwL`Ä®PˆB‡Æ­Ä㳄¯?³9"S¢ØõŤ+ù`Í:rªÞövE{É®œ7‹ªÊgÛn·T_Ã5Õ”°õmÖX qª Dè‰L9uÕz¢òùñí©ÝU|¾^:r²FyJÎU†OcC Dh:öá)Älž§=9Ù>‘H¢)0ð¤‰B@!0iˆ@4iºÒ¶†üúw ßýùyùm`ñðÝùžwÒÇïºclŠ»ðGÛ´÷Úk9lÁ¯¬C4Î1­Q=Ä!Å1‘(ÈQ®až%›b&Ùd¿% q«Ýmpøg³ýÌù Îá"‡Ð µX(›E¢d‰ I‘àI‘Í{Pc‹½ëOD+¡]%ù9tíKxþY }IS‚‡mz<­-caçív’@dÅWÛ*‚ûñdÓP^>yn½ˆÃG% =jj먽«[™k[ÏñrŒ} D°s¡cnôì¬LÛ"‰D ²¯ÿ¥&! „€Ñ(Z‚S/ÿW|ˆŽV×hþÆžz4âÛbü¾OâÙ~ùèüÎRA| ¬}T :Ï‘ ,…Kð…$q;f²ƒ:ÍN‘ÈF‘"&‘ö‡ë?ÃûÜj·á†Æ7C/sÆùÇñˆ—£øB°Ïf?y ,(¾æ¦Ú;»}ÊúC}JlW²ÊqžÌë0Üxå2ÊÎH³Ët©'ΆOÕ½ñ:&%Ö´äPN1Ê)ŒûDÚÖiš¬k#¢¥íMTÕÞ¨}<ïOØxѼríý²UÀ<ÛGNHôPG}Œ$ÁXU$Êáy±&Q¬“D±&,å ! „€°Ž€DÖ±œ % °/è3¯ó‰@d_ß¹²¦†æ6Ú~èõöØbààŒU…©ì[·t•äÆª )7N†}>~ùE^{¨É°Š`¹.¥¦Ó±¬ºš©\,õÞÀè©ÐŠñ2`Ó´Þ.žN®…Jz»•¿ •=úE1\ÈkÝ| %¤¥é1_Ž™bº{zèxÍi["G§ZËškV ‚É8µó9š¨€_†d¢ ÿì°;Á~Ó‘ì7ÅË­v›j¬=™0îá™o刡V>— iž aØñ¾üÛ?ScÖ$ˆìéGWÕ‚Sw¾‰ö«¦Á C*ÃNn5cïI,]±°œæN/"„æIr/˜½ÝÝäÝ´‰¼Ý–L‡ˆÑ•è¥ýy%t.-˸€ÓŠñ {¦û:i¯5”58`Ξ / L¹8™I·¾“R32ÎfIB@% ‘J¹ïÑ Dh¦›ËÉÎ"¯7É’†Š@d F)D! „€-D ²󤪤µ½þùK_£¾¾¾IÕ.w4&AùÍžÈ"I¯)|ÿç?M+—U:Ïô maçv#G>˜M^öÝam¢4=¾‹ ÿ‡Ù:£ÍgZ rˆý†ÛïV» 7Ô¾ ðú˜ëy>wÂJóØ«ëiù(E ²¯?_HS[‹CÔÖÕC-]†Öq1Û@­i¶,#ùðÄEAv&åð;s¦SQn¶‘ìr¬ óÅO0½¾}'å9CwåZ:f±6‘Ÿ£‰ê9šè8O;×âM1õ´K4¨ð„@þ@-èl¦9=äa1lØŒ â‹çÁ³—Z©eV]·v UÌ+#l“$D rþ°B Jâ˜('‡¿½ü9Ú$Q´%¿B@ûˆ@dëÉRÓ ¯yñ¡Ï}…Ú;:&K“œÛþ]Žût¯—Œ_èù¹9´pþ<ºfÍ*Z·ê Já)£™ÂD°Nn¬›bÖëIO¯‡ˆ"Fš)„ÿCóØo4%9È~CxÜj·¡FÚw0Fw?3mc?` _{Ã7„a1˜Å×’ia~ï‹@d_¿:º¦ÆÖv:yæ<5¶vМòÅTsöbÌ'—8„ÎHâ/”ò™¥Tsî"ŸdDÅyÙ´`öt~Ïqt_‰q#ªkëèO/¼Dû#¿¯‡þëÊe”ÇÓ%ÅbLA(êNL¦ÚÌ:œ]¤Ôâ6d\÷XaKeû%šÛÓN™f£†`Q˜/ ´£…×ûÜöý””ž®„§¿ï7SÅÜ9¼GÒT& ‘ó{ß ­„H”ÈQµÙQm$‘DÎ7b¡B@•€D* y7Bà_}€Îž¿`$‹«ƒÔÄ,ª „õ…ÍŸKk®XAË—,¢Â—,‘A Š £"‘,š‡Àׂ5‰0UV)GóÏ„ñhã°ÕÐs³_7·Ú­»öˆñ !´‘E¡n‡ùó¸1lJö‡JÈC(N¢`¨SäoD^tùz©þb3UŸ»@ýý~e!lÇ—ÒÒy³©¹½ƒÎ]j‰Y4Îs³Ý…vÎàõ‡ r²éЩ3JÔ Ú g[J2 G3¦Ñì’Êä°]‰¦0KÙú|˜J®†#†žýÛ‹´uמ݃ûòöYÓèŸyÌBè‹ujIN¥­3©›×& —¢ßiüE°¾å,öùÂUyó‰” ÿŠÏƒçX$æÿéêÕ«èï¾æÏa®|^HšzD r~Ÿ[%¡¥‰üМÀšDшD‘ó Š…B@! „€Jà+ï¿Ný(ïB ,ûz˜ðƒšð«H2O@õA%$$*¾Œ‚|Bå,-§e•,åå™/<ž9uD0¯–×#êci‹Dpp[±~´ö–³D·@äÖsÊ­vv”C>c¼tòyq‘}‚ºRöˆ²›ÃçG¤$Q$B“hÿ{! ©ÂÏ¥V^«…5H?/zÇ{`*-È¥ªù³éͽGÉ×7 8Œ÷Gû9ž·8wÒR¼´áŠÅt°ú 54·kn‰‰˜fg˜£‰²hfQ IYŠXä±Cgü=ˆœßGN±ðÛD¿xò÷üÛ\$ ×'#‚‡ï¯1m܈ ´˜¡µ+W(`æ»4B(\£±O§@¤sšE"_´"Q€ÿŽõ"vŠç³Pä„?OD(À~'جÛ·Ú­»±?ãµ…ýÎÍü2äO Ãå¤ñ5g–ßó"ž¯ãRƒÐOµgÏRCK5vôÐ ÿ­F\è1Nø¹3Jh]emg§ñiv[‘ v+*Ô(cî´bZ[9Ÿ¶>I§°þŸ4z¾à“’©$;J9ºh”hà„Ó[ÏT?®Ÿ£XvìÝO¿yæ9ºÐxIsìb,ep_üøÊ**IMÕ<&ÖùFïÕ’2jãˆ"¤hÆ7òæ ôÑ OSÒðøˆ>¥p#ÿ„ù¢U Ævco/}~ÇAêf!Nëi3­¸˜>rçûh-ÅÔ¦ª>Ùî|2Åœóû(Zs‹Þfefžn.P úÝ;¢X(„€B` 5ˆ¦pçGÑô]û҃ߌ†Ø¡)é2ÜCãþ‚Ö÷„´¸b>­Y±Œ*- ¬)„c&}2(Ç9ö!z ÿ<*X£¨E¢L~%Ç‘;|=a" ûÁÄÉͶÇ0Æy?³ëbß;„!\Iõ{¦G Áã kÍ`_µÏ¢D£\'Ã[o_]¼ÔDǪOÑ–»èhu ­Zµ†**˜š"(‰¿Ð®_]E©ü$ñ›ûŽRkGWT_fÑ8Ï­èŸ!>yò³3éZžŽ¬·€^ÝuýFN—+°NÃÉÇiÏÎm´hAGT¬Væ‰-)*¤Ô”+L²e´´¶Ñ£Çè…Wß #'«ùæ€cuB\ôü|!ýð¼™ôá¹³Ckˆ$ò%ziw^)MË2]ν´º¥Òý<}ž¹Û¢‘úCðÒcnVŸª­§ßœª§Ä7P8}RÉcÿ7l¤eKS>ßèJš|D r~ŸÆJ BËQv2"‰2Œ‰D"9܈…B@! T"©$äÝó é÷>À‘qŠ‘zœ~ìeA(ùÞ¹° Ÿ–ðïä5+ªXZHy99|OmJòpzÓÃÛgB B­ì(o`¡È±þøZ aª­|v˜§#ª(Bžð3¾6„ˆl¶Å¸õar¸Ùö0ÍŠå.Ä´u17Œõ^~ǬH†Æ»j\ökÅ<Æ1Õ¢Ñ$‘Qb<¾ŸÃ1_Ù²•öì?H'kOSk{‡bå0;Ð.ZLk×]5nZ.½M€xVI!]½|1µuuÓæÝ‡©‡E(3O<`Æ3áÜIMI¦ëWVR.O-·eÿQª¿Ødª- üÅ¿cëÛtìØÑ±üy9ÙT1o­^¶Œn¸v½òäu<Û붺{z|ôü+¯Ñ–;éTÝY…ë0¯™2&‡;4——qôPaœE9\Ì{ùâ ‘¨.=›#Ù\¬*±ÃBY¯³¨ – Bˆ§ŒC¤Ð”‰ŠÔ“&"ÛÁ~Ì ìãÔå1èÿ@™)ÜwÓx¦„T~7å˜Ôvý¨wÒ DÙk`™R›0 ]äÙz9ÕØ Áe–ðovD™I"™¡æ€<¬ªå5Y^Þ¼E‰š0…Ü耙]6‡6\»ÑÐ4jÍCXìúªÅ4½(Z;»éͽ‡É××xHÄϺ.ìK‰î€4‡®½¢RYwè|S+½uà(GU™{ÚÓï½¹ùuª;}z‚Q¸a€íZ^§åÆW+7 ^™†k'uCSK«2†ÿø× p¡¢…Ôã•÷Ñ‹é?ÎIwÏ™…{3G$D½U8ƒêÓ² Ù3Ó×ASB ƒIDAT×4‹.j5†ø’0dÌh1O®§§kÏê»)ã<÷ˆžûÀ»o¥[6n ‚É:—²Q˜.?^"çw`¬"ÀwêÉá\=k‰@äüq# ! „€P ˆ@¤’w£îþì— 3€Læ„ß¹x@ MòÚAóéÊåËhÉ ŽâDM:c'37£k³èc߯öyö°ï-¬«Û¤ž@EYü0\&÷_zŒû0¤@dÒþ`^¶ÿíV»m7%úc¸›yuó;ÆuØñ¬Ç> ö¨‚'Ä!¼›õÁ‹@¤§r žÒhhl¢#'NÒ«1TSWG~޲@¤Ð„4:h é7ÞL)QDY”òSë—-TCg/6Óîc5,é‹$2;0'´ÇäiÜöÕ‹ÊifI °ZûÖãÔÐÜj²D¢>nûß_~‰šš.…,7˜¢«|nÝxÍzZÌaÆÓŠ‹(-md­š§ÀL ×ÐØHoïÜK/½¾™.67ëMçþĘÊñ&Ñë–Òìô4Ó?½¨UýIÏXöó\Ã{òJ¨6#›íRsjׄËöœîZÉ‘CIùà\ji!íÐQFp™Z£ž:Žèz`÷!jÐ^‹H+¶yXP.á›æ[®¿–®¾r%•–+¡ö¡Ž—íÎ& ‘³ûÖÙ!¡<èÀ_i”Í‘DÉzÄ$ ! „€pˆÜÑON´ò3÷ƒjëêhši›!/ûŠ1e\OwÅ2öéÌ2.j¯iû\“1Š¢À6"Ú¢•_šÉBÿ‡‡è`ú¹<ŒbÑ»ðáLˆ ²È~M6±ÜèV»cÉ$ løÓ°žú/xëCúðòéú¨Áeç²ÿ¹˜Ço´I¢h ÚÿÂÅFÚ¹ïíä)äNÔÔ*â ¦ßÖZÈ>hÀ¤ñ”O7Ý| åäš_#"KUùª,Ÿ¥´¶¡¹¶:®Dá 4T²ì$UA„í°;=5…ÖV. Ò‚‘öª©§ƒ5§•ˆ‡ÙCînkk£—_ÚD¾žžÇ¨;°8!¦âJåÖ…óËùÆ¢Š_ËY,*V™Rï§êΰ(ô&mݽW™ }„—®4z\/_d=ôÉó´ÅQ]…E>HÛ,xtsؼŸMÌæÄH–âlà>?”S@G² )1DÛü|Þ,éh¦¥íMäåóXO¹müM—™šÌß8ü5vhFˆ:"·0ô8qâý–£ˆRù&Éh»|žoyÝšUtëu¨¼l¶Ñ"äxÈÁ»"ÕŒŽÆÍÎäH"¾†J"…"#Û…€B@8€DÎë·Xô¿¿ÿCÚ¶g_L~ÚÆ€·zø76žíD¤|)¯+½¸~LWN¹!d®+,ˆP9œìÍÁÓrÅÀß ^9ìÿÈfH ¿’Ãø;a›Þ„rÇ D1°_¯-QçV»£jtøÌð¢1m^ü@<¦IÄv¼,KAìQ§~7¯ˆðw´I¢h Æ(‹'ªkéO›^¢ÃÇsäËPäu„‚ LƒóhÃÆTV6W¿#^£MPÔ×TVPÙ´a£½«‡Þ>xŒ:ø]+Y18µÊ5²-;3§Ç[D¹üŽt¦ám;t‚†48é-ŽïººZÚüúë‘ûc´Ð@ýªÅ é}·ÞL +æRfz†Þê]yÚæÜyzòÙ?)B§ˆ×Ù1:@F3 ï¦qÔУ««”("£Å舋¸Ÿë9ÙÑMÏŸm Í-tGÙ å¥ÿ?L‡sŠè0‹DÁ6¢ŒÊŽ&ªdqHo‚„ôÔ©zúKýZS˜Oÿ0«”汓6™ÅÇÀñ¥·¼HÇÁÆvŽºg×AºÀÑD8ÿõ¤`[p¾$yi?qõ±;o§Ù3§³8‹grôX'Ç% ‘Qböo·@„¦ðÙ¼–_¨éSE ²HB@! ÌÈ,9É÷Äÿý=ó—bò{4ftù÷©òÛ–ß“!TX@Kx¶—u¼†Ð"~ ÑòXƒSR”,ˆ`É ûg.±HÔ¯X÷Žê¿IÂáÖsã›M(sL õm™-+nùÜjw €a$à…ÅW:YÀìbA+xcœ"™)Jö‰ÿ±G„R.ÉBø—y\ªcvbFc[D 2Æ+¦Gû8R¡±©‰¶îÚKoíÜMµõg•//¬y£+ äñó`]¶|9­Z½&ê/î žíšå‹)—CH]=½´‡§›khiW¶UƒS©ÄÄ?pJ—æçÒ*žV.3}dJ·6¬Ÿ´ÿ(uûzM”x9 ÊÞµsØ¿O÷C°Ó¥Á©7ÄQ`óÊfÑ5kVÓzžŽ«„ŸVÁ:.“%aM¡úóèÅ×6Ó«om%¬“¥{,B×ÿ4o¶A¤Å50›ÞÏ#ïêg᪥×öj錄_h¤Ãíäca6‰E˜ù9ô¯U EÓ óX9˜SHÇ3óiˆ?#y¸- »Z¨ŠÅ¡„ v…³·‹o„¾¾÷âq<È׃´$-ÎΦJyäÜ*Hñ²X„[¥Ñ¨¢p…é܇±Ž¢ßœ:£+G¸þP„Qo"½ƒ§[¼õúëhöŒi“j¬ëäƒD r~§ÅC µ!9ÙKY™šÓ͉@äüq# ! „€P ˆ@¤’w£^Ù¼…~ð³'ÌýÆ7ZYÇã~s%§ðýkqA-]´@™Õeáüy”“•%kEÁ6dV‹"µž.öobm"<ÐkWBMx¥ò8Âtˆ0B2òØ+ò+‘^ß®Rƒƒþ±‘·ƒZ=Áu, J¨•Ç"Ö‚§ÏrA(°æ ö}˜NÂ%ì±2‰@d%Í(Êz{×zõÍ·éÀ±ãÔÛÛÏ‚ Ç  „Ň9edò—ÞwÞõ7ÊÊãEª×/[LY£Â DŽ“õçéPÍêç µº€_H[c¸âž©*/£ŠYÓù‹~ä4…õÖ£ÔÒÑÕÔr07ÏüþiêêìŒX–žþƒ£ /¬M´|É"ºèë¯\CJö ó5^'kï¡#ʺOJÔV˜qÒª <¥¤üÉÚåTd°^\°OfæÑ^^—éŠÖ‹TÑÕ:V®²1Â?°áXG'Ý»ëù õ¦¨”ÇN%‡¾¯-Ìãè¢\Jã_“ÓýS‹|÷ï>L ,^‡KzÆ:nÊÇ2™­¬ª¤wlX¯ˆ£áÊ•}ñ%ÐíóÑqŽdŃ’œI Hà\F$D¢àéæD ræX«„€B@hH‹ŠlÓCàxÍ)ºçÁ‡¢ö3é©ËÈ1ðI©÷ª%ETµh¡"-˜7W‰R##汮 ÄB õ á—)"‰0í¼#ªÏƸ‘ÆsàÑ}C‹Rù÷P¿ãi*:ØÙÆöª>ã5Ç1GO.Ž–ØV5Æ”:®z¹ýxùØß AŸÑŸFBÓ†s]H#ÿ°8‰éäͦn3]¶FÆÉ"ýÿÿt—pÈ@IDATì]œÔÄ~ÇõJc ÅÞ{ÅŽ½cWEÁ‚QQ,ØÅ^øÛÅ®¨(½÷ÞÛõBù¿/{“Íf“Ýd7ÙÍÞÍü823™òæ›Iv÷}óÞ$íç@ –®\M/½õ-]µ†öîÙÙ ·ÏìÕ‹6jLûö틬ÊZ973“Ž?èÊÉÊTÛ*(*¦­¤M;vªy±Ž4­_‡z´oM¹Ù~¹ ‹Kè§ÿPAI %E)P5hÓ¦ôÕ—ÿ ÛV¤Ë19%…ÚµlA}û\EÚ¶ŽRâØVßÃë÷ß9óhòGŸÓš ë©bÏÞèЬm¬½ËZ5£«Ú4ª]±Ö•Ðw·Ò/›·Q~E9Uì3…ìa9îèÔ–ÎnÖˆ"™Û•Ù5™[í¶-{¯»± –Ñ—ë7RJ’Oz ,j{)5’¨fj× .Þ¤µ¨|ÌG¥V ™´b-M^µÎtÍG‚:LåµÞ¼Yºª÷¹thî”Âi¼…@¿7/[I{÷:ð,{khUF|.åådSvVVÌÇ”Ä´TÊåþÓRSÕþŸýø'5>åÕ—Ô¸ŒH$‰€D@"à=.½áU¨½OPã2"‡Àîü|º¢ï@‚À !9%•:¶iIíZ·¢ÃêNÚ´¦œìlÂ÷ebŒÀqÇÿˆt®SZ_W^NŬã:ç:´ÞúNæßEÙ¼Îj&'S–ÁzƒÎdã±×`Ö{ŠCÉD“7Jˆ0—ø+âqïæuUÌs z´:µˆÄªÄ}gòúj¿·“#jÈz¥<^¿iÿm½‚GK&ñ—9‹|˜~ýã/ôáÔ¯¨´¬<"¥³Ò·…aƒê|@:üÈ#i%¸Õ±èºy¹tx×”§!cöóc³rýZÈÊäâÒ2Úãò‹r¤ðË7+#:·jJ­›4$(¬D(``ú¼%´-¿À‘ |±øóßiá‚ùa¿dD³1†ôô4ºôœ³èŒ“§ZyybHž»bœ…ÅÅ4ñRúô«ïhÞâ% ÖÑŒ_¤n]#Y'=•FÜ•31i·}¬ |直 ¥ù…ôÓ¦m4cû.*à4ÖÕCŒºÝkפÇêB‘|½ä”oXšõiÜS`.äÚÍ_¸ý3›6—R ®_ÙT`ÁÊzÁs—ËÕ©I'5ªOkæP-|˜ ®a-óL¬Å L²þwí(«àç+¸¬Ý¹Ð¶à{^÷S—ŽéÂ^§S—NøK|VÀs¬-/ã±E ˆŸíÅËWI‚(¶°Ûê-ž‘ŸWy99”šê#y%A$‘W‰€D@" xIy޼*á¾ýûè¢o§"Þ,ì…žžNo?÷4Õd]• qFÀI‚(”„¬/ÙΊü—õá…®P™ä°Ž)ºJþÃ/$lô-ÐDvõ2ÜDìC(Üc/c=jUZ ±%• Ň23Ùe ™k²"·{ÇÖÔ¤^ñËX龉•ï«7m¥MÛvR9¿°“yAkÉ›€ 6èw/?°ilmИûmѨ5ª[‹Ò5»—Ñ܆m;hÖ’•´»°Ø‘~ÑfqI1}ùÅçTÌJÓP!âùÔ4ª`Å/X]éEÔí€Nš»ÞˆîÚO¿üõýüçtZ²œ×rR & xm¬ë²½ûØ‚§ ײ©å>†°~˜µc7ý»cý»}§B´àd‹•>žNüáC}Ì¡R›ÜœÈŸY›S롟6n¡sQ*Ç  1lòîe2VEM²2èÐ:µé~Nz0i”œÂûøžÕu >Y½žÆ/ZAéÉ~zÌj}Cu™Prcíth×–N8êp:áÈéVMï£:ñ«l2‚Ï_²²^¬°PxyÔÑÔ®}ËŠa+Â-¬w:±ë/(ܵa+æ Ù’hÃ–í´Š]y•”QÅÞ=ÊÃiGE4ÖÔäÊÍJgR¨>5a7Z¹• H¯x˜–°2{þÊuìâÌ9eä]ºd [ý?'•æbT°¨¸õš+èäcYq½–”–Òw¿üNŸ}õ-mß¹‹0ÏŽÝÚÆÜ7ËÌ §9òx‡:Ò¡‚X…[Ù2oúÖôùº ´‹­_JYN|ˆû¡Ú÷´}Á2ç"&¨nb¢j?V±Iü¡pß?shãlÎ – cÀ‡J·U›­°z5iDÇ6¬KõY¡‹ £’¡ûxå³bûîsimI©ŠŸk]§°œuëÖ¦ÞgžF§w,“Ú:‰d2VDCÁšä€öíd±’×J?ø\˜5o¡aQZmÙŧ[»ÿúo[ŸÚyŠé‚(VŠü6 ôç>ÿU•Qº˜S¡‰€D@" xIyrZF¨;|”.Yæ y“xÃêà¾7zFoâ Pâ%„‘N/di(¬·ÛÅ¿÷¶ñ6Ç5èúÏg}’МA2DŠ•ËŒ+6$ã—¢øó’ìq•ÅfçÀVü)–A•„DDâ׸¸ÚlÞÕâ ëBoÇë!6ÇÀáH‚(×RpóöÞgS•óY˜IáÅË*ºÍ[´ c=ž-œ9ëCŒŠb6]Û´P\Ι‘?ÛwÐþÛʰì)gv„êïÓ¸¾ƒ m€p¹5s²¨~Í\ªÃuùÏ( vÁ5ÅZÉz²Ê¨Ž<øÕýõ—Ÿhíš5¦ÕÜR˜ PdWõ>.¿àܰîíLŒò,·àJߥÕ6úγbÜ &ma.¯k×’ÎoÞØ´+¼Üñ-æyZÍkë{&$ÿb+²mLÁò&Ò—¿t˜$j”™N/Þ“ÛŒ´EÓ!ÞØÎò÷û{í.w†ìÄx*xë19t[àÒ¤>µd2›ß ‘üã 瓵éµe«Õg6¸„“9üü³²¾eÓÆtË5WR׎‚È`'{“m#-AÔ‰-Â2*‰HãbŸ[^^A³,2ìQëÍ©¶KÖkU™  ‚$zóÇ™*¾’ R¡‰€D@" xIyrZF¨ç^{“¦~;Í3òB_ÒçÒ =#Oµ$Z‚ÈD7dOè5òyƒ0Îý)bý´7±ÑàTJh ¿BäCVü A€kjeWXAƒ K$\EyŽ*iGƒŒŽ¶AcÚ¹s Ž‚ÀµŒç¸‚ã àÊ‘WÇU” Û˜TÁüÁR¨ÿåò_8½œ›BI‚ÈMt+Ûù1qòšúÝ4ŸR=š>yG`ísÊigPƒ BZÁXi_/ˆž,V¶iÚˆÚ6mHÙavý£|9[%”±²(ìòǧ‚ç—!»¨A:~Æ»ÐÑ=ElÕ°|ýfZ±~•°BÝŒ ²2.£2hoË–ÍôÝ7_Àè1( +Ì™”8ç´“éæ«.‹9ITÀ¤ËG_~MŸý=ÏUEÔë'plœ ±®ðS8{¨®NѬ|Hð äë>Wèo&„æìȧe…ŠÙ(^¬áÖNš ÿGÌ÷‘”Á ä»u¢n|Q,¯›·ÑÓ –)¤Ž“ýáù¡†*mr³©'»ž;Œ £Žy9¾µÅþ¤õS²ên>‹h3?o± xöRù½u[]tö™”““«®e?Œ€$ˆœ]U ZøžñÎ/sTà$A¤B!#‰€D@" ð$’ òä´xJ¨ ÞØ»mÇNÚÍg;ïØ½›Êøwá–íÛiý†M´hùrZÃ;b£ Ëñìªüþ}Ô%ÜE ‚H¯ˆˆBRhr@lä5Ó3Š Æ`F…žOå/…s Ça”ÎÙØ­5ðë±üu,Ç ä¶\×Á‚Ú1ƒùP)ÇáN«°ÒŽYwP$Ç›‚œ8c¨1E€ç–;Þ‘%Ad¬H‹Nœü>}úå7¬cÖ.óZ‹òa…"½U›6tÒI§ò¡ã‘[$„zà0Ä,Vè7oPŸ:¶lB™:¥~£6­2hñê ´vËV*fWvQÂcÚ,®¦}ÿ-­\±Â˜‰í—¡ýÔ»×t“D± 8kè‰ñØt|yô§™Ð&“‡W¶iNW±ušÀYyŠx¡3ÁøÇÖíô-[ ­åƒ1 Ùz†hQ>eŠ„þ5Ž˜hÑ·ûàÒVÍéŠÖÁ.͆i>Hœ—ø­©ë6EÚ„¥z! ¢ry7gk¢Ó×W¬‹r™ ÅC%°YóöòÕôöŠµÊ—K;T®«:µoGÃîìËgņœsHô„nFDÎN_u ˆ€Ø”?¨ÀI‚H…BF$‰€D@"àI$AäÉiq](xsëx„öIJ~Ó&Z·qmØ´…Ï^.QŽDغ}BùL å+ç@×À™Ã\¿Žñ+¿Ï9ƒB8ð›q,ŸC$CœðA¤EÄÂÖ\á ´<Ú2ŽÄ¹m£)A¤oK´.®¸g–F @)nYƒ±"iX*‰€MÕJ.Hƒû¢œÕ«p£'ÊCNè¶D÷E>®˜Œ¡¬²Î B@Z;O"nF¦ø{Qª{î?È2çPÕfý[_•‘jð‰§Ð’ rýOØêâµ÷>„ž5ºu¾î÷±µÎYçžGõëGnEde(x¤3ÚªqjÎçÔâÿ8¼:*‹n–F» ‹hífœk´…Êø‹M4m†›¼@·mÝJ_|ö »¼Ò¾F}5i®Gïó[ñÆ+.¡ Ï:ÓÑf+à³²}ú9Z°lyCoT6â<“µ5Ô83“ÆÖrÙ’ érþ X_\B¿nÚN?±uÍF¶fIf<œ\ë)ñ1„¯ÁDÙÍሞ](­òƒ7âq‡©XÆkýž™ói)[G ÂT‰ú60Þ˃lÌ€'4¬GÇ5ª§œÿ”Æ–SÀ¸€Ÿ·þÏ¡ <NbnEp<‹Ú¶¥á÷ÞI¹ÙÒ’È fÑ–)⇋—­ä âkœõqQgv1—îâ&ëÒøKJs~,ÜŠI‚È-de»‰€D@" pI9i{ÕØÊV>Eü»­7SnÞºM!€V¯]ÏP¿ç]øü]ú”½¼Ë’j)ßù÷‹—•¿Ñã¢ïˆÀ†¼Qù­q££hAVuH ¢Êuçˆ &ˆsi¶2Y"zÇô-!äwŠ 2VPv ^ËG‰B°>÷âHÈüoˆ‰(§”ªü¯²RhS´+”àHûèn“Ëb¸"_” èSÓ®(¯\5õµùþVµ¹Þˆcœ°ªË¤È!¸TƒÉxÔû1ŒH‚ÈE°ÿœñyùu‚’-ªàà‚Á|“&Mè„“NQ\ÁØ‘Ë z +—s³2©n­<&в(ÿàŽîãR“S ÏÙ»—Y}¶r‚ºb¶Êgg8·hï`)(*UÜÒá™Â ÆÍ€³‡~üá{Ú°a}P_ñü²”ÅxÞuË tÔ¡»6ü-ü%rüëoѬù‹ÙrȾ‚Ø’`!Öv9½§k:¹IZÉ$Éœ»éßí»h!Ÿak!̼Ó$…‹zqŒÈwÔÄ#¢&L¢hïà®S;"60ùuÝÿÅÜZcQ„±ál¢jæÐ!uk±[½ZÔ†]Ð}¿a3œ»X1evj¼VÛ©Q#™ºБÜt5¬_Ïj5Y.B$A!p&Õ¤‘ 02[" H$¸! ¢¸Ao«cXûÀµ[Yyð†Ù¬Y»~mܲ ×o…ÅÅ„ßðp=É~v®(¿yc#TàøM¯2U9d³GŒw_ë¹MjUsñEB…Ð öá@&ÜÏmfÝR oˆŒúÉ#¬ "Ëð¹ÃéVE9mÃfubY–åqK/§j$qo°èjÀz5DAķH:r¨Ž$ˆRßÌ®]ùtçƒÐ6VjGµ\]X05xuô1ÔºM[½Ø¦éh8¥>ÿ—̾¬pø|*΂¯E¶*ÂÁà8sH¨âq&‘B±v´TàeÍ„v¶ ÊÅ*¬X±œþøí× /Rñ$‡|cO¢zujÓ¸ÇäÕw¹…/ OŒ›@ÿÍïÔaÖv‹ìLº¨USú}óå\¡]|^H#¸Cs#ø×8b¢®/ÏgÅtmÛVtU[v{çÆlÆ%‰ŸÑ)+×Ñ+KWºn© KŒg¨¥2èµØå\ûÜ:š-?XµžÖðN´x…Cºu¥ûØ·t[˜ÉàѺ˜KD ¢6-š³ÃÛwv)V?Ù•þÆÍ[˜ Ú­Xíe§¢‚ÏnÆy±Ü7~‹ÂCŒ²iŠZþ=E 8PÇL=‚š4jè@k²‰ˆ°K…Ñ E,‡ÅŠpq†3Š ø¯úH®Ð …mÆ‚üž$ˆ„ÜfD¸('Ò¸šÕ‰eYC¼Eµ¢Å#®¼“¹cœ…s¢rø/“ÿÒˆ!h„òã$Aäø°:9þEšþßìè&ݵ³ŸêÔ­G'Ÿr*edd1¡©Ðw£PiÛÿ_ecüöý‹®ñ(k—2IòÃwßÐömÛ^~žùÒÅ/š#9ˆîïw«mk°pмóѧôîgSÃ-p͘߷°¶}îÎö+4N[ é \ãH‰¯wP<%Îêyæ®”Ã_@ƒKè[·‘f\Ð3Ìžïš1—±åÌãôk=–snÌW\p.]}ñáŠÉûQ  Aâ¿1»xHágÄKÖñºF¡¡õêÔ©$}²™Ø®AËW¯ñý€7ê<§w>úL!€V~ezì·±ƒCŽKSø¾Ý·ÏUtîé§Ä¥Ùi%V "¯lÜYTÀ› ae—tªŽÆ†üž"ˆ„ÜfDOå4Z¹˜ÕmŠº P*RÚ\( ÓvËryƒ‚Ût!Gô›Æ8¤ò!ü!{â¾i×Fc7-Û’ ro˜?ñÜÚ¸i3ï)¥ fm‡.˜ãO8‰Z¶je¨È »°m,1*àKÅêU+é§§ì¦â0 #‹‰TÞ™[0Ÿzg_ª[»¶ÅšæÅp€å£cÆÑºõ›™à,r~ÇR ×¶ù(ýwük1ñáåÏ5&‡ðy¶ŽkØ€†tïHûy·WÔAƒ v=½»b-½±|uܬ‡¼¶ÖõøB)ž›MÍ›4¦‡ YOèeMät´Q"=Qd—Q¢Ì””S" H$ÞD@D±™—‡žCÿÌžG{÷TĦÃjÚ t9çž~2“DWWS<2l+‘Fâ©Cв$£]¬çE$¡E Y™oz† ¸ó³2ˆrÚBfubY–åÑjì´â¹Gøƒo”ZlLÍú(C)ü-Ëc„×÷J‘Ã3ñáÔ¯è“/¿QZ-¯ØCpSfy± VŒLvvrêi”§;ÇÆ–Ìcïæv³™÷÷ìZ®ˆ{Ák s¼„222(Ïq©w¯3è’s{ q#¾~þÍ÷ôþçÿSââ‚;&ÇBŒ×v8¹ýk1퇣ÿŽ¿ ýý$j˜™NÏ| Õä³y¢ ¸ôÿg.-/𯿨ڷYÙkkÝH|œY—ZøpÙygÓygžjTLæE‰€$ˆ¢0Õ%Ae‰€D@" ¨ÂH‚ÈýÉýæÇ_iÌÄוó‚ÜïMöpÄÁ=iøà;%ñD Ad ‰§¸‘ô]ÎDQ>ëËñwüFš$Ѷ('Òq»{3¢Ge4?fuŒÊ¢£ò” …±߉+4€p‡ã.r™y7rè?"ÌÆî„°µ! "‡€D3…lqÑèp*¯¨Pì8Ü 6¼(,«å,5f­”Á° :êèc)--M‘5-Jl­“)esyy9»–ûU± BÁ‹ s¼¤  ‡rršè…'¡¶ªˆ4Àbèñ1ÏÓÚõ”&JËÊ©Œñp$Äam‡’Û¿¾E s-⨩MûÖ¯=”ñ¥ÓùŒ•!];Сu£°Ü2ÀesCþ›O;Ëc¿»Ì‹k݇{àÿéü®ÊHOW2›7m®æúQ.ŸÃ"ƒ³H‚ÈY<ÝhMDn *Û”H$‰@õA@DîÎ5Χ¾{ø´xùJöÎáàæKwÅNèÖÛµnEÏ?1<¡Çð‡"ˆ t 5^ù…û¹R&Jù>Î.*ã?èê =Ÿ'"È^©ç4ÅÜ`|¦uŒÊ¢a£>(«ÕØ™Êoó†hzûtÖ¯‚Êàk:_á6¢œÍæ}ÅÍÆQcîU’‘ƒØ~ðÅ—ôÅwÓØlØïV®ˆÝÌáKAØÇ¥p»öèØãNdY™Ü +lÕ,ÂÖ8¿ü4–-]¢¼È1J¯*Ìqð{6»—!99…Î=ídºôü³D–íëoýCÞ|G­·gÏ^*â3£q\ÛF²û×7b¾—}àëy¢”¸–Ë#uQ‹¦t]Û¸i?˜àòóæíôì¢å|þ®ëlHåÕµn4¬}<Jà)ê{ÍUtÜQ‡•yQ €çñ²•|h®ü1Œ®V•‘«ðÊÆ%‰€D@"På‘»S¼;¿€®¸} íáMÄ2ĺuêÐÛÏ&œ+-Cœ0#ˆLt q’Ò~·6äîèŠø·t!ëv@Y6°/YøBöJÂð‚(£¿iTÇNY´gTÞ¨]“²BC§-š4Ú„»¸,þËã?¸Œs%Ý•Ž¢kTDÑá§ÖÞÁg=‡‰DÁz–r òömZÒð»ú§H?•2’ ж˜V’QLá–I$‰€D Ê! "w§tÖ¼tÿȧ6»Û£l=“7¾>fÕ®™'Áˆ’ B~“ÄELÁÂäÑÞÊ«v[°Ð8UŽ6CèŸÂ ¢œ¶?³:±,Ëò žV´Pqmy¸Šgá +!X eUŽ eµåCµkûžN¶‰MI9„óïÿÌ ßœ¬*×E³ûøPÀnæLW›‡K2ïÈ?ìð#©MÛvBüjrM¢Ë—Ò_ÓÿP­½<­0ç—Xn¥{9íAQØ·ÏUtÌá‡h³-Å·lÝNw=ü8“AÚ'¢b¶€«°bgÔ‹‡Ö6Äó¿ðELO‰cÜqQÓ(ͦüdÂá=¨c^n1ŒZ¦!.8èð–é³hciY€¦m9pÃÓkÝ`|p§˜¥±žC‘¤I4ö‘©Aýº5dV¤xÕÅ\ žo/îÜ»—w…ñg¾Q¡™’âü.ÆI5w£™>—‹F}Ç:oÊ Ô.§¼ú’—‰€D@" H¼‡€$ˆÜ“K–ñïìÒ½œ»0´žÄz‘ç{ˆ7¶ È—‰" 'ˆBè@b(Ut]E9†¬;)Í~1*œ†ö ÄH#¸¦+©,£Õ@iã¶Áí)¡’ 1­/Êi ˜Õ‰eY–Gh𴢉¸öâøËd¹3ù=7q°~Øh¬œ)TY†/î#œÜï5â$A1tþŠP°¾ôÖdú•]tS7sZ,ʃÄò¤¦¦Pƒ¡.]º±[! ®ñŒœ@ypS5wö,š5û?ª`k/(ñ¼®0×»—ÓÂ}܇ÒmL ë í½Pñ§~EŸó}+)XÀ—–ÚkÏCëZŒÙÿ¡!bøˆq”Ò¦µ¿(#Ò¢¼¿,ÜÀÚ¸! 泈ö›(†ÑºB`ƒ9[YXL×ýñe¹ HVeÐD¼¾Ö5¢*QÈ›•‘`=‡ Î=ƒ],ž{¶¾ŠLG€ "<'8oªu‹fQŒÌùªð.½nã&Ú¶c§aã ‡ìÔ!:‹Lƒ–ñ6*aBÖž^ ’ òÊLH9$‰€D@"I…Ç(šÛ¶ï ëïBeü}M†Ø Pƒ¹ã:þÈÃcÓ¡ì%ªF…Ðã>8š¤]•Qð]¿ÆIhžÊ¹?E8ÓZY\ц¢‘â|5nÔ˜6OÈÍ¿¡CQN[ȬN,˲<ʘ+åÂ(”‘°l {@áða¤?3H[_©«ÿŒpŠUßö# ¢ÓVÛ]P@ßC›·m×f«ñŠŠ=l‰Ág¹h0-–À†mÞ‘Ü­GO&‰$X¥TÕ€ÞóçÏ¥Ù3ÿSI!Ï+ÌyÝÀz"•ÏK2 ë×£GîH5ssnæíeFŒ}ÏY®â Ý… Mù×·ˆá#EÄ1Rña‰<G¾YÚ_±ºié4úà.Ôwðûï ¾.„yæ±ÓééùK÷r©1xî<¿Öuð! ™±¶õ(ÒÚ·¥aï ä`g Z•Ìò*A”—›C<¸#pͺ ¦ß@õìz€+뤨¸„àßÞ+AD^™ )‡D@" HÂ# ¢ðES¢œÝJ]Û0íØi¼‰(š¶e]cjÔH¦k/¹€.;ÿã2×}´Q=ˆûÂDÙƒòCOŠ 2’P«™Â}Xád`XÁó Ò p¡„>”+Ça¤Ög]‰ç2"¨y\6(hu×Ú›Q–ÝoЮÒ{e»Ð÷Àô;‚èP2þXœ„8´Ô …ÖJ¯kÅŽyܧ˜ a¿CIÙÇ,¨Æ¼…‹éé'>ü¾}|– »™S²Z,þHÄðšà— +Y{ô8ˆ:tì¬ìÖWe7`‚åáeS^^NK/äs‡˜b‚!ÆÙs Üˉ)€û­{x·Ì;Ь°WœŸõسãY±¹Í°lAQ›Ä‹õaXğ页 ¡üR‹˜o}ûÖ¦ÕÈÊšféà¶îìÜŽ-‰˜¯¡0¸ §|&’ïúw>­åwÌ_Ý ‰°ÖÆÂ:—Ï2 ëÕ£‡îîOuj×2º-ó"@@Dö@“‘/IÙ[7²´D@" H≀$ˆÜGÿ¶{ «×¸ß‘ìAAžO?ñXxóõ‘x! ¢0zx‰g¹_‡ä‡É.AdYF£‚,7H$FÐ!³Î&JÈCØÃyÊ}%¥ùÏLUYOS2ÐB{CW=‚ðI©ÜÌ«Ä9ù8H½oÖ·¶m¯Çuc÷º¸Zù$A¤E#Âøg_GpÑevöš-*.¥=pÙæ¡Åâ{-@:Ä„BZ“ËÑ­ZÒá‡E™LJ$ªR#/ÇVÀOÿëZ³j¿,qÇûnå„ü)l.Ísa@ì]zÞYtþ§š Ê_¾j5=1n»)* º‡ ¸˜ƒ\Èà¡u-äÔ¬äÊ,L¶?×·æ‘k_[S›'ʘ×=ª~ºÝÌik‰Ö¬<óX‡³väÓ°Ù wA¥ÊQIäç–sp1g29ÿþ}¥¯i#p"Ì“‘=à$AäÃKDöÖ,-H$x" "÷Ñ6òiúgÖ÷;’=øàר…ž¸°D$^€ Šô,ëxɬï×A4I1#ˆ4rëõSAiŸBT¹°JªT’¢ÁmÂ*I¯3ô|SÙ?,}`á#‚^¥m eôiQ/¡®ìJîJaóXŸœöñÑ9‰4ž$^lq[O£ž…æ.dÅn¥%Šp¥l±â%¿³`!%\qi_~^^M:úØã¨nÝzFCK¨¼íÛ·Ño¿üLùù»U¹ã¸tT¬FÒÓÒ(#Ýü0rXXt;  éw«Õ&é÷þ¥—ù ­=ìÕ(”ñ9D¥ea|&Çïñ3Yóá¢_Ï¢¸v½‹8îiŸ}ZÛVà½ì^îÉžP}ýAñqÁçæ„%«è‹u›Ð°k!‘ÖºXûxŒ\xÝzí•tÌa‡Ý–y "{ I‚ȇ—$ˆì­YZ" H$ñD@Dî£?nâôå?U‰ ·î£åL-›7£—F=¦XO8Ó¢lÅÇ˾όõK¶Ú‰Wa‹z«âA“‚Èa¹­ŽO–c{–_± ’QtËùæ»ïã3†JC~àCù^ÌçxAA+ÔÜ>e8Æ.È ƒHûç ¾2ùÜ›®ÝºSÛ¶í)•´^‡6ÜVCeLp,_¾”æÎžM%%ì«Ҵ1ÑÆó‡`Ed0ÖìŒLš8f¤Y‘€|ŒÿSXÀ}ñe@¾6¡¬ÝPëÛc/Bëë£ô­qßxEM‘‡´6î+å^Ä=ø>%º§K{:ºAÝÀÏ‹ØíÙK÷Í\@Ë ‹D'Ž_i­ k;ÔúÇý‹ÏéEô:Mþ00‚z,­Z»žËŠu\­°¨˜@&±¸2ã—ðK/bb= ™´ë]ÄqO[^¤Å}qù¢=qO”õµ^#™Þ=æ`Ê`R2Ü¡¦X«ï¬XK“V®còOä:wµ#‹s½:ßHŸœìì 'QëæMiø½)Ï*’!zŠJJhñ²•Êf€è[s¦<#YL”7kÔЙjÏñæ­Ûhçî|ÓÙb{>ÓÏéçý⌸°n@ ¥r'SDîà*[•H$‰€H‚È TÛ\½n=ÝvïžúN(aÕKÕHN¡§¸‡ºvêXõ—#JD‚ÈEý´J’ J„…kSF׌MI"/®ƒ$ˆ"‡Q©9峩4õ»Ù2(xç¿g¥l+ãKJK£ì1²ê~5·ˆ e·hO¤µj”5NïÝ»‡p6Qûö©Uë6”ÅŠ[Xë8­üÒY¹BQ«¦â¢"Z¹r-]ºˆòYY¹´!ž2jå°ÇLd³ytªÅw &*Î9ý$ºüüsÂvQÈXÝùÀ£!×%V@Iy»é«ð·§_Üþ;q‰‰Uíë\¬[m®Xß(a¼¦ë"eT߬î~*áµw_—Žtj“¶v‡a=öýg­*dׇÐ~;q­› ®-3y'˜vVôe³ø7â¡0D’¾–L›!àE‚HÈêŵϠPÁ ™Aœæ²µhNvV¨®czOD1…[v&H$¨QTðYªŒÍ–WÝ1ˆ]Ý—X*/ E6ÍÞyS:íx> G†Ø# ¢Ì¡Ãp ò˜n.`àU=‘èØëä—Q ÊX͘=—‰‘@ë ÎJ/û8³°°0Š#«êW¨"&Xþ\sËQý×õq?[åP“¦Í©eËVÔ¨q…[27”az ƒë5C›6n U«VцõkUœõ »XȤ—Ñ©4”€V„¤¤tH®4è–º£›¿d)7!¬ëCœC„s.”`´¸díøW²ˆaÝŠ8iýz÷pE^ã¾|ñ¿¾¾HûúÚ˸T»=Ú£ÕPŸ3Q×øŠõ¹hWÝÎQzr ãBæ&òZ7r6[„: uð|Üg_¹SÌÀò¼èb.‚aTé*øüËËÉV¬K½2PIye&¤‰€D@" €$ˆÂcäD D[·mw¢)Ù†’Ù‚èÒóÏ¢k/îm¡´,â8‰DÅ@¿‘+Q dw|mT•{ù%AÅâ,f…ùÈç^ kÖ!8«½ó®‘Xº™ÓªÉàV”çfŠsQÃK¢>„;—]ÎuèÔ™š4nªXAy冢ŠuPÅÅEL­§Å‹(¤¬´Ì‚r˜õåt~ ÜËefZjØ´nÑŒ†¼#l¯~ø™&ü)¼ €v>s/¿Ô"¦]“TKâˆ8òµåEZÜ÷¯mO_{[äj9ìFjÔA]¨ïæ×¶‚–Œæjļ%ôÓæm”Âq§B"¯u# @üÀ½ð Pîª / ^§žª˜¼gIY*ŽÅ$AGðe׉€D@" ¨H‚(6“ØoèpZ²|El:“½ð¦åºèì3éúË/’hÄI ÝãQ^€2á,‰Ž½‰ü’ Šb™lݾƒÏotæ€ ÖJOeåå쪫,Š^­Wõ+¨E,P¡íWžkÛDY­V›Ö¶ƒ:Ú{¾4Æ÷sìê©AÃFÔ¤ISªW¯eòîÿô´tÂ9 BÉ+®¨i„¢×½{öR»:+᳜¶mÛBë™Ú²y•²Û>¸‘ ÕžhǬ¯çg°{­tv¯e5Ô®™GÃïH êÕ Ye›“èÏ3ÃúC¶pG·‡çÀ+A¬FŸ<Úµ(îh×»ÙšFm£ºÈõÍêŠ~|äj€ ¸©]K:·Y#$C´º¦¨„†ÌZ@;Ë+žº-ÜLôõ®b*?ßÙL…W .wÔÁ=鎮Ñ7!Ó ¢@‹qIÅpÙD@" Hª’ ŠÍ„fýñ÷¿±éLöÂz§êwý5tæIÇK4â@¢D¡”·âÍ‘£QŒäv‚ªÓT¢cB~IE±LóQã_R‡³Ú \¡Aéævð«®ÑRBÉ-îå7î‹{ˆëËŠ{¢ž(/ÒÚûÚ6™ÐÙ»_qÿ–Ì®³23³)77W9\<‡¯™™Y”Á1©)©”š–ª”C˰R©`…yÅž *e‹«’’b*,(`k¡b*àkII·½O)â"1„öÂ)•QÆëÁŠ{-í@( é+uj×V›‡%Û}OŒ¦›6[¨¬¬\9«(Þ¸”«Ï×¼~ÍŠNÅzkù¢¦È WuDYQ_´‹4Ö—ïªÄù¿ÃëÖ¦¡v°dôÁš ôæò5ªTþ–"U…õ®=ƓɄ³‚î›6ªO#‡ÝK°º“!:$A~±¨- ¢X ,ûH$‰@ÕE@D±™ÛW&½GNý*6É^ØûD=zï@: C;‰F<H‚H«Èq#¨Œ#ˆb(·Ë°$^ó‰Ž}ù%AÅ’üõ¯¿é•·ß£½•n·Â`­ôe'”nnºêÒ諹O#¸PpëßQ›§ˆ\Ù†öžèA”}ˆü@¥¹hE\©#ˆXÿ@É%°‘†¼ÄŸ¸o犺‰€M[` ¼¬Œ—ß|Íåtü‘‡›/äuxËÝ÷›Þ×ÞŒ˜XÅ;Î(Rb= ÉôëR䋚F÷Å=”Õ¶'Ê"?¸/£å•ÃÄĈ©]^Ž)ñ†VwWìQ¬‡V:CW…µ”B.[ißFeüyIôê3#‹#žŒE‚€$ˆ"A-¶u„ûÅ\>‡È+AžAä•™rH$‰€D <’ ‘%¾žö3yåuÓ߇Nô!Ûð!ĺŽm[ÓØG”Ä ¯DFб‚&É‚(Ær»Iâ5èØ[_DQ,ËO¾ú†>šúu%a½!7Ï!Òª¹ý mÈ&î…·Pv ¹õim¾ˆ)εõD vDø^«‚Ò–Ylm%fÎ ¢ “.:÷Lº°×¦ÅgÎ[@ϼô*í«$ãÌ Š¹–n“›f2hó—–XÚ\±Nµˆ‰û"O›qm/ˆ‹²ˆ£Œhió5ŽóœšdeÐý];PK>‹(™çBp»š|qÉ*ú}뎠ûÚ²vâUa­×.A 7sƒo»‘zØÅ¨9™gD!ˆ°FjÔHR?ål Ñ“EñÆ€•¬•gep>]íZ5=3Iyf*¤ ‰€D@" ‹€$ˆÂBäH¹ ÓàGGÒþÊÍÅŽ4* B®åêÖªE/~œ²Ø … qBÀËÿ~Šu@QDq;Ö8y¶¿DÇÞ¢ü’ ŠpÂjèå·&ÓoÿüËòö^0¥|Q9+ˆR %ÓâŽ6-âB[V+•Q=Üuµõ|e-®;m'®Å­(×\ëÜÁ†áZ .ãìDGv0ÝÖç*‚5‘Qøè_Ó'_~R ©ŸO7ÉM#µyb5úòD kQÄqG¤ÅEž¸/òÖÆQAÔq%“ÿåE;œãŠB×½\ Ï[×ZìVQç꬜ß wЦ’RåÌ¢€Š&ªÊZ7¾]‚kÿ³Π‹Î9Ó¨9™g"vñ¹xÙJÕ¢ÓFÕ˜1T·vmªW§6÷æÁŒ™TÑv”D«×m ¼oÃXv¦¥òaÆÀ+n%AnÖä}§hÙ¬ ÝÓ÷zÊÉÉâïÕôÚ»ÓŸÿÎvªyO¶SÇìÆD8‰£“m¹1VÙ¦D ’ ‡3÷·nßN׸‡ÏUÞãLƒ²?øqο±i¬m«–tûuWSçöæ®öýeÌ5¼J…Sä¸~¥FEÅIn—àH¬f{òK‚(Â¥YÁì?;ž–®\RÁnÔ<Î~±¢ø1ªk–çW‹‰˜^.ÒÚôeÍî•CžV¡^i®mÝíxUR˜Ãz(UG2„ÃJòv­[уwõ3­û,[ý;gžéú5z”1¹Yæ¹nPÓ^ä±kÈ:µ}Ö;vî¦| ϼ•1§§§Ña=äs([ÞãßýümݾÓëpÄT>+8B +XZm+¦ŒcgV0‹£x²k$AdŠ YðÚqñÍý<á²Ý…á¹Ó$ÿ®Ão;üäß¿ŸB%ÕÀñIÊ÷l¢MMNQÞÕM7¤S=ŠN9îwd‘­ÚCÀ‹‘‘‚ËÞ¨". ½PÄQåŽxÀU¥b¢coS~IE¸pó èî‡GRaqx޾ |9À6§»Jh‘Ò*µ…*÷DR™¥E(cÖŽþž¢£Bf܃S¸Æ} ,¾å0A·YvC+ Ÿ~?ååæU-,*¦Qã_¤å«×ÝC†Ù{Äh‰…]í†F‘é_‘"¦]—hX›k\[e´iuÅ}QW›ö—5Ã¥ãªÒZ7Ã/‚´mËtß€ÛøpRo(ÌÍÆæõüDp1‡‚Õ› ÚG¥üNiР~}ǾWD³6%A zÁuÓRSéœÓN¤c?ˆêv[Œ¥*¯¨ ™óÒ¯ÓÿeËàïÕ³1ƒ V±ŒDSÌc³Ïé'M'}Ò½+[‡§ÌÞ¹ÿÎY@¿ð<~óão„ï\ú`eÌWô>‹úßp•Zõ«~¥áϼ ¦e„È ŽÀÉ –VÛª.¸[Á¬º`‘(ã”QìfªÏ€Á´ióõWiìzöfOÐu=tdHã—7®)¬ÿhX¿ÕÊË£:5kRff:{ ¨CÍ›6¦†õêñ‹ZÊw¢´´TÞ4“åÍVg©¼FÅY‘ƒuAg¹«ó6UŠ&(6×$ˆ"œÜ%+VÒˆ±"vWXTDp mð«®Ñ’òqZÙ¤¸#ÞÚ{ú²Ú´¨‡<¡tmhˉ8®æ„‚ïnìþ¯j såüVrkgÀ*š©ìvèAý¨C›ÖAU6oÝF#ÆW³’à° ¼,YFϼ8‘JJKî…{‡`í0Aä¹бIB³Ò¸„X‹þ\_žHëï¥EYtˆûH‹rÈCyþ²ápñÕ‹ÝÿêǮ˸ô‚4 "«ãå2ù@Ò»ûÞH];uŒ‹ÜU¥SIÅo&­D°˜¿O€$jÎQ]ÞYï "gfà\¶Ä„BF†½3Ñ;\Ï=óÒ›4õ»ŸœÆ£­$Šbþòó{Ñ€›®ŽÅ[?L³,VëZ3ÖÍ%瞡֙5oÝzïp5h‘Þ½Náó$® tÞ%7|·ß÷­\³.ªaXÁXÁÒj[Q œ@•­`–@é¢J‚(vÓ<’=xüøÛŸ±ëЉž@ÎàÇ0_@ÿ$ñï3aƒßÐû –?Éìö-;;“½7äP#Åê'—¿¿dðf½zÔ´QC&‚SÍj„<¢ÈfÉ6AäÙ£žƒDl ѱPþ ~¿ç̘‘ˆ3 s+ýÚÜ€[î$&}ø}óó¯2XÎç¸@9énˆÀÁ¦|#Æ6î‰x¨´¶ ߇=JûëŠûþ{±EÜ'ÑÿÊÔ ñŒ $hÆ…]4i¼ÓÆNB2ûã=ó¤ãèê‹/ªþÓÓiâ;STÅ»¹,åu[Æîl´«*¨ƒ(3§)Ñ[à7â>rŒÊû×­¯Ž(¯-«­ëïÇ6¾¶Ýýÿ>¿lîößÖ{zz*eñ +#ëRcvo¹æ :ñ˜#ã;ˆï]Dñ›@;ΆËÏß­¸×€ÒxIEþm×^J×^r~PCPŠËçɬY¿QñÃß¡MK:€aîÔ¾µ¡{•¡#ÇY 5šÀ‰ ˜Ñwÿ€›ƒP.*.¡…K—+_ˆ7iÔ€ºthGíyNµaô„×ééߪYVÆÜ¹}êwÕlAÞŠV¯Û@o¾ÿ»­KÜ{Ë•½ÏV1è?tý=k®šŽ$bG´kK«mE"g"Ö±‚Y"Ž«*Ë, ¢ØÍî;Foð‰úOÞà\‰€ŠÀYgíØ¡&ãñˆ2ú [‘GäŽÛ¼Å³ãDÇ> ùkÔ­Ku¾ÿ>žè;ÒwÌ ¢á£ÇÜÌEjIzìfN¨©í `®,ÅHZ´Ú»èÙׇV!kµÇ˱HžÃña\Ä_¼²%ÕæµXÀú¢S»¶ôðàAÕßýä úß÷ÓÔõkç²ghñŽu7ƒXi} ×Íõ-z½óe;Àˆê.^« 9$ „ +»Ëôó„mçžv2]yáy¢)yIEšCUìDx'ä+–D5âÝš Ö§ñ ’ Šù†õëÒ‡¯Œáݵþ9Ä;nÌËoєϿ6l‡4?~ß‚3mXµv=]Ñw0Ö‹Ï5íÝÄ{]1M>NCujÕ ûË~¡'ƽbxÆPÏ®éæ«.¦žvVêDBtVñ$ˆ¬Àçõuhe ²LõF@D±›ÿ_§ÿM#ÆMPƒÇ®gãžRØ’ç »øÎùá3}²³2¨qýÔ˜7-Ôb(\x@IDAT‹=™¤$× d¶öÁŸ ËôéC´Øom¹ž“=¤ËÁ7qË‘‡ävr:¢­DÇ>JùS:w¦Z“ÙSZ‚‡˜D×Þq7•ïeqN~ìã"½b3Ü<ÿĹn*Ïý}Ø•9ܘ"¾Ï"yF–ˆa^ÖeØ‘cÅÊÌ®ŸÊVDïLx6¨“'ø‹é¼E‹üì.aôÉÚ Â$C¬´ÀÛÈus}‹ÞDï’ˆÄëZƒw®a§Z¸õo´öQçÀÎiØÀ;â%~•è71¢$jP¯®rˆ-Ö‚ð|.&`¿Îþ,Þ÷…\fWE>~Õ-_µ†ß³Ö‰ø"þ>’oÉ&ìÆ£ïþ‰I$ "³Ùµ–?”­MÎa«àŽxø3è;¶ ’™Òÿ&:çÔŠ úújÚ¯yU%áuÅüMLôÜpyon¼£&¼9…Þúà35Ï,rþ'Ñ™'GOOxƒ7¤­R‹y}̪ F$Aä ˜²)‰€’ 2Å¥¬«×Ðí÷?¬¸es© [Í6oÖ„&Ž~ÂVYX"` ¡C‰¦M³TÔ•Bv•\®áoZ&K‘Çäö Ä{äO;õTÊ5*á';¦Ñ–­ÛiÐÃvíFð#±˜]uíµq‘P]÷é¶ò\ôìSÐ)dƒer?§ª[S¤0¹“Å»xÂ)È´Ùœ`§Ï³c_ÀõÕ AÙÛî} ò,!1·êmK‘â’’¨ÏÐ2êÈX·×·DôîAr/{!ž·Š_¡ÜÎÎ ïªÀpí3AT“­ï^æâƬntù¢g)ÍY$;?£üjã0^|i Ba7swÞr=qPµ™õ7ÑàGFF…aŸ¡…øN±ÒÛt{}‹ÞDïZç•¢Uu"TÌ€þŠƒÙC)·C­{´Uƒd£‡¥fé›–i‹H‚È"P)VÌ °ÂÂE¹ ]Úµ©~½zŠ‚:–nA$Aù‚ÒïF:ÿŒ“Õ`ÅwöÕ}ùÌJ럷çŸy2 ¹ãFµ Dnô€rÖ6îèž~ø5ëªÛïe—2õiøÝw(çª782ñiâ䔬Ö-šÑ½Ï¢SXa꼫?gÌ¢gÙ-ÎK Ž?òPºçöë ¤ŠQÉõÊäéëi¿Ü¥˜(¨I€86ðVjÇD”QÀ÷ò¿üžÆ¿ö+ð"ßvçMWÓeç÷R»ÀgÖ•·ßÄÛ:5/’ˆ•1ÅsûŒfn_~û}zí½OL»s £5wÚwÝÚ‡Ž<¸G€ÛD!ÌßþHO>ÿjÖwÞ| ]vÞ™¢XØëOŽ£ï~±~ð»Ñ©,Úú„×Ð@ÃIÇ´QnÆç,\BÃFŽ£m;vŽÍK+Ï'sêT+˜iíÔúC› êÕ¡;®»‚N;áhmj|ïÞ}ôãïÑϽÂî²C“tj¥j‘Ql'ùÂo§¢¢Bþ-Û~zÃ÷Ã7ǦúuëÝ–yÈX²„næb½ÐcÝŸE„ð¸‡$ˆ<*·Åá%v±DÇÞ!ùSXÃîåR:uJìùdécJMùt*}öÍ÷Q)Øx9+ÙK-(Ù¿;ˆ\I%üê5@&+ÈÓBìþ§ G³Pžæitù稽üö× záIQ­_§Ï!«YR‰ ×Íõ-z½KrH â…kŸ?d¦Ô¶ºöï¸þ:öˆC½0œ„”!\Ì%$°. ¥caa!•”«X° ¿“D¹99”Æ~æákÞí·ª$ˆ"Ÿà‰O?B];µWøà‹oèéßPÓV"8÷æ“&7CGŽ¥~P=H©ÍÐu—]`xîá o¼§¸E‘óøþ–ݲ­ÏCi[|ϹçöèÌ“Ž5º”§%ªp3h ï~L¯Lú ¨žÈ¸â‚³è¶k/3$*Dq]¸t9ÝÿÄXÚ¸y«È²uüÂ(Å‚ATúeú ºçѧE2â«•1[)#p}¿ã&N¢®èÍÖÀY¢;ÃëœK¨ÿ°¿‰|‡©’ߨ»–vzY_5Y;VÊéËü5sulÓŠjÕÌ3UÍÛ°i õú8­ç«>èÛ|ÕÂó‰6œ|F#•!¨ž ¶×ÉõW¿nmš4þIª™—‹¦C†Çž}‘¦~ÿsÈ2Õé¦$ˆb;Û·Ü3”V­Y˺ý,ü¸°™ôÉî¥n_!~´²DÌ8áâ]űëÖ!E¹CãdJyXn7°ðT›‰Ž½Cò+ŸF¬7¨;=ð÷¢§æÊ†01#ˆà¢cüķ诙³¢R°clV”ìBuŒE,”ç¾>¬(cƒås'§:YSd³‚<…wõ˜+ó‚èÞµÙÿ¦ëw-hëÍ÷?æ³ ~çõÇghEöñ‹»Õ¡Œ6¯ñX¬oH.z÷ 9„—½/Z¬>~¬`ýà È(XYûÉì¢ñtÞAÚ粋šy‘Ÿ¹RmbÊ«/©q ÀW“_¢Ú¥òuL®,\º"|E]‰‘CÒ G¦æŽm2Múè 5ˆ^‰pS—x磩ô[Ô<À–7gr|ÀÝ»ó >xoÃEÎNÔ†ÿæ. ¾CÕf©ñ7^ÅXÎRÓ"R\Rª|¿ÀFmX¾j­b…#òôc0Sò£üålÍ3€­z´ÖÐp+WÀç}µnÞ”7ô¹4eàìÆ»IËWÈýÝ”‰–#÷?1†¦ýö—å6Ì Z³•2hßiLôýêÇ€ïŒøþhôÝD(Qn¼âBºñÊ‹D2ìÓß3ç†-' èe5[;VÊéˈ>Ä®åð›ÏÈ*z3»-ï}Ãå¾(k¸6µeÅó‰<'ŸÑHeÐ×3ÃÖéõ7ö±ûèðžÝTh€û?¼&vå°Ë̺¬ï ’Ûv EµÑ*‘‘û»‹Ý/d—©³ØÍá·?ÿÖͤûùzÀg÷í×_MgŸrR¬º”ýT'>þ˜èiÞã€Î(,lÐx8@:C‚Èãr{ÒèEKtì”?“? ’‡ ¡Œ‹«†î,ff£Æ¿ÌÆú ‘®LüPÂÁÒf¯2³|ŸÖØXq©,Áõ뇔æ,Ž¥pð83zñVäáK›Q°ƒEÇ6­iÈ€¾Êy.Xs>;ž"+£"wÐ Î!â_Ñã5.rc±Æ!½‡Öy%˜Õ‰5Z?))ÉŠB§6ØY÷xvp¶Äƒw PÉQm[2I…ÇÈ‹%`µ×%%¬dß¿ßOâãùÁ3ËÔTX…Ø€ñ¸¸ÿ¶”«Õ%A¤B6kš?z# Üi—ÝDùLøÙ ƒn¹–.9÷ µÚGÿûŽžzá55ˆ^‰+nâs}Ò‡_ÐOüÍ„I:¸ÛŠõÑLVj p3sÚ‰ÊÙ S¿û™>ýzšrŽŒ¨ òñ¦«.¢«/:Wd)׫û ¡¥+VäLzwÂSªÂ7ç/^F}IuÆsD@ õîuŠR.R‚¤ÕGÇÎ8a»™‚È(ÐH$­"ÿîáOÑoÿ'ŠXºع=½2ú‘€²}ÜÏX­ È‹$¡Ÿ7#廕2n`¢ïWŒ¯´¬œ^e|ñ-U° ¿ƒ»u¡á÷Ü@†n`K­‹nÀßM}ßñ®.o½æR:ûÔãEsôÈ3È / *í½¬F8¢=+åôe„pWËŒ»”χ; C;ºõšKèî]Eå:Œ-ü¾cá'*„z>QÆÉgÔl\ádÐ×3ÂÖéõ‡M?|ðªJÊîfR覻 pqY‹-‹àöñŒ¡ã^XCßêz•‘ó35¸`ÉRšþï,Z°t) ÞÃ.ñ]lß>þýÌW/„$þ½tÎi'Ñí}7PxA6)C@€?©»Ûe®ˆ J¹C)¡ï%:öËŸ”›KuøXIÐÓ*„A´uÛvþa2ž}Fï}G|…²^±Â0˜\㯠"7Pi±†ý}ØQÆ6åT&‹äYœS˜vp0,\rDv±¨Ç>…¾{û宫¸ï¸cÈCTÄç_EûÅga÷m¤A¬´ÀúÈus}‹ÞDï’ˆxéŠÃ¬áKì®{ÖP6»Zš0ê± ¶´íʸ9’ 2ÇÆëw 0‰ws Œb–—øŸ]IYGgâLzþIµ”ËÇš¶¹æâó¨oŸËÔ*ðy@ƒò·z%.òðž}pÔs¦g¹´oÓ’íÑU±„Ù´eª†G=D=ºøÝÕÜ÷ø³ô#NÚ0ê»è¸#Q³fÍ_Dx‚=‘¯8 C[êÃU4wÑzþõwÕ:ú1)¢Q¸?[*Á•• ¿†°LFáR&Ö2Á&H+œád'èÏnAÝHÉ>}¿VÆl¥Œ˜èû…ì nîzx+J— ¥sû6ôú˜Çò.¹yP€B_ÜìwÕteï³E’úek¡YÖ­…ÔŠšˆ^V³µc¥œ¾ ºrxëG@d¼2z8µmÕ\Íž9w!Ý6$P4j3Üó‰|F#•A_Ï[§×_®èÅ'R1}ïÓ/iÌ+o«i €$ˆBãcåîǟQý;“泥Жí;žgö‹?}•vÝ.2¾ç]è‰û»Ý•l¿:"À럆 #úñG÷FïÑgK?`hŸ¢‘[?Ž*‘Ntì–Ú×´“O¦œQ£›ªBˆA´tÅ*zrüK|°eqÔ¸á‹>"|yм@‚rÝVžûû°­ 8úÇrD?[-$'³‹-&ˆ¢± ¢ûúÝJÚµ¡e¼~e¬å­^± ´DSÁ|}£±X¬q_?^[[Š<ÆàD€râVÁ°pІHçê±ûî¢lE'ƒ}bI%1)®ßÙ—Øåülbc‡Y±b2’«ÁH׸YhO!òyç:®>·¢‘ÉgÖ‡Q¾$ˆŒP Ÿ²¤‰°¨è}}‘´u½øœÓé®[û¨up&Ê€aO¨iDôJ\ä)r‘o7Àz VL"Œ{uMþø"©Xuþüé[VlwÜÿ͘=_-c%¢ƒ™ü?|ðšbE6aýpù­wÓÚ › »€Ô'¯S­W@ÔÁµ¶žÏ3N:†¾ëvµ}X)tÑuj:šˆ•1[)ã&ú~ññ¶{ °2ÓŽý¥§¦îtT³úÞ÷(ý7gš‘D#ˆ`1…õ …Fá¨C{Ò3ßpëÌ+n °‚Òc‰Âfë; !‹‰pÏ(š‰T}=#¹^XGXO"üõ¿ó˜p–Á’ ²†“¶H๋³…ÐLv»ÁÖMЖ?™õ0É'Dy£Gu•È1#ˆæ³éðÏþåØh+l´Jcµr#QŽÚ™RÑ3÷ãð¢³#…¶¬O !—öNÕÂ(=5Mv…¡Àú:áè#¨k§4}ÆLš5?xwf¤ˆ–ò®t»Ë%x"'†k \Ñm¤ƒw¸^Œ8n‡¥v¾98‹C»ëKÔ×@GrHÊ« ŠxÃbVƒ`p3€ Äy$pƒéå€wéŒÙó -0†–Íš($Q$c°nã&ö#iϨÞ1˜KŒÜúŒ±Áoa-]ÌÍ„q^Ï®i“ª7a}qâ…שi;‘ë.»€n¹úµÊ×?þF~^M#N‰PØ$‘ž–ª<·uëÔ¢z*É›Dp¦=3FO~P7û(+ *÷L <Èä€Ý`e Ý»°eÁ¨‡Õ¦÷òŸ3MMEzr\À9D·Ýûá &«Ao!‚gî+4¬¶¥/geÌáʸ…I¸~õc¹ú¢sèöëxWqe=áuúpê·"©^ Ò"ê@4‘±ÝG‡÷ì¦æ ~t4ý:ý_5mKµ¢.é3Šf"•!\=·ÖßsU\`j!ØÍg¸}Äkêóo¤Pn1µuªc\DÁ³ŽM;s,¢¿fâ ¡åÊñ8" „¢»‰öJp—qËIg·Þï<ÿ,orÊŽ› ²ã*ŽÀnÞw /Í·w^ )* øüAõ´‹¿‚X–!N$:ö.ÈŸÄjöÕàkU 1#ˆªhr,‰€D@" 0C –Q3&ˆVc‚hý¦Í´~ãfW "³yv:ÿ·ÛÕ&%A¤B6Ò¸A=vmö\@¹z÷QÎ È´¸û¶ë袳OSK¾öîÇôò¤Ô4"ᔸ…u‰ã<”z|¬be“žÆ›YÂ=AÙ £ï|<•ž{õ‘´|µ2†³O=†ÝõÎÀGÒŸÿζ,W‹¦éý—Ÿ (Ñw2 ¼9 /’„•1‡+ã&áúÕWoiU¢áƒï ÓO8Z…dÔó¯ÒÇ_~¯¦íb©V¬ŒDûŒ¢™HeWÏ­õ×°~]zcìª]3O‡¢Ðÿeú »háÒA÷«{†$ˆø¼4&„fÏ_H°Ë¸ÅËW(BÐÅaCÍ>ÞXๅ.ÚÉÉ4aä#Ôªy3[•MIt¼öÑĉx¨t7"H:ÑFÝFSEDÑ ç@Ý\3£vAþÞ˜vë­”uóÍ]U…„$ˆªÂ,Ê1H$‰€gQàT¸iA$ ¢@¬«c*™­Ð~ùôm‚±‘’ Ï ¿—-'{ˆfè±1/ÑÔï~RÓˆ„SâÖ$îï3{ú‰šœðQ=AÔ·ÏåtÍÅçª_zû}zý½OÔ´Õˆ•1\{ÉytÛµ—Ym2¨žûóúÜA[·ï ºg–‘’’L¿|òV€·~C§fÍ3«b9ßʘÕq “pýêyÞé'Ñ}ýý®øªAôä°AGXøÁÒO»XŠz¸:ñŒ¢HeWÏ­õ™á®v(ÂÚ÷òEÀó<飩!]вÕéZ "BÿÍÏgýÇ„ÐJÅÕ/ôoûöù,­«Óüƒ z`àÒãBušôxõÞ°3wnt$‘ ŠòXÀkrÕÅ\,:”}øHÐ5£ÀùáZ®F÷îTóõ×ÕnªRDDUi6åX$‰€D îH‚(p $Aˆ‡YJZ™!>ÿ³7ÆvÁ‹0æ•·é½O¿IKWìžÿüÍç)55E-ßwÈ#¬ t‘N‰«VÖDî¼éjºìü^š¢²òrúoÎå€î»ò7†íZ·P~ë "C ‰DÀ1V»ÁÊÎ9í:ÀoA´fýF[dÔòUkiÉŠUvE£O^GÖWëm°2æpeÜÂ$\¿ú±Wg‚è±{ûÓ)Ç©B2täXúá×éjÚ.–¢¢SÏ(Ú‹T†põÜZ\áæïª>ŒÒRSµ·”øø×&3QôEP~uͨÑö»èß9óè¯ÿfѶÂÙøn‡¿ê@õ¹¤7]zÞÙÕ9öX °iÑìZ–ݘG\P”G$‡ÝJ,·bAÄ®¾¥‹9»àEY>Q׌¶Kò'eeQ­?¤äÆEOUê* ¢*5r0‰€D@"oŠŠ‹yW媘œA$]ÌIsñ^ï^èÿ¥§¦îtTEY±z-]Ñ÷5m%¢?×Нs®¹ Ó†pJ\mYÄqüwS&XÅ|Â.±&¼5…òùœm8¬Ç4îñûÕ,=AtʱGÐcC¨÷§ý6îb¬š¶±2†ƒìL/Œ|Pmò÷fÒ]RÓnEÆJ‡tïª6_XTLç^{Gª©lÉʘÕq “pýª`TFª3AôÊèát`ç*$7 z€æ/^¦¦íb‰ŠN>£h/¬Ôský¡o}ÈËÍa¥÷™t9“EY™êíòŠ :ù¢ë9Lm4#U‘ Úºm͘3—-„fÒ²U«Ù…\¾âÆ·:Z…[š8Gó¤cޤÁ}«ž›¡pc—÷ã€ÀÔ©D#FØ·"rIQî:•rK‚Èu¤ƒ;HÔ5#Fâ’üp-—ùÐC”~Þy¢§*w•Q•›R9 ‰€D@" ˆ'’ D_Zâa–’DfȄϿêÂsèŽëyg¥&èÇš[AQ¸©ÃÙ7M7TïÁmÜWéƒ]åï‘w§g¢6óßÜÔwÈ£jZ GС-½öìcj•Í[·SïëûÓ^›»¸­ŒY°Ìaãæ­Ôû†ì?ÕÝ °^€5‡6¼ðÆ{ôÖŸi³lÇ­Œ9\·0 ׯ~°‘Dƒ˜àûƒ‰¾h‚UY­”Ó—yﳯhÌËo™ŠW§VMúâ­ÜIöºêVÚ±ÓgÑ€Šú6_åsÄ^Ñ#¦ïÀÉg4R¬ÔskýéñЦ;´iEÏ?1Œrs²Õì>î§EËVªéê© Ñþù{ÖlúkælZ¾r5í.(àûIŠË¸ý6?[ªÛZHJªA];µ§Ñù7vT7 äxcŒÀÛl5þ Ö;uù;›uAl–ÔÈ- "›ØE[\ƒ}´MÅ¥¾KòÑyfÿþ”yÿ,ظŒÏåN%Aä2À²y‰€D@" ¨^Ä’ ¡îõëÖñ<ÀÏœ£ì@Õ ZƒÏiݼ)Õ©UKËRJëµ6¶m©’Qä“‘‘žNŸ¼>.àõÕë6Ѐž M[¶…l8=-•FÜw'}ØAj¹=ìÊâò[ïæµÅ.=tÁ®ºÏ¥çÓ­×\ª¶òæûŸÒ„7§¨imäÔã¢Gïé§fé-ˆjååÒ×ᆲÞGd»Øú^ãb+à&'ð~€ðÁÊ’x—ÎÒºÜ ×—h?škjJ Ma²®‰ÆÍ\AaÝûØÓAîþŒúéÔ® ]xÖ)ô|pá'‚•1‡+ã&áúcW«ι5"<òÌúò‡_D2¢«UY­”Ó—ÙËÚ?6æEújÚ¯†²Á¢eàÍר÷`]vÊ%7¨iDômZ!ˆœ|F#•ÁJ=·Öú†¾ƒN?áhµÈw=Hó-UÓÕ9’ˆÑÆ-[èo&ƒð½lùª5ª%+6óìß_½]ÆÙ^Ëü9Ù´QCzõÙ‘L©%Ù®.+Hl#ÀßOéú뉖, _Õ%EyøŽ£,¡“[DQâi§º{;U=QÖ%ùA%uêDµ&M¢$v-Z•ƒ$ˆªòìʱI$‰@̈A„a7¿oG¿·˜Ö¨a.Ÿo ‘Lïqåçø« ADÑÍ╞Mý®¿2 ‘m;vÒޤe+×ä‹vÅ~hp€{:ÜûâÛéñ±DŒ¨cW}Á™§Ð½wø•Ø?ýñ7 yüYÑœzE»îGéjžž  ìö­'»g 6‚6a—ž{ÝΖUptáwŠ*–•è ¼•Î:åxµ^¨¾ÔBiÞ¤‘r¶RYY¹6ÛrÊh(¥µ¤Ýó|þÉ»&gK5¨W‡pFSï^§*îü^ü½ò·jVæÍJ70±Ò¯:ŽX%ˆ.áùt˵jÕPëZ-&bUV+åôeÐ5”Ô#Ÿ›HŸó3¨ °d™ð䃊;8‘ÿö‡ŸÓó¯¿+’ÊUߦ‚Èég4 ¼•zN¯¿ž];S›–Íé륢bã³-ôn/»õ.ZµÖO8L@5K$A´Ž7Ñ€ )´bÍZ*,*âYª´rI™V–A^^Mÿ4¥§¥U§a˱ÆXöÝz+ѼyæîæõÙ6[D1ZlØÇ¨ggºqI~C5ºw§Z¯½Æ¤ªvQÕž_9:‰€D@" ˆ1±$ˆb<4Ù‹H‚(:p¬ˆÐb)“ó-¡…KW(n‘à–­vÍ<:¸{eW<âÚ€òP€šYYQâjÛëÒ±½úÌ£Ú,÷ê$z‰Ž}ûöSc¶”9ó¤céÆ+. 8§Œ¢];Ñ‹O>ÐÞÎÝùôŸi´`É åì³m[ÑœEíÛ´TÊ­\³Ž.¿m°ZÇêê×­M¼üli«q'ñ¡åói[ðàMII&XîyHe,°þy™Ýz½Æî½" oŽAÛ¶ª¾jízZÈã\´l°,À¯#“GÖ“R4»ú€ñäÿ§Ö·2f+eÜÀÄJ¿ê@8b• :á¨CiäÐAjÕŠŠ=ôäø‰ìNj.µeRàОÒçßL³¥ì·*«•rú2BP¬©©ßÿL|þ²žágõ¶„ª[Ûoiгp.¸®Ôg„¡O§ŸQý¸¬TÃJ=§×ߨî¢ãŽ8„vñ;äýÏ¿¦ÿ±…Þ‘5ÙbñúË.PÎ"R2ø?H°ÚòmL¹Õ÷ê5‚ó²šß‘p÷Ϭ9ül¯W‰?œ!$çÍùµš––ή_G&„5¿ó£—-Æ vIW³;^¶ä;P }:ð®wS&rK‚(Sf‚} zv¦ —äOæM¨û6¤ZS¦P Þ P‚$ˆªÃ,Ë1J$‰@ÌQÌ ®RI‚(úéìÔ®5=Â.Úàz1’åçÝߢ™óšV·¢ÄÕV†Ë´ÉžR¬j´ùù…l ‘pžŠö>âFòÇ=v?ÆÊ}«®/ೊD°3W7^y‘¨p-+/'X ÁKoÉ÷ÎGSé¹×Þ (o'ѸA=e.ìÜÁN5µ,ÎÂ9R"X³•2hÏiL¬ö+Æb• ‚‚ÿSv½˜™‘!ª]õ}÷óAùfVeµRN_ƬO£ü)|^ѳçéÛ´BÎ8ýŒF"Æhµž“ë”ÇhÜkB|‡).)U\¿&³Xmå"S[®ºÄ½@mܼ…~úc:͘=—`å‰Ï1¶-'œ$ !÷Wb Þ˜0æ‘a¼¡¡ûÉ$z®½–h)»ü rqÕ—óz:„Ü’ ryòB`ïrÏÎ5ïÂ@%uìHµÞ ´TwNho¶$ "o΋”J" H¢’ZÌ8ï…Ÿh$˜±f•îñ­™©S&SQ!ï”Á6™ì¢mЭ}èœSO°Ugj<ùü«´tÅêõ¬*qµ€¸zeô#çùhï‹øïÿ§X3Ôª´j2#ˆr²³hè€[èÄ£UM¯ qF°»¼o~ú]-cg pyÙy½èæ«/¡Œtë.tžzá5úèß©}FIfW×3AÕç’óCiÚ¶A¼½ôöÜ÷·ÚlKÊw«¸8‰Õ~Å€¬D(ûu—ÓÕ+ª]}výïû_‚òÍ2¬Êj¥œ¾Ì3/½©œ×…3³Bã_Ÿl¨üÖ·i… B_N>£‘Ê`µž“ëï¶k/åsªÎ·zïϳhÐã qW U£HvN.}éʈó²2è†3ŽˆéèwìÚEoNù˜~üó/ª(¯`‹Tù3¦PÙY2o|Ûüy|d<º—}Vwø7' H4w.ñÏÄD#Œr_D.Nkì]ìÙ¹¦]CÿþØËnåòƧ$ÞÌW‚$ˆªÓl˱J$‰€ëH‚Èuˆ«d’ rvZO<ê0:¿×É„36ÒRS /--SÜm}ËäÉ÷¿N7,£Ï<¨Ûô¨Ùp·6ù¿+3õ†.ÒªyêÛçrÅ“î­^·>ýêå|¸£ƒË+„ o¾§XéË‹ôEgŸF8g¦Yã†Aîéà nÚoqýOiý&vA¢ ‘Œ}Üyó5t`§öŠë)MsjäÌ?³æ)–;¿þõ¯šm¤[]vA/:¼g·€3hD»{÷ìFð—?gЧ_Ocë‡à³T¬ŒÙJÑ'®Nab·ß“Ž9œFÜç?Sêá§ÙZjšßZJ+#,»®ì}6|=;ví¦¾C±åbΪ¬VÊÁuÜøÃqqÆÔù}úQZZ*ÝÅç&Á]a ¯wÌ1ÎOy÷“/éKvƒf¬ôkVשg4RìÖsbý„½ ×)„s˜Ú¶jn ¬Rà.òÛŸWÜbª†™ñ&ˆ=ô-Y¹šÉ¡ÈÎz«†SæÊA]vÞY|^oWÚ—J,!0ˆÝÉþaÝØR›±*FÁ/ "'" ö.öìLÓ.ÉŸvôÑ 9䌉Պ$ˆk¾¤´‰€D@"àq¤‹9OGÅ›»!Ÿv—V(Òýøå´eãJšXb¥³ÕK«fMR£»ÜR~hòy;wå+îxp–‰Ý•©e¼k{w¾=K/¸ckÞ¤5mÔ€ŠØ,—@¬ˆ€ó|j׬©X Bo%àü¥v­[(„ê­Ûv(–P{q±Iˆf À±U‹¦Ô¨~=ÅÅÜÎü|ÚÂ}nÐQ&]GœÌ®|€[íZy”—“Ãø•жí;•¾KËʶkeÌVÊu-&vûÍãu”ÁÖr 9ó qè|è7j-ù9Àúƒ•Ö¬åB­³­Êj¥œG~AŸæŸÃúuëP;&,jÖÌUÎ[´teÀ}3Ùo¥ßPõxF#•!ÒzÑ®?àQ§vMjÓ¢¡­$&Ž6mÙªË;vZ{…´*Þkи ØëehÍêÕ¢‹ë³aŽ|îEúeúßÒR=fˆ›wsÇq( éw›y!yG" žžhÒ$¿»¹Xômü’ Šd“ú°7©élä‡[¹´>}(»¿ß-·7;)$A;¬eO‰€D@"P ðA¤Ý…mþ}!”ËØ•®?sÄNÛ‰P¾ûÍü÷'ñ’Øõÿos(ûMw`K‚È&”²¸D@" Hâ„@¼¢õ›6Ó=ޤí;vš~G‰$Õ²[©Ú¶¡gù¢ªþ½¸ZNp¢ zÚ4¢ØÊ=ÜÍYTðK‚È…Eh{zv¦IäÏa²¿ÆÈ‘”vÊ)ÎȘ ­H‚(A'NŠ-H$ÞDÀK˜nÒ°b5a­u6™VÁ¡çux?~WÕ Ì¥Q€U ¬<`íað‹FÇU°µ vcWìÙTMDAÈ ‰€D@" xxDÏœC<;ž]Ëù-î< P5 ¤P£ hâ3OP +eÄ¥K‰†±ûÖµk‰w¥Å]Cl(ø%Adˆ`ä™6°¼k:,:¿Ã÷´jEyL%wèà¢à‰Ñ´$ˆcž¤”‰€D@" x‰ JMM¡]:G„ÜŒÙóLw§Öþ?{÷'7q6~ü¹Þ\qoànÀ†ÐÌK ”’ð’B‡„@¡÷Þ{¯¡†„ H ðæO )@( ¸žîö¹ÜùÜîî?Î’µ:ííjWÚ¦ßð1«ÕªÌ|GÒi÷ÑÌôéìŠ&ÓÖIe(Ç+é¸0‹—.ó}"´WÏ2jÄpk¼Œ Ùj]·NfΞ+­¦[(oš¶´Y–¬éœÿÖë¯Jãô©ÞEx € 0jÜùÂÞûX9Ù~›Árà®Ûæ$WOþîòËß?+íÅÐB '"ùßIÓuç/î¾UêëêòŸr€€ 4›®‹~Xä©§:»œ ùGõ¬æ…QVÚ‰+´O\¹Þ…™ªÕQ¾û]©?ùd)ëÙ³ ˜ÿ, Ê@ˆG€¨— Ñ1AJ5¥ 1Ljª«¿ÕŒ93sŽ€hnS«Ìmêl±4{ú4yóõWm›…@@ 7“vÙM&µ³ÉÛ”=Ì¿\¤§þôgyô7O'}€'y`‰UUUòð­×›–D?àùøË_D®¹¦pZeð?¢¢ ìCÚs8› ;ÿ¦”žW\!5‡vŽ%N&‹+ˆŠ¿) €@  * ÊÈ"+¹-6­‡¦›VDšeQq¬Š €@ĻッŒ×ÙͦõÐDÓŠ(é¿"]«l2]Ö’ C ¢²Rî¿á*Ùfø°Âȹ@À-°Èt~ûí"o¼‘ß±‰2üŸ‘»23œÎÐ>ý…¿Zˆùo0¡ûì#=Î=WÊçæïvø Ñm‘Qt¶l@ †-­­2uF£´-葉‹¹x´ Z²l¹ojs™´ ZkZÍJÒ‚hUëFùpájk-ÍkäùßþÊwßÌD@ü rä÷¤¡Gg—4'4YzÕ׿$C›Ì½åϹX.^l†)ÐñEr"Q8;©4¢gºWêêrs NÉÉIÑèwÒwßùÉOò“å,~à'@”e•eaŸåžÃY=¤üë°Àú¯çƒJå®»JY ÷‚’ <¢lôX@"H‘¾ÕD¹ ­ßÔ.oÏm²´éAC¶@b!pä‰';åÌe€Hw:cö9벫eýZ9•§ ‹sû ãäæK/R—3O¼ì6 ‹/ùç?E̸¨¦¯Ê(ö¸Í,÷A€(‘3л,íí+Š…CÈ¥g¨ÍŒW½çžÒ󦛢ÈeIm“QIU'…AÈ·]Ìå»ÂÙ®»˜Ó\ÿgîJY·©³å™¶ Ò@ @ KÀ :óð}rž¹Õfú›îyP>™1KÖ¯_/íúCšù!¬Üä¤Í´,Ò`EG{‡™o¦7?)ݾi“YÄ Ê]^a~n·Z •›÷º^‡Y§Ãl£Ì¬§Ißë|}âZç[I—µ§;çÄþÿêyþi'˾{î{ ŠH ±QD,ÿàƒh» ázA€(Ãã*û ÷Îj!äßêNnç¥á¢‹¤bôèpòUâ[!@TâLñ@r+Qo½õð’~Z2U€hÔˆáR]]èàj]·^fΞ+­úÔžOúpÁjYµ®ó‰àWþòœ,Y¸Àg)f!€ €@¾*ûÜ9°õðþ}䈽wÊKV4p³~Ù3o¾¹©1÷de²¢i¥Õ²¨Átw¶rM³¬ZµJ´ 4'gñ’%²ný©«­• f½æ½~VUQ)«Ö¬–•«×HC}½pÒi "Õ×ÖI“ÙF«Y¯¶¦Ú 0-_Ñ$5ÕÕÖººíèÎzJ[ƒLfÚ 2™¼µ›n­48¥ùloÓ@• >i0ÊÌ×åÊ]* ´h°J»Í³Ueºìæ@•nS× áC³©Ð’æÿs·“.>7´m²!r*𷿉Ür‹HSg¡ï;„s–Qµ‚{{ w• Ë`=Ô`rR¾ÕVÒpþùRsÀáæ«Ä·F€¨Ä+˜â!€äV DZòL»¼è®o{ë Tóe½”“þ‘ìÇ-z™þx8™I̽ÉÒ´¥Í²dÍzëãÙÓ§É›¯¿’lQæ#€ €@&í²›LÜyWkÏ“·){˜¥”tœ£ÊÍ­Ž6™VGú¾¶¦Æ•6š@Ñz©0 *­4A¥MÍgµÕæu“,3Á© Ó©²Ê|¶jµ¬5crêz:þbÓÊUVÀ§ºªJ–._.--k͘=uÖ¶?[¸Èl³Bô³ææY¾²É Té~W¯Ycö»IêMàªÙ´šÒmV™‡sªÌò+Ì6íõ6š|v”ô«3˜¤œöŒ2A'§•Ù¦uÿf‚i憬³•ªô?ó^XV0Ê TY7|Ÿé–õsSÆ2³þ¡ûï'§÷ýRªzÊG}híÙgEþô'‘Ù³ÃkQ”áüÞ* @äIã}Höiì)šE2È¿þ*Ñ`þ.¬3-…j¿ùM©ùÆ7¤Ìü! @Ì‹¥@èV ÐDÝf– J`± M7A"Mˆ ªjÈ  €–Àî{ï+#Ç·¦Øu[™¸Í`d"Ðà”¶pÒ´qãFÑ P½ùÁoƒ™n6&m1UkZN5­^mµzÒ–Qm&¤*ÿTWUËjÓUïªÕÍÖ´v·§Á)Ýn¶Œ2(m)Uc‚XÚ™Þ‚Å‹L@IÌ6«MK«õ²Ø,[·ù³U&PÕ²¶U¶êÓ[¶>\øÒe×vИ ÒÐéK/‰Üpƒ˜*»î$3ø?"¢d2Iæ‡hŸdÑÎÎ ÿúÈf‡¹n÷¸ôR©9ø`}:6Ú<–ðÖ •påR4@Ü  ʽy©ìqý¦vy{ng7:þŽCDB@Âp?tâA“¥W}mádŽœ„" ¿QjkqMî@Uçþ@‰ üò—Á¢éÓ; äGû ˦ÁhˆLP·s„Ö4Vˆë"!»ç…1@¬GÌEºc©=è ©;öؼd¹ÔvJ€¨Ôj”ò €äU€Q^ù‹~çŒCTôUH@JTÀ=þ†4@DBJN`íZ‘)SDž|Räí·;»žKõ~ªÏ3@¢Qšhا¹çpK3ÿÚ6¨¯iYºæ _°‚B•“&IYCC8y`+B€ˆƒ@ZLÿèSg4ZÝ\„¸Y6Æ!ŠIESL@¢(õñ‡Š®BÈ0D/0¾ÈWˆh‹"³È/¥ù¿ßªÝÍ#@ÔÎæÏ"²OcÏá,’"ÿÚ˜S3àT/=®»N*LWŸ¤ð…oÊ@b,@€(Æ•BÑÝã-Y´P^ù³8–„ €yØ÷k‡ÉÀÁC¬|0þPÞ«ƒ €@.EÞzKä…:ƒEfÌ/Ñ÷SüÀŸM ¥Ð‹Ð>ÅžÃù¸›üW™.äÌxBͦ¹šC‘êÉ“¥bôèpöËV|ù²0@̲ •™›!»ßóÌrþZ÷þÖ³;¾Ï&Ï©¶­ƒç#µëHÅyHŒC”tv‰ €@Œ?”‹ €@i h`hñb‘nè µ·GV^DÝÐv\éf­Âù(Iþµ¹jjÛ}wéqÉ%R1x°HEEá仄sB€¨„+—¢!€ä^ ›Q¹¹êÓ»§¹*¬› ö¶vYÞ´ÒSƒC½z6HuuµïçÝÍÔÐÏšæµÒš¤»õè¿UŸÎfåÝm(äÏ4_K—7™âò$b¢+”Í!€ ¥€»{9ÆÊ“Õ@ 4ZZDž~Zä7D´…‘¾ï|ú/”ò J˜§ï¨Ir|¶+ÿæ·„jóo]R1j”Ôì½·Ô~ç;Œ-\5ë5eMÈ@Ø"Ðbõœ:svFcUUUÊvãÆHMÁ–-9jÃÆòß)Ÿún¸¢¢\Fo3Búôêåûyª™s?[ ‹—.÷]¬ÒxìŒYcÕGb×︣ԙ€PÕ¤IR®­…*+ ¿ %šCD%Z± @ ?Ùˆ¶;Fjk‚·Æ‰²´6˜ÑÇÉD£¶!}{g šoDË’ˆ*+dçIÛGY´¤ÛÎg€ÈÝÍœfð•¿<'K.HšW>@@hè^.Z_¶Ž%$`˜”gžùãEššDZ[3  òEÒ€þk7-…Êë륬o_©=üp©=òH)3ïI…!@€¨0ê\ €”ˆ¢`9—‘/˜»›¹ÙÓ§É›¯¿â»3@@ Zw÷rÛo3XÜuÛhwÈÖ@RЮç¦OyóM‘÷ßY¸°3X¤ÁŽD®ƒ …•kÉ‚˜´»Ž«3-…V *•;ï,Õf\¡Ê ¤bôè‚È#™H @”èÁ;@² @Œ‘¿—»›¹–æ5òüoå¿ s@@ RCŽüž4ôèiíãšh‚D$@ÚÝœ9tìÙ?üAä¹çDfÏî6HD€h³o‡ÊM–58Ô>r¤Ô~ãRû­oIYm­iBdÚ™`©pnÝ3@" @¬Òº Åt "¤›¹`ÇK#€ …€†4@d§3ßÇžä@ Õ«Eþñ‘wÞ™5KdÑ"‘eË: 1ÿšÚÚ¤­ˆ$Ùp$]·ÀʯÝÅiHÇÚ¨yëß_*† ‘rÓ2¨úóŸ—ê/~QÊz÷NZ>(LD…Y/ä @ H«8Dɽ¦-m–%kÖ[ ÐÍ\r'>A@ *}¿v˜ <ÄÚüäíFÊæ @ dmY¤#³èÃE^{M:Þ}W:6n”µ¦õÑ&ˆÐ±KRæJÒ5æßêŠ ©ÜuW©Ùg©ÜqG)7c i@Èj)» * *º¤$ € Ðbáœ:£QÚÌÓNAS•i1³ÝØ1RSStÕH—ß°a£ü÷ãO}÷QQQ.£¶!}{÷òý<ÕÌîDê±ÓÄíRm"’Ïßù`Š´kWyL´"Ê#>»Fˆ½€·õЉM–^õ¦« €@N:–.•/¿,?úH6üç?ÒnF²aƒéna½”›àI‡þÛœû5'ËÅNrÒVAšôµÌÚÌ¿²š‘êj)«ª’êÝv“ÊI“¤ú€¤|ÀkYþWZˆJ«>)  €@ž² éÍX­¹+/·oÑò\˜Í»ooïV}ªË'™,›ûÆj©4Oe’4ø´qÓ&ßUÕ£¾.??Ä´¬mõÍS®g~¸`µ¬Zg¾™D+¢\ë³?@8 ¸[moÆ:ÐŒ?DBÈŸ@Ç’%²iñbéøì3i[°@Ú¦O—¶¹s¥Í’ú¬\)Ì~ú˜¦¶6Ò®éòû¸_N‡ìq‚´E~ƒ¯6c­ìÓÇ ü”o½µT'åC‡Jù°aR1x°”˜EAXµXKM‘O@¢Ȧ‹¹¢( ™Ì©€»QKóyëõWeÉÂ9Í;C@ nÞÖCG쵓 Ð'n ”(lÍ=>hK¢²5kdÓ'ŸÈÆ©S¥ÍŒk¤Á#m¤-‹Šª¥QˆÁ!û±S»e‡ÚtÌ ñã¥ÊtW5a‚Tl¿½”õìiµ²*ÛŒHñ @¿:§Ä €  Š7¦›¦QL+žb#€äM`÷½÷•‘ãÆ[û§õPÞª#€Y t,_.m3fH›i}Ô¾p¡´56JûÚµÒÑÒ"í:î‘鲮ݴ>Ó«Eµé5C[ÕØ-ÚM°ÆîºÎ 4mÎNëg¡§ÍÛ,7­{‚;›w¤¡íeC“ì1Ëo¨«©¬”rÓ H»„+ïÕKʤ¬¾^*F–ò!C¬ÖAÚ2¨¬_?k]þ‡€W€‘W„÷ €d!@€( ÏDÀÝŠhÉ¢…òÊŸŸÍd3¬ƒ €)Üc ïßGŽØ{§kð1 €¦û¶yó¤#ÉØ½~>e&¨S1b„ßGÌC §ˆrÊÍÎ@J]€Q©×p~ÊçnE¤9бˆ§OÍOfØ+ €%*0jÜùÂÞû8¥cì!‡‚ @(QD%Z± @ ?ˆòㇽÎmj•¹Mk­¢¶4¯‘çû«8›2"€ 3CŽüžhsš&o7Rö0ÿH € €@)  *åÚ¥l €9h1ýOÑ(m¦à¨Suu•Ti?Öֶš>œ7÷¡ìÍZee…Ô˜Á3sÚM?Ñ­ëL?Ó>IÇù¬©®1]KëP‡´S@IDATŸÁÓzÓwõ¦MÑ×·¶"Ò®æÖmÞ×ìéÓäÍ×_ žaÖ@@.»ï½¯ŒgÆtØœÎ<|{’W@@ d•lÕR0@|ä*@Tn¤1t° ìß/ÅìvŸïOùD6n4ƒ}zRyy™ôëÛGFŽîù$ú·­¦/è>î»# ¹ôêÙÃ÷óT3çΗe+šR-Êç‹×¬—éK›­mi+"íjnÉ¡l› € WC†Ê¾êŸ®å &@@ Ä•xS<@Ü äª‹9  7¢AE ÚʈFå#@dZ5}4µøDz4k+¢Uë6Z6]Íåöüfo €¥)°ïדƒ‡X…Þ¿±÷N¥YPJ… €xy@x‹ €@6ˆDºkAD€(›£«s]íjîí¹[Z,ÑÕ\ö¦l@ ¾îà*œxÐdéU__JŽ €± @«ê¦° €Q  "@õ1¦Ûww5§ïµ«¹ÆéSu’„ €i е\šP,† €%+@€¨d«–‚!€äC€¢\wÓÌXDK̘Dš(Wêì@ T¼Á!º–+•𥠀 D€Q-–E@ …" }*7vŽ‘ãæ*//º˜s‹d7­]ÍM[ÒÌxDÙ1²6 €@LÜ]ËŠéA@±@@@q € ¢@.D#†–ýû…˜ûp6Õ]€¨_ß¾2rİpv`+­ëÖËGŸNó]£¢¢\ÆŽÚFzõèáûyª™sç˲[ÆJµ|˜Ÿ{Ç#Z²h¡¼òçgÃÜÛB@ äÜÁ!-Ü{í$Ãô)¹rR @@ •¢TB|Ž €@–ÖV™:£QÚÚÚ¬|Ѳ²2©«­‘šš‘ŽàëG²FYçVW­^-ííþ™ª©®–úúºœç¹­½MV¯iö-¶Z6Ô×KUUeççþYÙ\¾o3Oƒ‚6tm1廳fzÇ#š=}š¼ùú+ì‰M"€ Pü‡Š¿) €„'@€(…›ir† €€¢¨²€ P:ˆhAä>š )@´qã&Y¿©]¦-i–Uë6:Ù¤Ë9‡‚ @"HÖ¥œ¶"8Td•Iv@@ /ˆòÂÎN@JU€.æ 4@ÔÃt17&÷cD€È´fûèÓi¢"M$šÓ´V–¬Y$ZºÐŒUôú+Î<&@(dIf¬¡‰;ïšEÆJàà  € R€QJ"@@ }DˆÜGK!ˆìü-6"o—sŒMdëðŠ P¨~c i^éR®PkŒ|!€ €@!  *äÚ!o €E'‡Ñò¦•2sö\)++ëR?UU•2iÂx©¬¬èòYÔ3>1KÖ4·tÙæ³WÏ?:÷-ˆ43o½÷¯•Ž#µÍð¡Ò«¾]òæ ëCW "÷¶ýºœÓÏ—,Z(SÞý,Y¸À½8Ó €äMÀ¯;9ÍŒ¶:p·m¥W}mÞòÆŽ@@b @T¬5G¾@ R W" zô4ݦ5ÔÕI‡G¶Iõ¹½º½œw}ûsûuíºu²jõûm«=4àQa^ído×~ïÝ~Ÿë6—¯\)6l[ÇÞŸ¾ÖTWËV}z»g9Óöþ½ùrðL$[Þžo/®ÛÓy —,¿mw®zH}mçZ~ëë¶¼óuž&{›©>oïh—¥Ë›¤½½½sEŸÿkk¢yM­²nS›ó©Ýí\ãô©Š&@r-ÐУ§hwr#ÇOص„&›±†&n38a>o@@Ò @”¾K"€ R W¢”a hk" Í5ã¹"·Ó €¹ÐC£ÆMèÒýoo‚Bîºm®²Â~@@’ @T²UKÁ@ò!@€(êì3LdÝÎé>fOŸ&´( S›m!€xº ÑœW‹÷ € €@vˆ²ócm@ZZ[eêŒFÑq_H³@w"ZsÍ’w@ ð´¹Qã;[ é´7òŠð@G€Q8Žl@K€B© l4ƒÍ]µQ®Xí[´%‹Ê”wÿÃ8E¾:ÌDèNÀêBΆ⻘v%·‡gHÇ"!€ €„/@€(|S¶ˆ cº˜‹qå—hÑ+**dÂØQÒÖQ&ÿúd¶|@JB ¥µU¦Îh”¶¶¶’(…@€Ç € € €¥)@€¨4ë•R!€äI€QžàÙmdˆ"£eà € € €äU€Q^ùÙ9 €@© 䪋¹òòr2p€ôߪoÂéH˜W&eÖ{ï|{!ûsû½w9ïç+V®’¹Ÿ-²²ÎíÚëékee…Œ=Êzµç{×Oµ}ïçövZֶʌƹf¿öœ-¯ÄµõpkœïúÙî¿»õÕ`îg ¥É˜”j"@Tª5K¹@@@â.@€(îGåGU —¢CËÀþýBÍ:[¶¢IfÍ™ç ªªª”‰ÆIUee:› ´ÌšæùdúLßýj`jܨ‘Ò£¡>Ð6ÃX¸qî|Q“RMˆJµf) € € €@ÜÅý ü €¡ ä2@4܈ ²êQ¨‡qÂÆ¬Ñ˜‘ÒPŸûà[BFxƒ € € €¡  •“!€Ä]€-ˆJí @Tj5Jy@@@è @Ä‘€ €@ˆˆ…x8Ħèb® ªL € € € º¢ÐIÙ  €@œ *µãŸQ©Õ(åA@@@ S€G €! ´´¶ÊÔÒÖÖâV»nª¼¼\F˜1ˆ2‘…cA4z¤ôÈÃ89sç˲M]+©Dæ *‘Ф € € €xy@x‹ €@6¹ •Iÿ~[IßÞ½D:²ÉqðuW­i–…‹—HYYY—•5˜0jÄ0Ñ×°ÓÚÖu2oÁBßÍVT”˰!ƒ¥®¦Æ÷ó¨fªÁc±Ú˜”j"@Tª5K¹@@@â.@€(îGåGU W]ÌÙ™îèÈqtÈìØ/0dçG_#Ë“ Æt ImÙs'EáylÉaqN *Îz#× € € €¤ @”JˆÏ@ ëQ€¬±(  ʈ•@@@(xD_Ed@ ˜Sm‘×t¬Ñ˜‘Ò‡ñÒÉË € € € €@fˆ2sc-@|ù²0³ˆqå‘u@@@º @Ô !€ T€QP1–/tD…^Cä@@@ÌeæÆZ €ø  òeaf  *âÊ#ë € € €t#@€¨>B@ ¨@Kk«LÑ(mmmAWõ]¾££Ãw>3ƒ ”••_‰5Ä % uuh € € € €@  *¡Ê¤( €ù3@TYY!cGn#‰²¯×õ6ÈìyŸ%ßPÛ&1ÐÉ?ê“ò ‘òò¨¶Êv …ÂÈF@@@(8DW%d@ ˜Âìb®ªªRvš¸]1sLÞ×¶®“)S§ûçGCV€¨Ýÿó(çVTš‘ p"@TÀ•CÖ@@@ÈB€Qx¬Š €€W€‘W¤0Þ ʼenÇš € € €²¢B®ò† Ptˆ ³ÊZM ¢hA”QåX¢1#¥¡¾>£õY @@@ S€QaÖ ¹B(RD…Yqˆ2¯D™Û±& € € €…,@€¨k‡¼!€¢Â¬2D™× ¢ÌíX@@@B @TȵCÞ@ŠN€QaV¢Ìë…Qæv¬‰ € € €@!  *äÚ!o €E'f€¨²ªR&+í–C™”Y¯ÒùÞ‹õçÞýyßG½ÿl¶¿nýz™6s¶7Ë[Þ·mÙì¼ef¦Ê+Dô_'+@4v”4ÔÕp.É € € € @TŒå@èF ÌQ7»á#r&@€(gÔì@@@œ  Ê)7;C(u–ÖV™:£QÚÚÚJ½¨”/&t1“Ц˜ € € €± @»*§À €Q  ŠR—mçC€Q>ÔÙ' € € €Ñ  ŠÞ˜= €ÄH€.æbTÙ1)*]ÌŤ¢)& € € €@ìÅ®Ê)0 €@”ˆ¢ÔeÛù @”uö‰ € € €@ôˆ¢7f €1 @£ÊŽIQéb.&M1@@@b'@€(vUN@¢(ÖQGGG”,o»¬¬,éºVŽó”ïîò•4ÃEú¢"­8² € € €@ D)€ø@ Å *//—ý·’Úšš E|Ùò²r™9g®øct^ßÞ½¤wÏžÒaþËejYÛ*K–-÷ÍW.ó‘«}ÑÅ\®¤Ù € € €¹ @”[oö† PâÅ ÒÀØQ[K¯= ®vÞ|÷)/ïÚŠHƒZÇ ’Aúç<ÏË›VÊÌÙþ«œg&;$@”dv € € €@å]"€”®@Kk«LÑ(mmmESHDÁªŠQ0/–F@@@ @T˜õB®@ŠT€Q¸G ¢p=3Ù-ˆ2Qc@@@ _€Qá×9D("º˜ ·²…ë™ÉÖe¢Æ: € € €¾¢Â¯#rˆ PDˆÂ­,Dázf²5D™¨± € € €…/@€¨ðëˆ"€‘¢p+‹Q¸ž™lÍ ) õõ™¬Î: € € € P ˆ ´bÈ €@q k€hüè‘Ò£¡ð݈F ,û÷ËùÒ´r•Loœ#eee9ßw>vH€(êì@@@èEoÌ@b$PŒ"­žŽŽó¯Ð*ªCÊËË“fª3ÏšéÜj4.—àâ Jzò € € €E-@€¨¨«Ì#€š@±ˆ Í‘üŽ¢Â© r‚ € € €@˜ˆÂÔd[ €±him•©3¥­­-ö”†€ ;JêêJ£@”@@@,D €„(@€(DL6Uˆ ¢È € € €¡  ” "€ÄY€.æâ\û¥YvD¥Y¯” @@@D €„(@€(DL6Uˆ ¢È € € €¡  ” "€ÄY€Qœk¿4ËnˆÆŒ”†úúÒ, ¥B@@@ ¦ˆbZñ@ DѸ²Õü  ÊŸ={F@@@ JDQê²m@Ø  Š]•—| •|S@@@@˜  ŠiÅSl@hEãÊVó'@€(öì@@@(E©Ë¶@b'@€(vU^ò¶DcGIC]]É—•"€ € € 'DqªmÊŠ ¹¢È‰ÙAŽåœÝ!€ € € #D9‚f7 €ñhim•©3¥­­-¦”%/@€¨ä«˜"€ € €ÄT€QL+žb#€D#@€(W¶š?Dù³gÏ € € €D)@€(J]¶ ;º˜‹]•—|­Ñ˜‘ÒP__òe¥€ € € €ÄI€Qœj›²"€D.@€(rbvcD9gw € € €äH€QŽ Ù  €@<Å£žãTJDqªmÊŠ € € 'DqªmÊŠ ¹¢È‰ÙAŽåœÝ!€ € € #D9‚f7 €ñ @zŽS) Å©¶)+ € € €@œÅ©¶)+ €@äˆ"'f9 @”cpv‡ € € €@ŽåšÝ €ÄC`më:™:c–ljk‹G)eÉ TVTÈ„±£¥¾®¶äËJ@@@ˆ“¢8Õ6eEˆ\ ½½]>øxªlÜ´)ò}±r!PUY);n?AÊËËs±;ö € € €9 @”#hvƒ O¦Ï”æ–µñ)0%-i õ²Ý¸1%]F ‡ € € €@űÖ)3 €@¤ /‘Ï.Žtl\ 2H†˜«Ý±@@@È‘¢A³@ø´®['O›)ÚÝ bÐnå´õãs-’w@@@üù»0@¬f6ΑUÍ-ÒÖÖ–ÕvX| TTTHï 2fÔ6ùÊûE@@@EˆË¦@â+ÐÚjZ™±ˆhEßc ØK®­‡¶7­‡êêj‹½(ä@@@|ù 0 @0t,¢EK–ÑŠ( L¶‘Sm=4d`ÂØC9ugg € € €äR€Q.µÙ €@¬´{¹9óHÓªÕ´$ŠUÍwaµåÐV}zÉÖÆŠŠH € € € PšˆJ³^) €@¬[¿^>[´XV4­*‘ ºتoo>d°ÔTWw¿ Ÿ"€ € € €@Q  *êê#ó €Å °nýYhº›[Þ´R:::Š!Ëä1†eeeÒ¯o«[¹Ú‚C1<(2 € € €@ÌŬÂ). €@î4(´iÓ&Y¾råæ1‰Úér.÷ÕÀ“h—rå2ØŒ9´UŸ>RUY),"!€ € € €@i  *íú¥t €"`·Ò.ç–,[nZ­²~„ooo·ZÙŸHvÉF hðGÿi`H»~¦K¹ýû‹ÝjˆàP W>EC@@@À%@€È…Á$ €¹ÐE«×4ËÊÕkdíÚVY¿aƒ´Óý\®øc»ŸrÒ±…ê¥wÏÒËü«4-†H € € € ?Dñ«sJŒ € € € € €1 @ó€â#€ € € € € €@üůÎ)1 € € € € €Ä\€QÌŠ € € € € €ñ @¿:§Ä € € € € € sD1?(> € € € € €ÄO€Qüêœ#€ € € € € €@ÌÅü ø € € € € € ?Dñ«sJŒ € € € € €1 @ó€â#€ € € € € €@üůÎ)1 € € € € €Ä\€QÌŠ € € € € €ñ @¿:§Ä € € € € € sD1?(> € € € € €ÄO€Qüêœ#€ € € € € €@ÌÅü ø € € € € € ?Dñ«sJŒ € € € € €1 @ó€â#€ € € € € €@üůÎ)1 € € € € €Ä\€QÌŠ € € € € €ñ @¿:§Ä € € € € € sD1?(> € € € € €ÄO€Qüêœ#€ € € € € €@ÌÅü ø € € € € € ?Dñ«sJŒ € € € € €1 @ó€â#€ € € € € €@üůÎ)1 € € € € €Ä\€QÌŠ € € € € €ñ @¿:§Ä € € € € € sD1?(> € € € € €ÄO€Qüêœ#€ € € € € €@ÌÅü ø € € € € € ?Dñ«sJŒ € € € € €1 @ó€â#€ € € € € €@üůÎ)1 € € € € €Ä\€QÌŠ € € € € €ñ @¿:§Ä € € € € € sD1?(> € € € € €ÄO€Qüêœ#€ € € € € €@ÌÅü ø € €ñX½zµ<õô3ÒÜÜ,ÕÕÕrÈ×¾&[o="¾ ”@4øš&‹!…çYx¶*uY`âÊ"“ € €ÄIà“O>•«®¾Æ)òÑG}_>ø«Î{&@ð ¬[·NÖ¶¶J}]ÔÖÖz?ŽÍ{þ†Æ¦ªs^Pα-äœg[,Š}Šº,Ü$@T¸uSt9»ÿeÆŒ™&ß²í„ òƒœ”vV45É]wÝ#«V­’ªªJ9ôCd¯½¾˜öú,ˆ«À‡~$/¼ø¢lذ±KÊËʤ¦¶FúmÕÏz¢ÿóŸßMzöìÙe9f €@îJíœå‹ZúÇN©Õ}ú%gIˆ»ÀGS¦È;ï¼+|ð¡,[¶ÌÜ·npHªªªdÀ€þ2iÒ$Ñ{ÕI':Ÿ•úD¡ÿ 2åcyá…eÝúõb¾VÈÀäðÿ)ýúõK«jzèa™3w®Y·\¾´÷^òå/ïŸÖz,\€s,¹Y¡Ÿgï¾÷ž¼ôÒ˲iS›o!ªÍ5rÈÐ!²ÍÖ[Ë6Ûl-Æ “ÊÊJßeK}f¡×e©ûwW>DÝéðY “tªhsAM}ûö•ûî½;íõgΜ)—\z¹³üþûï''x‚óž @ TÎ;ÿ™7o~ZÅÓÉÉ“w—c9FzôhHkBpJíœå‹ZúÇG©Õ}ú%gIˆ«ÀÚµkåg&Hðæ›o¥M°· $ܱ±hYTèCýþn 2Dn¸þZ«[ÙT•zÜñ'Êz\Ò´ë.»È9眕j>(À9–¬Ðϳ³Ï9W,X˜º ›—¨¨¨=ö˜,Ç}Tìþ,ôºL»KpAD%X©ù*¢|ɳ_(fŸœ~†õ$f2 DBÜ ú9û‡?üQžzúäšk®’1£G;ï½|QóŠ$_èuŸ<ç|‚øûoÈüLÚÛÛVÔî‘ö˜üÈ£òÖ[o'|vÂñÇÉW¾òå„y¼Ah ýœ ú¥/jé/…^÷é—„%@äëÖ­3­OÏ’U›»×%5Øóƒ“N”}öù’6m’{ï»_þýï7>ÿÜŽ;Êœ—0¯”ÞúßP÷ß-¯û~ûíkÕ©w¾û="·FxÓœcÁ,‹é-ÿ‹_<)/¼øbÂÇW\~™L˜0>a^©¼)ô¿¡î¿[uuuÒÚÚêЗ••ÉM7^/Çwæy'yEÂyÏ9̱˜Î³î¾ÏÛ¥îìZðÓ}çr{–5&Ø-7ß$ôwæE5ô»B˜ù(ôº ³¬Å¶-DÅVcœß\ˆ´»¥%K–XƒÁ­\¹RêêeÈà!²õÖ#¬'œ‚P-_¾\Móø5Íkdø°af[‹^ØÓI«Í“U­­ë¤Áì¿GÖ*Úlôcó#îbÓŒ´Ê *¿ÍÈmd³Mo ³ ÞmóŠCÀýÅ-Õ åÞyGn½õv§`úEïÑGrÞÛÙ\—ìmè«>Ý6wî\ùì³ÒУAF aºøkosÆ ²pÑ"YhñÔ€Wž=¬kn¯^½ìEœ×JsíÔ§UÝ)Ûr­ZµJæÍ›'+W®²ÊV[[+Æ 5ÿ†¥5P¯ßþíüévg̘iþ4XFúwD¿x{“þx2sæ,ó·kÕ=àèÑ£dðàÁ¾Ëz×u¿çï‡[#·Óa³z¯ ÷/z.ÔÖÖI¯^=Ó*ˆž—ÚªP¯­¶ÚÊ9OôüÒ¤?:è?;]vé%¢Ád;ézúcŸR}QÓ{­©S§Y÷E£FÊú‹k¶×•îÎÃ¥K—ɧS?•šê #¯¶‘ýVÝÛÛó+Kº÷ö6ì×l]ííØ¯zY°p¡ÌŸ7ßêâ´OŸÞ2ÚtUX__o/’Ök6÷ØîdsÍËf]w˜F ú„ûé?=3¡»¸ŸzŠ|ñ‹{¦]ük®½^¦L™â,¿ÝvÛÉe—^ì¼·'¸f{ogçÅ~ zíKõ7ÔÞn²×°®‘ɶïþ»¥õPSS-:þˆ´k«óÎ;Ç~Ûå5›Q&eËô^¦KÆ xF.Ï1eÈö< ûÓ<åò<Ëä8Ô<Iîó,Õ÷y{»ÍÍ-rιç‰úÚi·Ýv•³Ï:Ó~›ô5Ó: ëüÊtÿZ î®™úH¿s,[¾Ìênoü¸qæ{Ç€¤É>È&Þm†y™‹cÑ›ÿ ï ÑbÙnr Ò‹Ú_^xQþd~Ñ/ÃÞ¤?4xÀW¬æ™ÞÝËêEãñŸ?!}4Eš››ÝY?º 2X=ä¤ÍèuíkùâK.µÖÕ\î»÷nk[·Ý~§,4_ªÝI›äk3nMa•Á½}¦@ 8‚ÜPê ¾^kí¤×Gþ™h ÈN™^—ìõõÉÂ_ýú7òᇙñÛ³WýaYñG}ÿ{²í¶Û:ó»›Ð˜ùä¯ä?ÿyG4@’NÚa‡ä¢ ÏwÍ´\ú%ì•W^•×ÿþwšïlÏ=¡ã8}õ«ÉáßüF‚¥{¿ýk€é©§ž1Ý©ü[šÌýî¤Oc}ô÷eGS;½ûî{–ƒ÷ïƒþàzè!_K«[þ~Øšù{ ëœýíSOËÿø'§ útöî»Áyï7±bÅ 9ã̳eãÆÖÇû￟œtâ òüŸÿ"Ošs,Ýtùe—šów‚µ¸ß5=ž}ö9yùÿþ&úeÆzšÀîá‡S:ð@÷ìn§Ãº®ø‡zþüþ÷™óPûww§Lòê^ß;VÝëvýÊ¢÷£©î!Ýy ËÕ½ÍùóçË£=.út«~)v'½¯ÖõíûS$Œ{l{¿Ù\ó²Y×Þ?¯ÄM@¯§O?ó;§Ø:FÍÝwÝhl ífîλîv¶¡wÝy{Â}Ù\ú·sg0ÓkŸßßЃþª{Ó]¦Ã¼FvÙ¸g†ûïÖ„ äØc–‹.º$a©‹/¾P&Mœ˜0Ï~4@”MÙ²¹—±ó[ ¯¹:ÇÔ"Óó,ŠsLó“«ó,›ãPó4¹Ï³tDº¿þõ%ë7I÷þ®¹úJó`×÷,k:Û:ÉöüÊvÿvü®™˜ßoõ{Ç_^xÁt×8–]ß¾}eWÓzõ˜cŽNxÀÍÞžýVþìí…u™ëcÑÎ&¯ˆ2Qc_\ˆô$½ô²+¬'Ú}3ášyÞ¹çÈÎ;ïäš³eR|¿ï¾¢õ[>MœÒ§¥N<áxótomâæ÷âvÎÙgÉ}÷?ðÄ•½ÒÑG}_ôf1¬2ØÛåŠ[ È ¥>Yþý£ŽI(ðc>œp}Êäºdop†is÷=÷Z­3íyÉ^58uˆ jüïßîöGƒ7ß|Ëê“ÞþA;Ùö¼ó‡"·Þr³3;“r5Ξmu˧µtR_ó#Èõ×_+½{÷wÿ]x<òèc¾A4{e ÖØz-è.…y]ñæUÔÒ²/0­»Kéæµ»mègaÕ½nË[–tî!u=;…éªÛÔ¿/ùË ¢K}‚³»¤¢ÓN;Õ¨Þ»\X÷غÝl®yÙ¬ë-À-·Ü&ï¼û®Sdý›pÄ·¿å¼OgB¯!§þø'VkW{ùK.¾H&NÜÞ~›ñ50Ì{;ÍL¶×>ïµÜþÎïÔ3æ5Ò³iß·î¿[ãÇ“+¯¸Üº×ÿç?ÿå,?räH¹îÚ«}ï?ƒˆ²-[6÷2NaŠ`"Wç˜RxÏtî5Â>Ç4¹<ϲ=5¿A“û<  Òkå™gÐÕœ>ø¥€¹Su’ÍùÆþíòxɯ}í`óÐþG2gÎ\{ß×í·ßNôøu?k/fþt›aÝCæãX´M2y%@”‰ëø ä"@佨iD;ì0Izöèi=á:uÚ4çK­ûÇw†õ›o¹Õ=ËêZH»"ÑnâæÏÿ¬Ë£ú#ŠnÏ›¼7ý‘¤#ÉÓñGšp¾a~È £ Þ|ðŠW È å"Ó=Û™gméB›\ë™î”ÉuI×ï½÷åÖÛnïòĸ^×´/ä+šœë«{{ï½—œò£“ݳœimypÞù&͵K,í­]ôšímÁi¯¼ÓNŸ“óÏ;×~Ûå N:×Û_þòIù³ùÁÓ4ø3pàs­QOïþ÷Ó½p’{kÚëê]@ÿéÓöÞ¿º¿íLK«¿™8h³¶Äò šu÷p?¼êùyÖ9«O”éhú¥Y“Ó÷Ýwôñ PÚ%½èâK¬.qí÷7ÜpÕ…í &øøÄ¿°g§|½úª+eìØÎ§SÛ Ð`§ßñzî9gw;&EØ×•(óšÍ,VÝë¾¼eIçšfç1lWÝîË&À¨-‡¼I[hé1à½Vúuæ=¶æ#›k^6ëz x@œÎ=ïë)»Ì·ßv‹Õ®ý>Ý×|H^}í5gñüCùÒ—övÞgz óÞN3“íµÏ[ŽîDa_#Ìn&ܷƙºòrYºt©œuö¹ ÷õ§žò#Ùk¯/vÙRº¢0ʖͽL—ŒðŒ\cJà=>Ó¹×ûÓ|äê< ã8ÔüMîó,H€H÷ãê`ÓŠÿè£JÈBu’ÍùÆþíyI{¾ýªß9ô{²_oQÛm·­é®ô{Qç5ÌüéFø‡Ì×±è d0A€(4Vñˆ:@¤? œxÒ …þÈxå—Iÿþ[qÓ~<_zùeyõÕ×ä'?ù±Œ;6!³úãÝyç_ð¤©þ¸©-„ª««eÿþÆòÈ#Éúõë­yú‡ôFó#̈#œet"ÙÅM(Ô®ŠvÙuYjº;ùÈtc·×Þ_”QæéœlËÞ €@Ñ ¹¡ü0X»×´“_?ÅA¯Kc̘zmÔ/+î®Ï´9·¶fÙÖtG¡×G}ÂIŸÎyâç¿3gÚY°~Ô¾áúë¬n眙›'î½ï~yã8³5ørüñÇ9-Žôš}ßý÷[Á){!ífkWsíÔ›C½öÚ)“rÙ7‹z“¹ß¾ûʾûí“0œ–é¹çž—§ž~ÆÞMçô¦»PíRÅ’í_»°:ú¨£¬Öú´‘¶âøµé¢/YÒV©ßûîw¬ík+;î¼+¡[¬d­HÂø˜,OÌ&æ9ë}Šôó…P»wóKÞñˆÃÍàÒ78‹655‰vàøgÓÝœ¶±“>m7ÊŒue§ÊŠÊ„ñŽ’Ûú„Þ¦…àþ¦{\½¯Ñ.Ç´…¡vsg'í¦N»«óKQ\W¢Ê«_þýæ…Y÷ÉÊ’ìR¯Õš¢p]»v­éºð,ó´ÿ–.—õIM½?:t¨µ_­wmù¢éEƒ…ÚUÑ—o©û°ï±³¹æe³®UXþ‡@ŒŽ=îëéi%Ðû°_<ñ¸uO”äÓMÝïLwuvú–i‰ômWK¤L¯aÞÛ…qíó–#Y€(ìk¤íšêÕýwkܸ±&@t…µŠvý¬÷ vÒßV4èþMD?K'@fÙ2½—±ËQ ¯¹:ÇÔÂ{|Ú>ÝÝk„yŽéþruž…yÚN龺ϳ ¢·Þz[n¿ãNgWŸûÜŽrÁùç9ïu"¬:Éôü kÿZ–dǤºiÏ$ÚzJ¯C³­.¤5ÐâNäÖ`·;…™¿0î!óy,º]‚N *ÆòI¢é€ÞgŸ³åBi·ÈIš!Ÿ´/ýGM—@vÒ§˜ôi&¿ô÷¿¿aug¶û¾ gœqºýÖzõ»¸éS–úD­>ýîMa”Á»MÞ#€@q ¤{C9Óe.¿âª„>ß7ãbše»SÐë’®û·¿ý?yø‘GÍ 6ÌôO~le‚DÞ¤OóèÚS>þØùÈÛÚÇþ@›Ì/ZÔ9ŽÑ Aƒä–›ot‚Cö2«W¯±~˜Ô±44MÜ~{¹ä’‹ì×LÊ¥×Ü÷Í@¼“÷˜ì[{ãÞé/¿ì’.ã+ùí¿ÿ~fœ¤ dÈ!ö¦¬×ÇL·a/™îüé»&0tØ¡‡$ÌÖýõ)NwË£Ÿ?þh—/èüýH`Ëë›0ÏÙ·Þ6_ ͸…vm‚×^s•ý6áõøcB0S‡zŽ']Á»Ü5f{vp!aƒ›ßøÛúCÑçŸÛåÁ "뽘ûx}ègH=ºl:ŠëJTyí’ù$3¬{¿²twig) Wû‹µ½ýqB‹ú’7Í;Ïj­6iÒÄ„qÓ¾ÇÎæš—ͺÞòò8 ¬4ã)žrêiN‘µô÷ßë¼2á½&h7º§œ²¥Åy¦×À0ïí¸öyË‘,@äõÈöwˆtëÂýwË Òñ:~zÆYf¼gSÚe­v]ëN鈢([Ð{wž y:—ç˜:xO—ê^#ÌsL÷—«ó,ŠãPóŸNrŸgAD³gÏ‘ /ºØÙötqç‰=„„]'Aϯ0÷ïwLj7ïç_p^ƒœ ¢_^}Íu2cÆ ÇG[:j‹Gw 3º­BûÝÙ]Ö(§ E©³mG šgÍ=Ï<án§ýÌ­?8éDûmZ¯Ú5“þ§I£ÒwÜ~«èSò~I#Ǻ¼®®I—»ÏwO\síõ2eÊk–Þ j7[Þ”I¹¼ÛHö^[ê¸tvÒ.ó´u©;y÷ßÐÐ`uÓa?]ï^ÖÏ盦U©>å—®½îz«¥©ý™þmÒ€š;ñ÷í‘ßé0ÏYmŦ?ƹ»ïºíÖ›»µÄÚzÞ¼ùVáõ©î{î¾S4ãMA¿ôùÛ:FÁ°a-G¼Û¿éæ[ZýÝtãõ]IºNו¨òê-c²÷aÖ½·,ºÏîî!íòïCL©ÌÒÉ_÷ù<St÷9¢îtø,@Ô"ýaToPôÇMúãˆvUô•/ïŸV>u½cŽ=Þyêõs;š¦›&JÝ]ºëî{ä_ÿú·³ˆ÷©nïÅMûc¿Ô<ùîîÉYÙLd[÷¶˜FÒpßPj‰†nL¯ÚG¸}Ís—V»_»îÚk|o¤‚^—ôÉÁ“~°å‰ÎZ]LèÓeÝ¥Ë.¿R¦OŸî,âmî­­Óz†ó¹G4Hâ—®ºúZóæ룞={Êϼ¿ËbAËÕeÝÌðnÛ¯e†w™ï~çH9ì°C“nÕÝ}„þˆï=[~$ñ®ôä¯~-Ï?ÿggö¥—\,Ú½“;ñ÷í‘ßé°ÏÙÇBþúÒKN¡¼ÝðèóÍC2Ú ¤’µ´Óσ~é zlÿîw¿—gÌ?;]xáù²ã;Øo­×¨®+YçÕÜ÷íhîÿ2MaÖ½·,©î!5ÏQ¸jëÍNüC2v̹úê+Wî¦2$ŽIDAT÷éLDqÍ5/›uÓ)/Ë Pªïÿ÷¿rã7;Å3f´\sõ–€‘óAÿøç?åž{îs–Üy§ÌÞç8ï3¹:+§1áݾ÷Þ.ŒkŸfû¿ߣ¸F¦A`-âþ»å i¾ôIyûX]ှ"Çw¬³ùT¢¨Êô^ÆÉpOäòS ïñ™Î½Fº„Þm{Ï1ÝN®Î³¨ŽÃt-ÜçYБö¦qòNqv¥¿%>ùË'’þ¦è,è3‘NèjQ_éìß»Lªž¡´×}˜NÇnµÓÝwÝ‘0Ôˆ=?Õ«wß~Çl¶÷ù>St÷9¢îtø,@Ô"ÍŒ>…¤O#¹“>Ù¤O¦ëÓîMN–´[íÆÇNÚ%оû|É~ëûúÚë}ÜNÚ=’v½d'ïÆï†Ð^Ö~ͦ ö6xEÒpßP¦Sªáætœùâ6qâö¾‹½.Íœ5Ëtév™³­îº¼p2O=õ´üárfýøÔSDÇ×±“¶~Ò ‰Þ$iÚ}wÓM§_È›ôóÓO?CšL·&š40¢o Z.ïúö{Í—Þ`jkÖ¬‘vsÓ9Ït™ô+׸A~]ÁÝ¿»{½È]w&v`çG_µxí ÞNçž{¶ì²óÎö[ç•¿E^'Â>ggÍj”‹/Ù2–‹¶ÓVdîô´Ëá÷®±¼ƒ}»— ú¥/è±ííâì´ÓN•=ÿçÜY¨®+YçõÇ&¯{&æ5!ã)Þ„Y÷AË¢Y‹ÂUÇ–»è¢Kœ’å+_–ÌXAR÷غÿl®yÙ¬¤ì,‹@) h’ç_p¡S$ëWˆË$ýŸéÚýW×îÞÀC&×ÀdùÈäÞ.ŒkŸæ'rDuLæážïþ»å érÿ~óM¹óÎ-½¤èCh7ßt£iÉ<ØÚLªQTe z/ã.s!OçòS‡tŽÏt¼29Çt»¹:Ï¢:Ó±ÑeÜçYБ÷ÞN»mÖî›S¥LëD·Æù•éþ39&õ¥×*;éXôãÇ·ßú¾fš?ÝX6÷ù>}1ÒœI€(M(K-‹ÑjÓýÅ…æK¬{€d;gz!ÕÖDx€hÉÞôî{ïÉÍ7'þàâ]&Õ{ïò™\ܲ)Cªüñ9Ÿ€û†2Yîµ5¶ì9ðÀ¯˜ëÜ—»,8èuéŸÿü—5ð¼½ïÃÍ ÂG¸¶ç{__zùey챟;³u]×ôz=Ûüøh§K.¾¨K`ë©§Ÿ±nRíe:è@9ö˜£í·ÎkÐr9+š ôë¸rošA@/^ì´$u/ãž#@tþùÊÜyó¬Í¦ ½üòÿÉ£=îd!Y€ˆ¿Q^'Â>gµ0Ú:H[ Ùéê«®”±cÇØo­\ô ‡&}F»tÔ.üRÐ/}AÏ­ÿÛü˜t×–“¼ÁaÍSTו(òêg˜l^˜u´,Q¹zëóÈÿ="al¡dîùQÜcëö³¹æe³®»lL#'µk[MWG[Zêß›'~¾eüÞ Ϙ~çz°á(3næ×\ãffr tï?Û{»0®}šŸtÊÕ5Òí‘lÚýwË/@¤ëy{Ðoµ»iM©DQ•-轌•Ù"ø_.Ï1åHçøLÆ–í9¦ÛÍÕyÕq˜ÌÆ;ß}ž y»ã÷¼üÚÕzæØc–ƒ<нIßiï5õ[ß:\¾mþ¹Ó[o¿-·ß~§3K]û￟Œ;VÖ¯_/ï¾ûž¼÷þûÎçúCĵ×\å;žIÐrÙÕ@–vá¥ÍÓÓM… Òüó÷#ÝZŒn¹°ÏYÍ©v1¨] Úé ó°Ë±Çc½õ^«_ˆô‹Q²ôK_ÐsËû´±_€(ªëJyMæè7?̺ZÍO®Ï™¿î”'˜ûït»o¶¼ìùé¾úÝcÛëfsÍËf]{ÿ¼"7írR»…²Ó#?d¾ÿú?`/ã÷úàƒÉ«¯½æ|äß2“k ½±0îí¸öi~Ò)G”×HÛ$Ù«ûïV²Ñ´iÓÍØSW&lÂ~P%U€(ª²½—IÈ|¿ÉÕ9¦ éŸ~\aœcºÝ\gQ‡~6~óÜçYÐßBŸ7=I<éêIâsŸ3Caœßu(Œ°êDóŸÉùÖþ39&½ÝqŸyÆOå _ø|BU„•?{£™ÞCæûX´óŸÉ+¢LÔXÇWÀý´´.p¿d\ÜL'½òÊ«ò³‡võë ÒùpóÄœ¹s­uŒ  6x?–sÏ1]ôì²¥‹ž·Ì“ã·ß±å‡ÊñãÇÉþûí×e½d3F%#6 b/“ÉÅÍ^W_ƒ–Á½.Ó PÙÜPú ½.ý¿ÿ÷Š<ôð#Φ4È£ÁžTé…ÿ*O<ñ g1¿ÀRÐ'hŽ9ú(ùêWr¶éžZ.]÷µ×^—ü™{3¢7íÛ›ñâúöí#½LkS ZéXOÚÂÈN… ²óÈß["÷¯aŸ³Z‚¦¦&ùñi§;ÌÞ½zÉ}æ>JÏ_ÿæ·òì³Ï9MÖÂÌ^ è—¾ çV:¢¨®+QäÕvKç5̺ZÍ_®Þ.3iAÅ=¶·>²¹æe³®7¼G Ô¼ßéý~ˆKe ÝŸúãŸXµØËÞpýµ²Í6ÛØo3þá:¬{»0®}Z˜t®å¹¸F:°ž ÷ß­d"]ÅÛ¥’Ýåóñ'œ$ëÖ­³¶ºë.»È9眕°‡¨Êô^&!Sþ&Wç˜2¤s|z¹Â:Çt»¹:Ï¢:½6É޻ϳ ¢k¯»^>úhгi¿1y¬ÝQÐó+ÌýgrL>fzÚxÉô¸a'ïø§aæÏÞ‡ýô2ßÇ¢ïL^ e¢Æ:¾wÝ}h°ÆN—_v©l»íûm·¯úä¢>]`§T?~ØËéëÚµk套^–çÌÓ·:m'ï àsæÌ‘ .Ü2®ÅÞ{ï%§üèd{ñŒ^3¹¸ùí(Ý2ø­Ë<(nln(ýJôºôÑ”)ríµ×;›ú²iásâ‰'8ï“Mè˜9:vŽÎ?ï\Ùi§ÏÙo­/“§ÿôÌ„œ=z½þÞ÷¾Ó¥å§{± åÒ~‡OúÁÉ OÁj×&‡ó -AuÞ¾Ÿ‹!@dÛð÷ÖÈÝkØç¬óoºYÞÿ¿ö[9ÿ|sN}îs¢çÑRÄÔÔ³gOë# Y ú¥/蹕N€(ªëJyMæè7?̺ZÍO®|ð\ÃMNq»kÅï,䙈âÛ³ çm6×¼lÖu2À%.pó-·Z-¼íbê½ÞãIÞnFõoÖÃ=(µµµÎf2¹†yoƵO “N9ryt€7O¸ÿnu Zlº`>ûœóDh·ÓE^`=`k·(ó EU¶ ÷2vž‹á5Wç˜Z¤s|ºÍÂ<Çt»¹:Ï¢:Ý6ÝM»Ï³ ¢>úH®½î†„M{Ç× »NtgAί°÷ô˜ÔüÞzÛíòŸÿ¼£“VºíÖ›Í8iC¬é°ó·y]^Ò½‡Ì÷±Ø%ãf €Å¢Ý üîw¿—gÌ?;éqÚ×pª¤ÝþèำMÇNwÞq›5Þ†ý>× ÈUW_k >n/ï=w‰þð¨Iol´9¯º»A²—IõšÉÅ­»m¦*Cwëò§@¦7”ÉJôº¤?<ëÐvÒ±Žî¸ýV)++³gù¾^tñ%ÒØ8ÛùìöÛn±ºÃ³g¸oxu[:ÈñŒ3eVc£,Y²D*M·CÍÝСCeçw’êêj{Uß× åòÞœíº«yêñìħís€È.?l‰è_Ã>gí{ûiÿâž{Zã*^zÙåö"ràÈqÇuv=çÌôLx¿ô]uÕV·ŽžÅœ·AÏ­tDQ]W¢È«‘ÆD˜u´,š½(\.\dƸ:Ç)ýØ1cäê«»r>L2Å=v’]9³³¹æe³®“&(Q¿™–å»Z–ë=Ü=wßé|§N§Øúü“O>q=à€¯ÈñÇë¼×‰L®aÞÛ…qíK·ù¸FÚØî¿[©~ÿøÅ/~)yáE{U3f´,Z´XZZZ¬y~¢¨Êô^ÆÉtLäêSŠ çY˜ç˜î?WçYTÇ¡–!ä>Ï‚ˆ.¾ä2™5k–³‹#†ËM7&ŒÂ®ÝYó+ìý{I¿33¡-RtÊ©æ:´¥1ÀÏÔùí ìü¹÷í7ê2ßÇ¢_žÓG€(])–K)àýÁ@Ÿºõ–›dРAÝ®ûÚë¦  ¶t¤O=úÈC)œôÛèÏMwG/šnìtýu×ÊÈ‘[š²ÿðäSžf¿Æ|c¾gš¼·£ú¾|ðW3Ýœµ^ª2dµqVF‚Èô†2YA‚^—ô©›cŽ=>á‰Á‹/¾P&Mœ˜l]¾l444Xc´¹[5¸o<µ›, اÛí¨ßŽƒ–ËÛ¥Aw×çO?*W^uµ³[¿Õ ûww1`À¹ëÎÛí{'^6Mæ5Mçí¤­½Ž¾ò÷íÝtØç¬Ó7Ê)§þØù¤_0¿øÅ=­î9ìeì1ì÷~¯ÞqjROAmïýžßDQ]W¢È«Ÿa²yaÖ}вhž¢pÕ/Þú7À=N[ªãLÕ7%ŒKö=v²:pÏÏæš—ͺî<0@© è5á̳ΑeË–9EÓ11´«9ý»”*ýÆt‹ú'W·¨:¾¤>üÙ·oß„U3¹†yoÖµ/Ýräã©àî¿[©DÍÍÍrÆ™g9÷! fÞøˆt™(Êô^Æ›×B~Ÿ«sL Ò=>m¯0Ï1Ýf.ϳ(ŽCÛ%Õ«ûÅdyNg>¢t”X&-”žsîù²bÅ gy}êäôŸœ–´5ÐÇ"wÞu·¬^½ÚYç»ß9R;ìPç½=±hÑ"óää2™4ibÒà‘Ž5¡ýOÚÉ;’÷©˜‰Ûo/úÃIª›]ýæO»{“÷âÖݺneðæ÷ PÜAo(S•6èuI·§cÀéXpvÒ§—ôFÍûe^?_¹j•ÜxãÍ2{öl{qù¾i-zˆi5êN/þõ¯òóŸÿ™5|Ø0™ö9ë.Ð#>&:¸©_XéNQÔIó+ìý{I-k]]Õ•é„ ãÝE·z‡ºìò+­ÞGì¾säÿÊ׿~˜ýÖæ$ÌïüaÜCæóXt`2˜ @”«$x÷Ý÷DûTu'½@~ó_— &ÈðáÃeÆõ2ßü ÷¶éCÒû#ÈÖ#FÈu×]ãûCáƒ?{H^}õ5óãâHùúa‡‰vdmô†ôõ×ÿnýa?©]ËéëîÔÜܲù©˜ÎfÒú™votòO²Z¹ \³f|ðá‡ò÷×ßMß 'žp¼ì·ß¾îÍþƒF2À(z 7”éÖ{Ó•*p­ÛÔÀþgžmžßèìB[½wì1¢_&uܽ~N›>M}ôqY¾|K0E$¼íÖ[DŸu§Å‹—˜¾ÌÏMh™äþÜ=­7ÂÚµÝNæ ÕCùšôë×Ïý±5´\Þ. ôï…þà¹Ã“¬íiàÿÝ÷Þ³Z°êîTˆ"þ~¸k(¿ÓaŸ³îÒLŸ1C.»ì ÷,gúä9Ü{R%ýÿšk¯sÓóWÇ33z´õë¿|(»áóæG¾^Ö2AÏ­tDQ\W¢Ê«ƒ•b"̺Z;kQ¸ê¼\p‘½ ëu¤Lþ˜c޶î»õ=wîh¤Ý»“þèwÇí·ù•2¹†}oƵ/Ýr„}t;w7íþ»•N€(YÝë>’ˆ¢([Ð{™î ñ³dÎažcZîtOÛ(ìsL·›«ó,ŠãÐvIõê>Ï´[N ôØi•¹FjW .´ºÜÓ.Ñìß,íeô{÷µ×\e}ç¶çÙ¯QÔIó+ìý{I»œú»±~×Ùߌ‡¬÷ºÜo~ûTÂC©ú7E»<ÕVGv ;aÜCæóX´]2y%@”‰ët+ -‚´ý Iƒ3—_~iÒ~òï½ï~yã8›Õ‹ÃàÁƒÌ“èu²È\l›V®t>Ó‰ïï»Ö 3Í›LtO˜®è¼I÷¯ÕÔT[‘êåËW$\¸4‘êo¸"Õº¾÷â–ê‡Ø°ÊàÍ;ï@ xÜ7”©ž8J§”A¯Kö6½]‚ØóõµgϦ{Îf÷,kZ¯›§Ÿ~š|áóŸïò™Îøï?nÜ2ø¹ïBž™ºÍ3~zºì¶Û® Ÿ-—Þx_xÑÅ2gÎÜ„íèš>ûì3Y¿~}Âgö›B ñ÷îü¿†}ÎzK¤Õ zg[?² 4°Ë|ïŒuëÖ™qÅÎèrÎjן}Ö™Î9ôÜJ7@¤ù ûºe^½Ž~ïìû eqç'lWÝöwÞ%o¾ù–{7δþØáý1CH×_­³ŒN„yÍ5/›u Äb, tÞ}÷ÿoïÞbí¨Ê/lˆ­í§1b$H(‹EîPZ„* Ø>ÉM(´”r)7¹I …õ°A| \ (&`‰( åR °‰$--”ÒPêü#³;g³Ïé™sÖ9ÚoHÙsÙ³fÍ73ë¬=ÿÌZ·§­Z5,… &ç…åà ½N˜»nùiÙ×d?r–‘½L{Í«ÿÝJ€(Òèî±Jw Q,ϽoMë2Uw¤ÏѾÆÂ¢Éùßk,Ò«ë,÷yyÊP¿Î†òýúw¢U£ø=]=¸U_ã£qLš\_¹·ß}NvïoLG€(^èzµŠ;¹êÛë\ì6k2-@ÔDËw‡$7-Ú޶‡«ÛZ1Þ.:û¬3Ê7Œúîòå¥ûî_6Ðâ~ó££ùÎë7¯šˆäÉ?=•î/ÒŠ‚q¨Ãܹç§#?¼ß×» ·mˆríC¿L˜ @`‡¨W(·g€(ÊÆx:<žÔJÙoü\8ï‚ûq‹J]¼Qúâ‹/uŽÏn»}¹xâg|ñ&é¦ò_¼¹ó^Ñd]÷7ßtcù†gµ¬iyëÅ«ûW]}Í€ *íø›ñÖª·Ò[EÐ(†6ˆüý¨ŽÖöÿÌ}ÍvïÑ#ýôg?ïÔeã7ÀœóÎMGyÄ`«•ÍPôKµ&u™jõs4®±°ÎùëÆ5éŽÕu6–×Xì×ïŠzÓƒýºgÓêñÛwüøñe³h“‹º÷ÞE?;ñ;ïëE =®«¡¹IÓë+×öãC<ÀC4ÿË;—¤ ÅC£wß½4½ðÂßûÕ=Ã'º!9© Å=ÞÁ†\ùË]‡ësq0£m- Ú–åY¢ ÆwÞy»hûþ?i⤉é+Å Æè#h[Aœ6–UÅëîëÖ¯K[>Ù’&Oî+û¯˜4iÒ@« :?n‚¾ýö;e;ËãŠ&æ&¡]wí+ÓtÅba<¿aƲ#ßøá<Ô!÷> u»¾G€@»¢²¶x‹&BÚñjywe8¹n¹Ô½­µEÓÑüÁ{ï­+›àŒ |w_CÝëÄt´«}Þœ Š'>(ÇR·,^”Æ×ëëå¼hùŠ+¯î,?äƒË¦æ:3Š‘‘ìW”Óÿ^½ºìƒ%š(²×^ee½J?nÆÇÛLQQèÿ&Û/ÓûôáˆIC8®ñwò£6–A·z»ÊUþêŸþ~Ô5Æ~|4®Ùú^DÓ‡sΟ[žï1?Ê„%¿¸=5©cTéŵçýÚ5kË7£îµûî_ýÌÒ&çv¤×v< 7ãGo“a¸åJµ±ÌkµÍê3÷±oº/U>z}ŽÔµ;͸ ýEg½QfF@¨×¹Ó½^÷ôHêØUZ#)óF²nµ}ŸüO ÞvˆßÌñ<úëý\ñßĉ_,þM*üŒ:T“a¤e`Žº]w~‡Söd?r”‘ÝûPŸ®ê·q£5ê·MnHG:áññÇ›‹:üç;ý?×Ól<×¾ µ.3X^v”e¹¯±Øï‘œŸ£qEžÆò:ËuF¾Û0ä>&M¯¯Û¯~GÄùõûQ—æáãoÌ—ŠÖJöØc~ˇâŸ#Õvr×!Û~. UGÞ' E n(.¸øÒNZñ´ùé§Ú™î5òî»ï–oMTËŽš>=Í™sn5é“ÀN#ð—gžIK–ÜÙÙß‹þ¸¢I8 @€È- @”[Tz @`'ˆv€/¹taGaÿý§¦+.ß:ÝYP‰×ÊëmÜ7mR«–”Q;´Àµ×Ý^ýõÎ>,¸hþ6›Uè|Ù @€h @ÔËW  @€m D3LgŸsn§‰¬X#ú ŠŽ8÷Üókâ{Ñ„Q´ÛüÜóÏwæGu·Þ²¨l:´3Ó@ št¼lá=?þ Eórw4nÖ¥“€ @€ 0ˆ€Ñ 8 @€Ã¸ïþeiùòÇ>³rôYm òÉæ´¦è%ÚFïæ_8/zè!ݳMø¿XºôÞôûÇïìç1Ç|;}Ö™i# @€ @ §€QNMi @€¥À–-[Ò]wý*=õôŸ‡,2a„4{ö)é;³f y_$ðÿ"Äž7ç‚·VÃõ×]›öÙgïjÒ' @€È* @”•Sb @€@]`õêÕé©§žNÿøç‹)Æëo 7.õõõ¥É“ûÒ´iÓÒÌG¥h^Î@`gX¿~}šsþÜÎ5¡ @€ @€Àh –¬t  @€~ÑçÐû￟6mÚ”"8MÍØ*°qãÆ´áÃÓ„ñãËkdëc @€ @ ¿€Q~S) @€ @€ @€Z- @ÔêÃ#s @€ @€ @€üDùM¥H€ @€ @€hµ€Q«Ì @€ @€ @€ò å7•" @€ @€ @ ÕD­><2G€ @€ @€È/ @”ßTŠ @€ @€ @€V µúðÈ @€ @€ @ ¿€Q~S) @€ @€ @€Z- @ÔêÃ#s @€ @€ @€üDùM¥H€ @€ @€hµ€Q«Ì @€ @€ @€ò å7•" @€ @€ @ ÕD­><2G€ @€ @€È/ @”ßTŠ @€ @€ @€V µúðÈ @€ @€ @ ¿€Q~S) @€ @€ @€Z- @ÔêÃ#s @€ @€ @€üDùM¥H€ @€ @€hµ€Q«Ì @€ @€ @€ò å7•" @€ @€ @ ÕD­><2G€ @€ @€È/ @”ßTŠ @€ @€ @€V µúðÈ @€ @€ @ ¿€Q~S) @€ @€ @€Z- @ÔêÃ#s @€ @€ @€üDùM¥H€ @€ @€hµ€Q«Ì @€ @€ @€ò å7•" @€ @€ @ ÕD­><2G€ @€ @€È/ @”ßTŠ @€ @€ @€V µúðÈ @€ @€ @ ¿€Q~S) @€ @€ @€Z- @ÔêÃ#s @€ @€ @€üÿ¼7Q¡ö²0²IEND®B`‚python-cx_Oracle-8.3.0/samples/tutorial/resources/favicon.ico000066400000000000000000000432561414105416400244140ustar00rootroot00000000000000 (6  (^00 (-†(  %ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ$úÿ#óÿ%þÿ%ÿÿ%ÿÿ%ûÿ#òÿ$úÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ-,÷ÿiiíÿ %þÿ%ÿÿ%ÿÿ&&ùÿqqëÿ--êÿ%ýÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ#ñÿ#ñÿnnÿÿýýÿÿ"îÿ!èÿ$ùÿ43ÿÿËËÿÿããúÿ$øÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿzzøÿzz÷ÿccÿÿõõýÿ­­óÿ¾¾õÿ44ñÿ%ÿÿFFøÿÿÿþÿPOøÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ˜˜ÿÿ™™ÿÿ``ÿÿÿÿÿÿuuÿÿÕÕÿÿ´´ûÿ%ýÿ‡‡÷ÿÿÿÿÿøÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿ&ÿÿghÿÿ÷÷ÿÿ&ÿÿ’’ÿÿØØÿÿ%ûÿÊÊúÿ××ÿÿÊÊúÿ%ûÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿiiÿÿ÷÷ýÿ$õÿ’’ùÿÔÔÿÿ&'øÿòòýÿrrÿÿëëýÿ$%ùÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿjjÿÿÿÿþÿnnìÿÑÑøÿ­­ÿÿddùÿøøÿÿ''ÿÿòòÿÿbbúÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿJJÿÿ¬¬ÿÿ¯¯ÿÿËËÿÿ44ÿÿnnÿÿ——ÿÿ&ÿÿ––ÿÿjjÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ( @ %ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ûÿ$úÿ%ûÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ýÿ$úÿ%ûÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%üÿ âÿßÿ!åÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ"ïÿßÿ áÿ äÿ$õÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ýÿ++çÿ..åÿ&&êÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ#òÿ//åÿ*)åÿ áÿ áÿ%ûÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿêêÿÿüüÿÿÑÑÿÿ&ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿÿÿÿÿÿÿùùþÿààûÿ[[éÿ#ðÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ#ôÿ"ïÿ#ðÿ%ýÿ&ÿÿîîÿÿÿÿÿÿÕÕÿÿ$ùÿ"ëÿ!èÿ"îÿ%üÿ%ÿÿ%ÿÿuuÿÿééÿÿÿÿÿÿÿÿÿÿòòýÿ""ìÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ!èÿÜÿÞÿ%ûÿ&ÿÿííÿÿÿÿÿÿÔÔüÿ âÿÞÿßÿÝÿ!çÿ%þÿ%ÿÿ &ÿÿ&ÿÿ’’üÿÿÿÿÿÿÿÿÿ{{ñÿ$úÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ__öÿñÿŠŠòÿ%ýÿ&ÿÿííÿÿÿÿÿÿÏÏùÿ@Aèÿ­­õÿÃÃøÿ••òÿ!âÿ$õÿ%ÿÿ%ÿÿ%þÿEEðÿÿÿÿÿÿÿÿÿÆÆ÷ÿ$õÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ±±ÿÿÿÿÿÿÿÿÿÿ"&ÿÿ&ÿÿííÿÿÿÿÿÿððýÿùùýÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌøÿ #ïÿ%ÿÿ%ÿÿ$úÿ‚‚ñÿÿÿÿÿÿÿÿÿôôüÿ!#ñÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ°°ÿÿÿÿÿÿÿÿÿÿ"&ÿÿ&ÿÿííÿÿÿÿÿÿÿÿÿÿ¸¸ÿÿkkÿÿÙÙÿÿÿÿÿÿÿÿÿÿUUôÿ%ÿÿ%ÿÿ#ôÿÆÆ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿVVðÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿttÿÿ®®ÿÿ¨¨ÿÿ&ÿÿ&ÿÿííÿÿÿÿÿÿ÷÷ÿÿ%&ÿÿ%ÿÿaaÿÿÿÿÿÿÿÿÿÿúÿ%þÿ%ÿÿ!#ðÿôôüÿÿÿÿÿÿÿÿÿÿÿÿÿ––óÿ$ùÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿííÿÿÿÿÿÿááÿÿ &ÿÿ%ÿÿ99ÿÿÿÿÿÿÿÿÿÿ¯¯þÿ%ÿÿ%ýÿ\\ðÿÿÿÿÿööÿÿÇÇÿÿÿÿÿÿÐÐøÿ$ôÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿííÿÿÿÿÿÿ××þÿ&ÿÿ%ÿÿ00þÿüüÿÿÿÿÿÿ¸¸ÿÿ%ÿÿ$÷ÿ¢¢ôÿÿÿÿÿÞÞÿÿppÿÿÿÿÿÿ÷÷ýÿ&&ñÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿííÿÿÿÿÿÿÖÖûÿ%üÿ%ÿÿ00úÿüüþÿÿÿÿÿ¸¸ÿÿ%ÿÿ #òÿÝÝùÿÿÿÿÿººÿÿ00ÿÿüüÿÿÿÿÿÿ^^ñÿ%ýÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿííÿÿÿÿÿÿááûÿ "íÿ$õÿ88íÿÿÿþÿÿÿÿÿ°°ÿÿ%þÿ99ïÿýýþÿÿÿÿÿŒ‹ÿÿ&ÿÿèèÿÿÿÿÿÿžžôÿ$ùÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿííÿÿÿÿÿÿõõþÿ!!ãÿÜÿ\\ëÿÿÿÿÿÿÿÿÿ““ÿÿ$úÿ{{ñÿÿÿÿÿÿÿÿÿXYÿÿ%ÿÿÅÅÿÿÿÿÿÿ××ùÿ#óÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿííÿÿÿÿÿÿÿÿÿÿ­­õÿ\\êÿÒÒúÿÿÿÿÿÿÿÿÿZZÿÿ$ùÿ¾¾øÿÿÿÿÿùùÿÿ**ÿÿ%ÿÿ››ÿÿÿÿÿÿúúþÿ--õÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿööÿÿÿÿÿÿððÿÿýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÿÿ&ÿÿ&ÿÿúúÿÿÿÿÿÿââÿÿ &ÿÿ%ÿÿnnÿÿÿÿÿÿÿÿÿÿnnÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿ““ÿÿžžÿÿooÿÿRRÿÿÈÈÿÿÚÚÿÿ°°ÿÿ--ÿÿ%ÿÿ--ÿÿŸŸÿÿžžÿÿrrÿÿ%ÿÿ%ÿÿ,,ÿÿžžÿÿžžÿÿ``ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿ &ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ(0` -%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ"ïÿ!æÿ!æÿ!æÿ"îÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ"îÿ!æÿ!æÿ!èÿ"ìÿ$öÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ"êÿÞÿßÿÞÿ!èÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ!éÿÞÿßÿßÿßÿßÿ#ñÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ"ëÿ àÿ áÿ àÿ!éÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ"êÿ àÿ áÿ àÿßÿßÿ àÿ$ùÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿˆˆüÿÔÔúÿÔÔúÿÔÔúÿššûÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ““ûÿÔÔúÿÔÔúÿÌÌùÿªªõÿVVêÿÞÿ"ìÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ¦¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½½ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿµµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿ âÿ%ýÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ$÷ÿ#óÿ#óÿ#óÿ$ùÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹ÿÿ%ÿÿ#ôÿ"ëÿ!èÿ"ëÿ#ôÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ··ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûþÿ76æÿ$õÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ!éÿÞÿßÿÞÿ"ïÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹þÿ"ìÿßÿßÿßÿßÿßÿ"êÿ%þÿ%ÿÿ%ÿÿ%ÿÿ@@ÿÿSSÿÿƒƒÿÿôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿžžòÿ"íÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ!éÿÞÿßÿÞÿ"ïÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹÷ÿÞÿßÿßÿ àÿßÿßÿÞÿ#ðÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%þÿ……øÿÿÿÿÿÿÿÿÿÿÿÿÿççûÿ"éÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿLLóÿppíÿooíÿppíÿ67öÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶¶öÿ àÿefíÿ°°õÿÄÄøÿ­­õÿeeìÿ àÿ ãÿ%ýÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ûÿHHëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLLêÿ$úÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ··ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿÷ÿ¤¤ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶¶öÿ áÿ$÷ÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ#óÿ||îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘‘ñÿ#óÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„ïÿ#ñÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ"ìÿÁÁöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎøÿ"íÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿððÿÿ””ÿÿ““ÿÿîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿääúÿ#ñÿ%ÿÿ%ÿÿ%ÿÿ%þÿ"éÿññýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõýÿ$$êÿ%þÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ……ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ``ÿÿ%ÿÿ%ÿÿ]]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ@@ôÿ%ÿÿ%ÿÿ%ÿÿ$øÿWWëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\\ìÿ$ùÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿWWÿÿÿÿÿÿÿÿ??ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîÿÿ&ÿÿ%ÿÿ%ÿÿ&ÿÿììÿÿÿÿÿÿÿÿÿÿÿÿÿÿmmùÿ%ÿÿ%ÿÿ%ÿÿ#ðÿòÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿÿÿÿÿÿÿÿÿ››òÿ#òÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÿÿ&ÿÿ%ÿÿ%ÿÿ&ÿÿÒÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠŠüÿ%ÿÿ%ÿÿ%ÿÿ "êÿÙÙúÿÿÿÿÿÿÿÿÿááÿÿÍÍÿÿÿÿÿÿÿÿÿÿÔÔùÿ"ìÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÅÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿœœÿÿ%ÿÿ%ÿÿ%üÿ55éÿüüþÿÿÿÿÿÿÿÿÿ±±ÿÿŠŠÿÿÿÿÿÿÿÿÿÿùùþÿ+,êÿ%ýÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼ýÿ%þÿ%ÿÿ%ÿÿ%þÿ»»ýÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸÿÿ%ÿÿ%ÿÿ#ôÿvvîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTTÿÿÿÿÿÿÿÿÿÿÿÿÿÿddíÿ$øÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼ùÿ%üÿ%ÿÿ%ÿÿ%üÿ¼¼úÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸÿÿ%ÿÿ%ÿÿ"íÿ¹¹õÿÿÿÿÿÿÿÿÿÿÿÿÿMMÿÿ,,ÿÿýýÿÿÿÿÿÿÿÿÿÿ¤¤óÿ#ñÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÄ÷ÿ$õÿ%ÿÿ%ÿÿ$öÿÀÀ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ%þÿ"éÿííüÿÿÿÿÿÿÿÿÿ÷÷ÿÿ#&ÿÿ&ÿÿééÿÿÿÿÿÿÿÿÿÿÛÛúÿ "ìÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÒùÿ äÿ#ñÿ#òÿ äÿÐÐùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ$ùÿPPêÿÿÿÿÿÿÿÿÿÿÿÿÿØØÿÿ&ÿÿ&ÿÿÇÇÿÿÿÿÿÿÿÿÿÿûûþÿ32êÿ%ýÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëýÿ âÿßÿßÿ âÿééüÿÿÿÿÿÿÿÿÿÿÿÿÿppÿÿ%ÿÿ#ñÿ””ñÿÿÿÿÿÿÿÿÿÿÿÿÿ««ÿÿ%ÿÿ%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmmíÿ$÷ÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUUêÿÞÿÞÿQQêÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿFFÿÿ%ÿÿ"êÿÓÓøÿÿÿÿÿÿÿÿÿÿÿÿÿvvÿÿ%ÿÿ%ÿÿnnÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­ôÿ"ïÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèüÿ~~ïÿ||ïÿææüÿÿÿÿÿÿÿÿÿÿÿÿÿêêÿÿ&ÿÿ%þÿ/.òÿúúþÿÿÿÿÿÿÿÿÿÿÿÿÿABÿÿ%ÿÿ%ÿÿABÿÿÿÿÿÿÿÿÿÿÿÿÿÿááüÿ$ôÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ––ÿÿ%ÿÿ%ÿÿmmÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîÿÿ&ÿÿ%ÿÿ%ÿÿ&ÿÿòòÿÿÿÿÿÿÿÿÿÿýýÿÿ99ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿ££ÿÿ¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÿÿ&ÿÿ%ÿÿ&ÿÿ¼¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÿÿ&ÿÿ%ÿÿ%ÿÿ&ÿÿÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿUUÿÿ††ÿÿ††ÿÿ††ÿÿDDÿÿ&ÿÿ””ÿÿØØÿÿææÿÿÔÔÿÿÿÿ&ÿÿ%ÿÿ%ÿÿ&ÿÿuuÿÿ††ÿÿ††ÿÿ††ÿÿVVÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ__ÿÿ††ÿÿ††ÿÿ††ÿÿXYÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ&ÿÿ&ÿÿ&ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿ%ÿÿpython-cx_Oracle-8.3.0/samples/tutorial/resources/python_nopool.png000066400000000000000000007342301414105416400257070ustar00rootroot00000000000000‰PNG  IHDRæ¹ ]1*sRGB®Îé pHYs. . ÕtIMEÙ  ^2Ü! IDATxÚì½yÇ}ç™U¯†Ð”Ìã$ºÑàæ)‘–ÆÝhB^itX¢hË’HÇÀìNìnØ»hl¬ÆþgcE̬cwfÖa¡;û7GØÖa‹§4ûÀ³ŸuðD N¨Ñ¸HŠÔRÒx½^ÕþQèzYù*³²Î—õÞç²^¿:³^å7ßÊü¥†¡pŸ"wÀ²‡À²‡À²‡À²‡À²‡À²‡À²‡À²‡À²‡À²‡˜ Â0¤†ˆçyÚ €p hÙ8C/Kp,Kp,Kpoäsˆ$`Ä•Œ´/+Ú: e£½,À!°,À!°,À!°,À!Z<ýN3gNi€&Ô¨þÔÑÌ´àˆê!¬Ð¢Zí¨¯ÞFPœV ½,À!°,À!°,À!Z˲Â3tóbIãNë„“ÙO*<«1ÌS†°ÀxjG…g8’ÚQ²ò«¸Ð2´¬èe e e áFªV$^!y Œš„ÔŸý¤áü,#œ×²€¹¯­+ÂQùQ lÛ:í(SyvŒ^䬮ÑËËË¡\–6gÒ@*®ú „¤*0jRO–“PZnÒöÔ––ºÓ€PÖ¤€+ÂÑd ßÞüÈÍÔÛ5‰êhÙØjÙ¿64 ˜JUX}ð¼7€° M G†hJ%u´ °,  Ï<ó¬U”˜låÕ”rcdÇôôŽÓ”TËhZ–++gWΞÍM@Äý÷ï! ƒ¶"¬ ó\šˆÌì˜Þ±c…3&º€4ÀHFCüÖý”Æpi:—eMYQÂ0<{öìÊÙÕ••³gׄÊ—ÓÓSÓÓeíKÃÀù+›ñ¦wi ŒkÚjŸn m€ò ²gÏîÊB;±(Sù»“>ÌͰ+²)Ñ@ËjÕ²’QLMZÖ|ÔzË2RM^ë4ÀžÝ÷MOOíÈô.óKo^寲,£­6ëŸ|æY´ªbzjjzzê~ûx/§,Ë\›s䈆 iYMqS…„e™c¢)€!ªµ)òIJl– -Ë••³'Ÿy–¾30ù(æaYÖvs(ؾ‡Ã²,M+-K„ÀéÈ˲Y*±,1+ É`oÏžûL½T°,»ˆ¹ FC…´¬¦¸©BaMË’ž•£-Õ»ï›×i³¥Žz›—LaÖpŠL×âRzV@»ä#[Ê……%£Ä’¢P“ŽØ§*6¯@ÌPDÎòûŒ† ÊÄD5¥{n>&je‰p¸Ì}÷ÍÇ‘gÕEž^Îee™sä†Ë¿øûÓSS"{ÏXMÇëWØ‹,KD SSÛï»oÞ0z *ËRѵ µÌ͘¨–%½?Z$ÕÓSSX– S̲\Y9{üÄ_ð»äãK_ü–ecaéAj’³‡¿ôÅj´ Ë2:„ã–e%ÕÞ½ððØpòä3%÷pß}ó{vß—7Àò¬[g«ÍP& ëGY€ñæ»k±²²²²RÜðúÒ¿0==UA˜'°,s¯FÌP^È„SSÛwßwß œaYÃiËòØñ¿(ö¢/ÒK‚(€’!h1S~ÁˆeÙy-Ëbòж€½‚‹úRû§`YVv)«s(ütMÅŠEC»ï›ß-õáX–EqȲ,?|ïÞL‚ÏíÛoœŸ››šÚnãEê$V 55»ò}ߪA0ÔìÔCÑÖJúÑìÝûÀÌÌŽ33<ÐŒ||á÷?¯«X=©Â·ywå)_i6ñ5»- ͇yeÒsdÙ—y½ËHÎò2VŽü×ü»ª)Ø©)&rѲÌPÍÌìØ»÷¢)€ºÕúäÉgrEž¿ÿùß›žÚž+ ,ËFbÔ¼òJP C ö¶o¿ñ‹R˜—¨r±,«b.€ä}'»–X–Åpβ̥'ÀPÔÚ>òüÂço*v-±,›ÕVeø‚½¼bV€[òW¹X–¥å €(sÄäêÆ»–X–Åp˲´×N„ -RÝ;±,›ÕÖÃ0+ÀEùˆ«\,ËrrWˆ¹trvìØ ›5Sž`YZS—eY«vîßÿ( Ð"©þ݇>·}û3ÑF†ååN§“²~rì m´/ëž–¸\“oÜö{¿û®šµñ=½(ø+S%ZÎxÐ|˜§SCþJb.€J°= È™–™b%‹`ÇòUœåŒ=UÅJpŲ´¨ìßǃàG·é/ó»}Nî,ƒeY‡°Š¢¯‘WhËî–Âyů€aqÏ=Ê”_þòWBˆíÛo´´l,K›—¢ –%1€ r¶²²b®cc9³Ñ²’–¥—s 1½,‹MWwò™gŸyæY*€¶c3,bÛ¶­Ÿ{ð³b ‡tÿ£¡GŒdSê–=ÏKí8cù²Â(´Ù.œ¿y€“Ï~öwnܶM×mÄÐ5Òf“DÏýüQb…ᜥv»äìÁ?³ýÆÓ…IVœ¤x¥N+çyž§‹,v%*¶´ò8èÚ… ÷Ž®¬œE;Fƒ33û÷?j^çâÅK.\¤¬W€\òñÄOQPˆ@ÛåìñÇŸ¤ ì²e™™þí5~F^D`åì[=AAY2LËò$ïúÆR§{F^TùØkœõ…~úÅ8v¿ Q9›™Ùa”³‹È™%ML¿“šleeåìãÆíh)7Üpƒy:…_ýêWÛ¶n™œœ #„×a(Ö>Æ"´Yî´;[ËìÔ6›çݶrmE^ íažyú‚_þòW·ÞzK¬¡Tû§ PˆTíð‹(4Iæl<§O/ÿƽ÷„Id]’0 é«„Æyž6&ò¼Pˆè_}óê4ßh½,Í}@ffv ­;Ío¿÷ý1½^“„¼ü/Ã蟹© ‚Ñy€Vs`ÿ>ƒ|\¼xñ{ßÿA¯×ëE;r"Ú©)ˆ€ƒrEC½^/þõ‚ þg„®ý³Ó5!ýÓ‰—³Z6ËreåìÙ³g Úy`ÿ>~â#¬Ó—.½yñâ%J yP0饗)"D9†cYš_÷™ï+Œ†N?õô·)"ä@!3©å óäl ˜(¹}î£æ×}{÷>Àð€QÒé“'ŸÑ­páÂÅ-[6{R’Ïó¼5eñ<Ï×eû’?øý×ožïûAÿ=X[V²¨(cÆÓáyòWyó°(»µß¼ð¸ŒÌÞ4È+´…µã¥—^þÐïƒ~méwÕu¬žç…káyž®³†!³db“X_ôšR2iW]Rs ]ÎVVVVVÎêäìÞ}°_íë¢I<¿ùXÊD"q¿ÝiË›„©BùÓ1„^–æ×}åuÀhé´áÛþð¥ È [zAÐ[Ã6ÇeœÏ%Êk)ç¸Ô§kÉÌAÖº”—fyeô´‹ýû5|{ñâÅžDPc°kÓèòZZj 1Àxbîhùƒ¾g³”é幩™y-b¢áãe¾îã 0zaç±c'R¿zó­·.½ùæ¶­[(¥ò ¯#À‘Ëòǃ‡\8Pcg Mã¦ë™òÊ+?Úú‰­”1@»åìÕWï‘:Z‚Bã–¥^;¯ûÚ\YìG2ˆd —N¿ùæ[X–å¡7͈¨êbS–ež5vV {÷>`xãEùsÁP¸}׬üñÔòiÊ °œ !.½ùæÖ-DCéÔeYêz–>£©Z÷ºOét07?77?ŸkÝ¥¥îR·LP~ãÀ`)™)p+ÀÌÌÌŒáÕâÝwÝéûr2ËkËa2›¤vÄ‚œ9Å÷ã\cžï{k˾”×2ú˜)X–¹ZÈáb>Ïè+]ñŠ6÷¦Q‚¢k2WîÝØG÷u»)Š`ޏÏ„­-M5Åo­ ù÷0xP^èÆüÆë¥—_þàÝw¯Õ¿‰Ú;ìç²ôãÔ`¾ï ME* „¡òWjÞLQ0ÈŠv”©7J1—.ø¢=PG,\YéŸÖ1×A³œ]ºtiÓÆBˆPw¤®­ÅJ¾”œÄó}ÊÈˉ Èó‚8ų]ÜÑ|Žf™F{Y*ÑÂ×}Ý¥¥Dس˜;€9°oÉ'YÙÃÜÜœ´‰ïT7_IJØ1hîT‚y"…K—.mÙ²9V²X}ËRhdXöƒ ˆgãñ‚ þ*–ë“ä!b8b½iŽ,.nõªÂ Îcxãõê~|×w\ûÐK†vk³æx~èǹûÃP™®-=œKŠE_#p8ó׈Å\rý_ =5éqL€X¸n–å"–¥Š¡£å«?úñwÜ!’–¥'Oÿ£~.yÚÒPÓ?#ˆLÏÔ= £OF&N¿³2ZUêp²r½‹Tæt±h'{‘ñ޹*oÕÓ\“3:pèð9³åì&µô²\¤—¥»2®%@I ‘)”áìÙÕQ’W«:9…L0zÚãsUÝ`(:püø'¼K'w.Ëy £MF2 ôÁ…ÙdÌ•`«|F esst±´ÅœuôÈáÅÔÎ8GIm P.ìÔ¦$»xñÒ?ºá!„çùq1Ï÷’óähÄHZö}?ìt®m.¥Âô=O7U‚œ%Læpñú8xÃÍx(½U/¢î–îõT©ß^R,ËùùbM Bf{õS’¾vêõ»îºƒ"Rhn`¸!¦jï…ÁØrˆA‰, ¨*¯Üž8>Øw•Q$…1Ôö¯zò©œ›¶B‘¹\™Èä&8]&ZÇ òaÍ3Ï>7z1W%­ú£Ç 6ì#×’Ÿ 4%gtàÈGs–¥.Ó÷ìà‚,ŸåŒC}2 Ó®1V=d¹ìv»–®¥òª‰Qºhe8†DÆåà[+{5( …óRåÀði.…ñ­i+(–Îr°ÇuÞ[Ê ôIp¼¦¡Lꈼöø€ƒR Z¿I´´Ô]Rotµ¥1™·}xZ­…–ÚgÖ…ß§³'VEȤMgyùòeaÎe9ÑO°Šu©;ñÃÐóWú~œ¿2ô}Y¢ä\-‰d+É¿Ç_ÕÃ¥pžh!ÄYÍ †‘ŒNZ8°o¿ü¤=‘]ÅÉŠee¬Uš©Ê%Gò]áÓÝØ*9«èÄÈM‰•‰ñz½ÞåË—;R¢®D.K/™ËRJ&,*Þ0 Ss"{Éœ˜†œÈ:½ÐI@µÉŽG)æªPGææçd)ES?¥¶Ql~놈ժJáTU& \Kêm­ðž6ƒ’ ŽdX¤pêõ×ggoíKƒ•hò2ûžï÷ú«‰õÅ‘–u —}Ïóòçhn€†rY’}Æ,œÝ¥%±˜¨Èê{^Ôvä\¬¸)¬;Ö‘ÅŹ¹¹ƒ‡ZQ;çMy–ZϦ&БK£p¢ŒÛºx-DK[–ÉÌ]Æk.éS×Wh‘ìîiô•aÿºª°È`8áò'Öv^?½|Ûì.DèÔÀÜüüÜÜ\\3D]cê«Çtul®‰Y2k³è¿‘øÖª5U¨Â³ºvb‹¹KÕq”™|rY!u”Xæ8U€æææZ‘ãïwÞHÇgÏsYIÉ`óÞ&©e$(©ïR›ß– ¶X§”Ξݥ¥#‡Í’Ò’›©™ñK´BµoA´5X¡z¾ÉkÑÍ¿*Ÿ™C4ƒ»@yϺò`ЦA8åÕÑ21¤³„A², òÙön ©‚—i8¦½ZÉýÛû Vå@S¸d;5óXÝn÷À¾ými תõ£în÷ÈââÑãÇ ü”ņÒÉ9sŠ ×Xí̛˲,ºm­…vàÑ}–=šãýÚëc1óÓ•C%'Ö ˜åe7WGKY1›ŸøÒRyeÊñå¾fk³÷¶±Zku‰47ï ä#µÄ;ï¾Kų¢ÉÄ…þJ‰âOe¾³¯f•[¦O§Ûm´ÓU¤µä3ã]èW_e"+WÓ½™k± ¦5´ðK£Zcp—¢àD¸”u6 šƒSÃm"SÜpñ‡~c>˜?Ð+eÞmÝ¥¥îË«²·ïš-Ø%×±F;ãõ‘ËöíÏÕâ?°o®9ðè>{‰Íl÷Œv¡u—–nß5[ ºÝn­cH=1ê|p“¨£¥dž¯Ä{šfy•×ýÕ]³E/ØÆ3©ÜPJ,jE¸ß6ˆ`\3ú[q6 55«Ž,.ºLr5àë®çs]K®`J¹­NÅàDÐeŽxû®Y&q– Š ÁË™Î2õyÈ•ÎR©Å̉,u/Ž”Î&©=áó&vé.-å}Ú#×rôúZê:J(7k°¸ºÝ®ef€\}1ƼÐr½>m8FuóĆBW®\)™wÌO¦z ;kËÉmM.K_ÎA&­&¯Ód—Ì—ã™wž£e"F³],uÚài”l%7v ’5[ê®Ôx–)JG‰æKlÄÔçòåËdÖ­NjÎ/ß÷ƒNú.tY&}_¬éˆðýx59!²HæùÒé‹¥vª}ÝW‰¿šZ`S_-Ø$_®þ×ܱ´.¥é)Ô‹¼Ñ„$ù]¹Ê¯E' ƒÁ”a>4Gb𑑺#hå¶Òq¤`YV; £á¨›#×Ó•„¤×•}¤Í¯›'Ñ+¬¯©•rjý"ê|«YAä©TYE1 ˜“’ÌÍÏ=1¯X$ÖÞhKÞ•B®³Ð‹Ë°s¹¸t9+êÿ¶KαPí‰ÈŒö@u|CÚã¯n£],Á™}=¢ŠE©RÜ9P™#󔿿ç?–è9»8F–eó%6(=ºŠF›Á9 ºK]ÃÓg9ÝGê«CK5n=–™êM‘ÈLïfn~>º¥9]ÆZÕLÚ·e6ïZê»ûwEÑþã ±ñ.‡ƒ·”"èÌÔ‡§€Ÿ"¨›žw©U°n'ÆÌ\9ÆçmÇí`s…žY§–Oëhn~þè‰ãƒ%–÷(©Î‘ý&–I”•21gg³—Ø4¹î—sµ…6øÈØìmN¼PN›=1€VÄ™ fP›|^ŠUÑ.(ßY%Û÷–§Màë~«`ÄJlnnîèñcGOlÛ䚆`Lt'õa±©šé7EaöZ0—{]U&ºã<´ ”†°ðƒj½åv\°:Pô38zü˜!üV Þn)¬?‚6Û2§–OG¸üÏài@cXY–¡„åWÊßG>«Š½oø*‘ÕB/iö‰,•'ß²"9'7H•«¼Ñ¦pò%FêÛžÌMT‰²îÒŸ: ÛæöwÖMê.4uz¸œÎ…ܧ²Ö0ÕAôÌAÿ_âÏé„AÁµa¨]ÍZ ÎEF¿F‰Rš|X…£¦*º±• 9ìOI­ÏÇ£kÃKÌì/´N>B(*’XO« ñ¿PZ$1‰…ôÏ (6á•.¶B’j”’Jk%¤rÖ„*Y™D½U¬:OÔßgÅòZ ôJQ®Å­ %o5 DІÀ)_ÒàHú•¥cŠ–%ã#«ØÇRË ‰^–ã «só…zY&Ÿ e'ºÊò]б•³"6Ä~†g¾XìfÕ™’‚$gË\&Ô`v6Ëð¦í¾U…­ CÅ–LH²!ºTÿű¦$É–Ö¤ñ?p=Î4†%ÊØºaUh¢6·´±å;+åM }œ,1K¡Eè£:E:z¯µ$o2 Û—Z9C¸sì@kƒÄù‘?ÉÂîÒHøPbðÖKá0‚Áhœk • $Ù²¦Ú‘²ú¸ùbŒáuUŽé½½Œ®A—Pó˜©LrlÅLØ2ѦpéÅã‘Ë„ Ö’y+A7onM÷i Ð”–„£å0ïQªÔMGKµíÛìÄ;JUS-ÝØÊTbyOI¹¡#_+«ÄÆ'9¸2«ÝNéP_•⸃ ^ íGeœÁÈÃô;ÕUÁss¹zx¥>ZòNºKKQ–ßÔx,óùTÇ‹å¬þ‡ºWò ±Ž0x£:á´ñ+íL”™}ÝG¬‹e…6(Šnf³·4ÛåQfNˆ¥jX],Eƒo•|}U2NKÝáhW‰”Àˆ>ÚýÙ«\~õ2”Î"ƒ³´;r-¦Joi N0í¢)Ër,Ɔ'•´úE®Âtó‘õ-K‹ÚܾaÚŠ&lªK[K͘?ßS$l(öUoj,¨ÓAØ·¿ð$ŒãpbÃ"J7©H‡¬¾ßøý5“[y»öÑ =/Þ> …´Z˜‘º,„ðúKžÍ Ïó´»Ò|%ÿ=³|¨"2Ÿ£Ñ{¯Ó®(]ùxäp©=Pb”˜%½^O®°ýÐïg÷µµtô¤åNª¦xB„k›„òrrÜ\bÙ¢Þ¶¯ù¡n‹ÍÒíj" ÕÍÁ¹^ØWu-)“Åw»Ý—k¶âÁ¶ƒ-"èõ¨';ÚÀSt-=>’Å(ÃÔøÈ÷}›ø¨yý¢—e…OלX̹~ÖNkpË÷6ƒ1Ûí»fÝ5{ËÙÊRïΑÅÅÊ[%'lq±©W¡¥>›Ñ¢–£áì‰ ‹(á¤R¬˜ÔÄ„ '¥WÚKà…)!¥š–EF&$Y>ƒ0D˜mßáV’cžÈRñn·K~aJ¬¡/<¯'×åJDÿ_Ú…ºeé}•¢)û8säð¢ûS„u—–Ž^t­VIñ -º‡×q-ƒC$»Ýî}û£D+sóEÒ­´%·`°]r–º,ä÷rò=/”{Eaúò@ÎÊÌXÉæõ[3`YV5¥¤³”CµÏ³¦—e®œ÷6µrjù4…0Ϧ.oC¤ˆÑãLpEþΞÀh<à<5P-˜•#s-- N] â·Gk79z÷YÌÁ€ò`YVZñ óÔ†Xú †t–å=@.<ºÏ¾‹%¸ÌÑÇ<ºÏÜ܉»·DOq3´=1€6‰¯&ê@%ÀŒ’]ÁÜ7?³Í6¸ŸæÇŒw—–R51U%cÎÙ1ãõ]ËÜüüÑãÇ2÷mo‹×ŽRùÈq0ƒeY#J:ËÌD–ñW–é,©. £ë>©èb”Ó×r48zâx®ãѨ„biLGãÄš' à è ÑOXi®Yö„Ö6ó–|·Û½}׬epM NSÎô¹,ûúе'ñ^MfÇʲt?¨ñy*j%Êm“È2õ«hN4›5¡²€!9$äÈâ"3rŽ˜µqôÄñS˧.,\XÈ:ÚØÀٗٱcZ÷ÕwO>3VU·òÈ`ý€™Á8+5¼J}£|ôø1ׄfpî—¨?`cƆ¯%zctjùôÑãÇ¢vxæ&ÝÇÐX–U›\¦!¯[Š7AŠ®oâ!Õ-:°o?ÞÐHÞèØ%ŒBËfÓxž8ÈÌŽBüàÈÉ[½æQ¤ݤÄGéU§kn :›næçQ|ÕV_ÖµDÞed_ÆfjÜÝív©´˲!!´Ld9¸‚4OW·Ž9r»v‡Í©lÓPhž£'Žp-»gÖÞ¸?ea¶ËP¡ÙžÉZeðw8ÜF‰³'àQùkÿ†ÔóepDaMâÛØòÞCÀ ”XIVVV(¨2Lìǧž6~Ü=¿² '™zžƒ’íεDæÑÇ?6h\þ„Úƒ B‹¨Ò² õ¶±‘k6aRfJe…¨ÖV{YæÌ°î¦¯árë|ðU^É©¼ØÇ¡ßÐЫ¥ôj4¤hàÄrñS}ÌAïW£W¯öÿzéÿzAÿ Âþ¿^Гþò?Y¡‰ÐŽZ‹¨±`«÷ÐBôoˆ±Íˆ¨rG(±Â¬¬œMýûûß½Y/âŤ$$BVY’2"‰Eˆ0Œÿé4E^§|ÐT’±Êbõ¸)“è»ç+‹;},Ì B7¤Ø4\¼–¹ùùÁØÐ&’¥oAƒÁ!ÒKH“.ô¹$×Ký'…Dîï‰zª)N±”¿†zYþÖoÝ?>¿3µÜáEûD–©+Dµ¶Ú=>s'Êh#÷ÌAûŒ×ÃâèñcJš³– žvRÛÛ¡šñá¬#3ª1§âï?­´J‹šIÎ…Ä#p \¡%Öh‡ÌŽéi Á†Á&=‡¶+×YptŸoi0HÝ^ÇS7o_‹Ù¬Ö]Z*P¹ž†kI‚-3^÷Vær-Ï¿ò R†ÞÔa6PhК°ó„Õ¶‡ÎR.Ä35Uh(_•>ªÑm«„ŸàÙa˜“ ä˜h0U" söÂ+€‰zi¢—aø–åÉ‘¤ ¼(8²¸˜+‘åàjÝn7W"ËkÁÌ¡*ÓÞÕQkXf¼zªœ˜Ùµ¼ŠÂ±‡®ÌÃË»Ó]ZÊÛ §ð›Ï\?˜º íÀ£û“½\jòÄ\iÂΊ«q ­käªÐrE5()i¼ Ý¥¥1‰ñ(1<ô·I¢'¥-ÓÔŒ^+Ñ&ùccgRÓû!÷cp¥°þºܸm+µô Z˲ڤ*aNÍ8…Œ^–vcŸSÓYæ­Üþ3_w—–nß5[y÷ì Çœ@Ü <©*U Š8ðè¾ûö[þÀtk*õ¾ÍjæcY:ž†“J¡u»ÝûöçR>û•š1:ÏQj=±6†½”¼cºÜ+ÉfW¹Z¤ìb‰”.š\c¦übiùȆHâÄ4¹Ì…6€M¿¹Ô(Ú‘å½üÁw]¹ÚÝ¥¥H°Æd0%Vž­[¶ôz1Oez¦/]:¯@“ 9!"a(ÿ³Š›òÇVÒ\†a8==•WÇÝ“’Ù”*<ûå‘˹º (g’Ëê*ü«±ƒ'tVš¹–#‹‹y{Xž¿Ë1¸³ávÝ´;ça&µL“Z9°ú—еÐâ_E~úÿ Sk±¹^–†q £7ÖÃÐ7­@/KQ4 Æ ×Õ˜–•þ‘˷㱌—Üìb“kqe,y\çÚU$´·ïš5ÜñÔvÕí»fuûîc±4gƒ-³xDÇ*ðs­»Ð¢FÆí»fm~üƒ-*ûÞ¦Þ£6sbŽ`¨íy¯X‡¼ÂpZÞ ©q‘¡+æ75v ¼Š™¢ûûögVéG/Fg8n³…Rb6œäŒ½(LQÌeÑD ÅÔ'åèñcy§ZTå–6í_l§Ž®µ*œF^xt—–ƒJYÜØµD½,#ŽAyÕuEr6wZ ëÝçúë¯G™pá$VVÎ¥b›ŸO}`rù sss©;Éåî<´0XßEcÕ£ý¨oó––ºKÝîÒR±>Í47?¯S ¨/@×›.DŠÓz`ßþÔVNÔcB¹ÆH£D9sójºœHÀì›5G¼­Ñ_äŸGáûhh1Dâ¡ÜâègS¦YÖ@¡É?~Ã>S Íðûš ‡…ñ Ü$¿Tr /0v½äÍkiÓS+B¹KSƒhei¤åñ¿ôºÉb8†S…òGݼ£'Ž×:Ú}p¬zö&…Š:uhCv+° ]w¡ñ2Ÿ¸¡¤l·9±‰´B“g2‘HE—ƒLÎ3†"=%Y IL©5u}êr_6ˆ![4=[ÝÑ‹jãç†TàÄN-Ÿ.Óöh]â J¬> ¯»®¿~CZ.E#Ör«)+¥Ì`‰”_ÚLƺd^%ÿÕD.éaîõ¨™š·Ôµ‡#3Qì\XÈÛ²œù3ok?3(K™}¨ºî#JèüJ›òúµ”Œt†ƒ·ƒÁj14˯ŸœLÓ29&Zûèÿ…ÉÓîSûX*KO£3†ßÿžñ‰©R«§¼½,«ŒŠ>ÿsssõ™#-ò+uÁ¡!7ðÁC ……êàÂB¦ÚßÓ¹¹¹S˧ u.{ôàÂÂÑÇ «ŽB;µ|ºÀ?jŽXþò‹s'Ö‘Þ¶u ­´2è&[$€sI/ÌuHa±±“¼ÕcÔê8µ|ÚÍ |)±æ1[lô²L¹ö쟘+oC±p35ow­µƒ‡ »6yßcEîa“á˜ýk³Z¯¥pÄ‘+Òq6wú9­9‚.Æ7pô²L§Ñ\–;¦§ŸÓß¼‘>?øÚ§@UUá45-<´ KŠ7xhCJJƒ6‹(ï^ÖþÛ[ÊŽ\ËTõ›Ÿ?zbÞ>Õ£9Lê=›Ÿ3xÞZi™“ÌÚ§²P÷Š -úñ[î³À…D眙Ig°¨ûÄZ#Ò“ˆtYy5|ûÝÖ*l}oÚçæçŠ…Âë[Ž™ˆúÍj†A©-ØÔ| 27"ªÅZΩÔH‰UÕ-GO·I›X>ÌÉlážåÈéËÞ¶ªvûÜœe±l`×v-rek¾­ Äà#FÝô°à \1<]μW¾R–ãÿû¿ùwºÍ÷î}€Y&uÚ²ªªÈÔ„¸ÔÂQå;Øà(_É*{NݧҞ( ½ƒr«Ëˆìx¡¥î³Ú±=×zbCä»'Ÿ1„¿qÏ=ïZ¯ß÷|?^ö;N¼ÚÄĺxyýúõ©ËN'ÞDYž˜è¤n²n]·ë¤¿{žt&Ò²çyžß£àKËžç¥.~Œ…R÷÷Lý ‚ ^îõzÏ=ÿÂóšäeBˆÿéü<×4·¾çº±4à;vB÷í®[oŽc\3–¥ßð­Ú£O­"„ø.)·FsÝ>9¹!9E”õ'>¿¡Í?iG£_Îd´€ ð ‰'32Šô§ÉI,ëDE§/­ÃÄ\ cN•™ÈÒvš5 k~󇜞"¦@¤£˜s‰,«‚—‚0&Ú!„زyE”sñ ÀÌoà˜xÇÌ,Ë={î3|{ôØqî ÀÈ`®Õ‰9+dÇô´9@5§ýp‡ïž|ÆàmÞ„vØÄ\»‰¹†ŽaH¸bë–Í‘aô²œž6t´\Y9KL0d ëÛ°arä:â;P?Å#¾ãöB›áÊ€ÀÔ裠ҭڂMËP§YPMNaP% ­±¾Wòѧ§¦¦ˆ¹†ŠùýòÎFqB;¬¡tM³¹¥Vˆ?”Ûfîhi6¡ -˜ëóÍÙ],CÊ0;¦§Í*ÃÃ`Ä<0Å\»‰¹††¹÷†`À™ñ,Í-CF=æœÜ@Ë!¨ Ç1 'ÀËs™:ZsÔŠùÍÐM;g(¢Lüaø‘G¾dø–ž £sn&æR€zìØ \Kp“Ÿ®¬˜‡„àåÅü‹˜  &è½Q þMO€ñŒ97oÚèŽH7ïX<òðÍ+ÔÜÔs‡¼LOOí&æh–£ÇŽÓ{£†iYÎÏÏm߾ݰ=AF2æÜ¼i#U+ÉäZ®¬œe0 ´K;„7íÜAA`Ïîûè}ÐÙ~å¦M…ßÀyö_µ¸‡¿o~μ P!-œZ>ÿ;xh2æcNüÊÈŽk íÒŽ›fð+‹³û>b.€&Èô+'7l )³=C¶,§¦¶á÷?‚ŒI̹yÓÆÉÉ ”U<òðq-`4´crô£T̵}û>ÿ{Ä\µ’éW zoäÄúLMm7”ÌÐ#s"ÒM’Ù­feåì¿ú×JŒ ŽkCÂ+йn$æ¨ ¿rçŽiÞÀåÂÓÍ6÷ïÊWAÈ¿’—ƒ ˆWûÚ×¾qîüyó¹ÎÌì8°÷   ½aÃûnÙ¹³/?~?ÇŠçy¾íšïûÉåN¼ÚºuëúËëûËëׯ—;N§Ó‰—':ýå‰ iyWëS‘8é =Ïó|_^M¾ÔåÁ©z­c©¿²Îöz½Ô¿½ÞÙ³«_ÿæßdÞÁýûÝ9Ã<¼àœv!>xç©Ú×öʲ¢#ñWò²býu×¥êÈuÒßMI]V´CÖyYN.¦[¶GVeYµî¯}ý›ç‰¹*%šzÔ¦åæM£Š_R Y¿de™HÕ5Y€]“õKp”X)Ö&eWrP£Ó5ùp-ÓéšAït_u¾ò•¯ÔqÏlæ]•5õÎ;ïX]=÷Ë_þÒ°þ{ï½·²²òk7üÚ 7ÜÀSàˆBë±ÇmbÎÙ[oÑ)™1ó3£SC¤šºl¹+ËðÒf٬йֱ‰Wåå÷¿ÿýSÛ·¿vê”yo¯¼òêÊÊÊ=÷|ˆŸ48¥7Íìc-¹žôõoä[~á”M &Çf4Åò¬JZ–d"úxçw,-uÍsäR´cÇN¼÷Þ{æÕ&7l˜Ú¾-®ø5J!+Kº‚ȤèšÎ[´‰•1*£kb$-K!Äõ×Ož:õz¦‚¾òÊ«B:ƒ ïž|æ±ÇÏTh!ÄÎÓëׯò4S‡e†á>ð!Äùó2öäÉgffv£€;Úqý䤮jŲÌ%ñÇ©íÛ_{í1@I¢×o'O>›¹f2à –eޝ\.„¸zõêùóç¿ù7ß²ü}0Š `ˆ m3ü!Ž9£¤-¾$à of`x?Ë7¾™éZFÌÌìØ»÷*ç»'Ÿ9i*1Ò/Ú10|Pr ¿&â?]ùÛo=FÌЀ¢mØð¾›fvHU=Ãs|U—e©ÓÎPÑNiµ+W®!r¹–„U “ˬBÜ4³#N2m|s‡—^j¨)„˜˜H·) –¥.¼¼îºt›r]røa[,Kƒ}Áê깿ù[œí´CébiY¦„s~'iYjtäºõIËr¢“%&tDó”ÐNÓ™¥d/Ë=E®\¹rþü nE[ë_)»{ÚºÀGÖ¯NB×::]»NóZNYZ–£Ó˲°eñõoüÍÅ‹íEtffæ£{àá¨UžWVVìÍÊ Þ·yãÆë¯Ÿ”¤ËÒ¤§u[–AäŠQQX(ÉOWVVVÎæ2+7lxßæM¯Ÿœ”%Bªr±,UŠY–ÑÂ7ÿæ[.\°<ŠDC¹MŽe™¡w-³,¯\¹ò­Çž°w-ÑQwN±6B¨.–¥IO°,…y]Kšv`YfPƲB|ã››7æÚ»÷!ŠcBÞ~›6þú–Í›µ Ë2×WîZ–Bˆï}ÿßÿþ ü¤¢È )(,ÌBˆÚ+ôæMåËÒ¬§ÍX–Ñ:û­Ç.^¼Tìç±wM[Yˆøéš^½v`YfPÒ²¼zõ*1@j4TàÝÛµêqÇTrú8,Ë ½«Ì²4¬VÒ²”£¬+—/G ç/\øÖcO”ÿÁÅ‚ ©”‰3enšÙ! èÂ"ÔÔÍ‚gÐÎõš9st)¥Õ©äHU–dÍô;žçù±$'ÏJgYfÊp¤†6Vfêßu–¥²¬³,ƒ (£TŽN;¬C»ÄD7šéw´ï®ò¾S-Ëä&ý³jIJ¬¤§È… {œ˜ ƚ¥Ìä† 7ƒâEešoyÊy*Å(L}×Ñ[–:ûRv? 3Ôé^¿ùšN»n‹•®ýÝqË2ÒÑ'ž|úâ¥K}ñâ¥üð%ŒK™Ü°aó¦ñÌàÐn¼qÛC7~öìÙÕ§žþ6¥hǘó™ßùôÅ‹—~øÒK—yí¦;ïÞ  ü¶œè¶m[?û™Oß{χ¸gN±iã¯ïœ™&æl/Û¶mýÿÅ?ÿ{ï¡( 6lxßÎÓ7íÜv8¨ŸùO£–LnØpÓÌŽµ™Á¡b\.V‰÷z½^¼üÊ«/¿ü 7`¸lÞ´ió¦_¥ÿ¿ÄØ>yàƒv`xb¶ÅÀðuå¦ßIŒ+—†]ŒÉÀð æ~ïû?xù•Wùm@}¡Ô³RD&}ò„Wn`x¿–îtäåŽ<¢í:Yl4E?ã’Ÿ¤V® ¿zõjj¨%ËA¯üð¥—P€,E›”ês©ñŸÔ²¤Hɺ–o`x§“XN8éSîȉP|¿O‚êw:ßOÕ5ùèŽæ²´ Ÿ„Þ¦TÖ‘õ2ŒC&éïŠvÊË—“–eq]¸xñå—_yë­·yTš–çÍ›ó$¨’#RÃKÃrªe)‡BˆuëÓ§dMägѤw1X–²$¯Sk›(3âÕjYZЬü1aS*3†§MHbªÄ¨‘æ^zóÍ—_yõÍ7ßâ×U±yÓ¦ÉÉ 6Úa˜„TgYg OÇÖç| fH–í¤åŽ,I¥³±)•ul^h•ŸB`0ìºtéÍ—^yEÈT´¤5™îèyž'wéˆEJÑéÈ)QI'=G³ŸÌÑœnYÊ˺͉Խ~+`YZ¾–kw.ËT¶lÞüñýgBˆWôã·Þz ï V&7l˜œœÜ²yE1òlݲeë'¶\¾|ùG?þÉ[o½ýÖÛ(,Žë6NNNœJp]¶nùĦõz½WôcÆ>Ú°eÓ&ᑳ²!&Fà>x÷]½Þí½^ï'¯B¼öÚ)î+@…ѦâÚäwÈó˜q÷]wŠ»„âÒ›oF]l^;õ:Å9„í>x÷]BˆÈ»D`LˆlÊÉ x÷6Z`Y*}JSljDËwÝy‡â®;ï¸rùòÏÞy÷wÞBôzbùÌn6@†$K&'7\¿ar°FÖ×Õºz;}Ü´2žZªÏýxß÷©^¼ôe/ùwƒLdªŒÐ-×/p +«îJu¥±iÓ¦_ÿÇÿXqç·÷z½^¯wzùŒˆr¼h2iFú†ò²4nÑnä»e~íÂë@5 ²a2[!ô–ê¹ö6iGY‚íÅ:¯æKY‰ òdz´à–xžˆ+CyYÔrHŽÕã˜Kqáâ¥wÞ}÷šôzËgÞàY€ö"'ë¿x»Vý¥Ð B2úð¤ 5‘ËR—÷ßWœþÀðIJŸ­eB¯kýl›®Þ‘*-K¹PLÑ‚¦Ptå+7Mä8GÎ&aè…ò]ß´qã¦ÅZN–Ù]· !®\¹,‡UqdÕKdø ’sô¤èKN¦„Ra®‰h Úˆe®Fe™L5nYú¾<ýŽg• 3y8?VñIJççL¶Rw¤í'úoIQð m¥¬Ë1…ýJiÄYØÂÐ ÃÛn›”QÇ«W¤)9Ñ®&õ7¶,Ù¾L&è”u6È%¦ˆ,@+´Cù{ªe)ÊåGÖ-èˆo!OÚd”6Qb1TÎâ ða˜8I|Ë׆žv)¯¶iÓÆM›6^«ö/_Ùuë­ƒ=í\pAHØ£¤eÉoR^¿ÒâÛåý—–“ºæé–“õ¼'m.;©:i±Rµq¥ŒÏ/ÜËbȹ,µ£t#¤¯|Ï ã¾µ¾ç…rgWݲ<ء߿ÕO~H:¹º\Zé#äu”ž®J’.~|Ð ]®eð¸”w¬¿,çµT7×vðé"g«\–¹¤C/~ºêò½èH˼+á­él,ËläDmJ0A1€›Úax65u”§·);ñrG«)~¼š)—¥6?r?ÜPjQß·š½§âÕÕœõ…]¢ª°+LÌF†¾ælÃ̰K'„]Ф®R=j-ËNÞÍvyÿõÍT¥´¬&Éc`88–%8–%8DC¹,SGÂA ›¬&1Ø^/ôÙ VÀ5 å"II/­ÖíŽTÏ'æl‘òWz©9È„ëúš5!íjÝÄ„´«þ&ŽßéH)̤Mt³÷¨¹É¤ùòæ²VÖêDÊ UVh®:”­'­Ó™è$n¯'Ëo?ߨN“?OÐXgƒ@ÎJ(z*m’=: ÐBíHLË£Íù•œZ§_GIù¿&$!ð9¿B §¼”ud¢3!U} M‰7—ÏĘaÁ¦@šÑO“âW·Ž.šÒx^J&D§§‰ÒîÀƒ05ì’•Bþ»¢2ñŽ‘pC×t) ­r4OHííNAäÉ IDATBŒ: 1ÒèZ"­³¬_rØÕé$c%pYÍe©)„æzY€C`Y€C`Y€CXå²Ld¼ÒgBÑ%¦TˆWó’yµd÷4”“ËHz½~6ÙˆuÒáÒOä¤*‰+‰¤*¾/'Rñ¥åô¤*Wš ¡ZÈÏ0Î4[D=D"ã‰&¥¯K˜Õ‘×Wó¼d‚•NzÞ±uëúõüÄDÙó“©05)Ì:É”.ýeù“ù++,öÊï”n‡a21´ŸÈÈ_¥–Œ¬ žT8=¯× ¼¸Àå]iÏ*™:3V« ”sY†‰”Óºüh"=Eu3jˆÎÚQöá‰ü•ºü_ ísYÊù¿díðMù‘ûÚÑI¤Ó¦ó¥¼–ºrª\M:0Ãß Y.9úœ•~òäûwAˆ /É,lë²ÃÉNµM.KùîA/3첑Â.t­öÝj¢t-ó<ÏÓäe–µlb€²“+u9šµº¦Ëe™O— ÓkøÐËËËb¢ùCjS´hÖñ”T\ñGÏóâ¬(½^ KÝ¢É3â½D.Ë8—–”cK$si铪$×Ï«’<4O íèó’hStÉ[ør")‡‹.—¥œ˜R±nBJï¢ÉA¦ägéôwå'ryé6•¼-ºMj-X]bhC.iÖ(ë$îŠ&e"k¤|DIs=Ïó×–{R†21ÁG–ïþnå\–¤¿É\–Nú™è’—!²­Ó9™—N;<Ïó‡³ó)IS“Q !&dM±Èù%g"óM‘CŒrÂa“°²À&J*ÌDå)ËAò¢™×b ‚PNalq’rÎJ¹Ú’»ò;RxtRÃ1Eï»`øº&‡ž6q?¿²BNdÛד.׿’¬_Ž•ä]%tM p<%—¥.>*V³¾ —%8–%8–%8DÓ¹,=ÏÓæÑæAÓ.÷¬ô!„/gEѤëò¼d~–8—Vò ÃôT²Æi¹ŸTE½R’ª@ÛÑåFl$—¥ïwRëöDâ)?‹/Ë„§?C!2GhÖiÃóÄ &%¯AwÕ¾’¦'.s%«¦,t~˜z¿:ò:’Î&ô7™ËÒó²sY†aˆÈ´K;’•O…¹,¥F¾œËRÉÿ¥ÓMJO£#^òB„…. E;´i.mB-ß—S÷óxz^fß"ìòÂ0%ìòaqQè,@+´ÃT7ZäüÒåÿR¶+vOŸ"3‘¿RÍ®”«( jÇÃ.C]]SØ…€ƒqP.]³Ô2O“dÙËÒ&Ôò SäÔ2K‘ª0¢—%8–%8–%8D•¹,ejJ°bÈ&™ø('O‘67äÕŠ£(6¤t‘OËê¢ø¹@Û5Ãð•&§˜n]Rù(žçy9YX«[j"3ø¼Ä½—Ë©$7`ä…£yí€æB-ùf•”Â.p[×r ·Ë|5VZæh.˼›HÑe™$+w.-„ §}iÿT˜w¬Àúmi4¬³–¢™W‹…1Í4 öUtM¯»*ÌqIØEØ£-dÕ8CL²Ü¼®10ËË¢é\–*LžRf“i¹¸p€Fµ¡þ4%%“QÖ”w¬ùrpM_JŠfÞÕšQ@t`äµ£¾¬Äîçù"ìB`4tÍÇ-£—%8–%8–%8„çfFšriÕ´¹SG¨Q3IkRò(e6o{òÊúôÈ}EdÆY;†(m×Â.tÍY-£—%8–%8–%8„ç~¦ Ïp4wÔ® Χåç¼cNÉP+$ @;ÐÂ.äF£Ú¯ð¬Ü×5zY€C`Y€C`Y€C`Y€CxíMBÜÌ™“¤ ùi$Ù3Óì8®€ˆ,¸V«#„]èš#ÐËb‚"pwº™ÐËÚ]&æu)´zY€ ~% óç7ò¯kx@ã Ùâl± ðF(^ÞÜšA/KprY”¥¦¾îw© Ó¸†Üå¤Â:ŠNF€J¿dêRhzY€CŒ~/˼.>¯Âܯ«¡½7€Zë…° €€h4 —%8¹,¬°ì¢[î$”¸YÉØtØ4ì–~1üäEËšP÷ƒ§"…Tèe A/KÜ}tw 3&8þ´–|¢© zY€CÐË AM9+å¿ÓW`¸TØ!ˆÔl4ÊlR²Õ0u/)¤‚e PW£³) 1 @ݱ±å&r…LØ 55,סyŽ×±ÌQuÀÀpp,Kp†äNL)ì}– ±Ì…´”2# uÛzž'e³\àˆŒ+áæA…Mu}ÝWõ_P‘j7×ýÝ÷«:œeŽKêÒqË ls_þ*›X¥@šKb Ò.³müÑÒ²ô¥h\Ùv$äbÛ·›X–àXE* –¥Å!¨<¡ X–Ãá©§¿7ߪŠI1 Œ\¨/ÞzË-·Þz E#̹óçÏŸ¿PùnCAó`¼ëÑ5wqnî7) hÓO—÷-yûD(È=+S{Y>ùäS¡ËËgΜ9CiTÂ-kæ§?õÉôP§º^–¾¯Àf,9‰F¬y`XG·É`óàܹsçΟ¡Xêv)jhží7Þ¸}ûvaácÚ 7Tª6£e%I]:n`Yqˆmì¡[–ã^¯-<ýíï,////ãQ4Á§>õÉ›oºéæ›oÒ·©–¥LËx§Ó‰—Ë25ÒÖŽ{4šU5ºÝ¿»fV¸ÄÜoþæ¶m[·mÛ¦VY†ÚLcY*Y5òæƾ„þ]Ʋb’¼‡¬I‘´,ŸxâÉ3gÞ8óÆ”0ÀPøøÇ?y—¾ÐJÞbQ+Ѹ/Ù”:ËRuõôSO=MÁ¸ÀM7ÝtËÍ7}âŸ~|0ÒÆ²„Í›¦‚°,Ï;¿Ôíž§O%´¨ÇåG>üa,K"X–@Lb‡è,ËÓËËO<ñI*ÜäŸ~üc׌K,K(Ñ<°i*ɲõä“OQ˜Žó_ÿWÿå-·ÜºX–·y`ÓT×zVž{ñÅ%²UÂ(ñ‘øŸ|äÃQ¦«û•'–%T–%‡˜lÊøcòW§^ýÈ‘ÿ“"h 333¿ýѽ;wÎø²eéyò ™ËžçÅY2=ßOì*NÔíró@Y'u5¥yð /vÿîï(R=¶lÙrï=÷lÛ¶5þ‹o1_™—¬o}Ù²Ôl.£L}FE:n`Y1I>ËryyùÉ'Ÿ^f$8@ ùèG÷~ì·?Še 6Íe³eyîܹ—H[ #ν÷Üsï½÷DËX–P7X–@L’ò\^^þêá#…Oà¶ÙÙÛn›½¶|Ûìm·ÝVáÅÕZrU¬Ö|Eaù•“?°Š#†y£UŸ€fý|²9¨nç¹öZ<¹aÑ 5ëæÝyáýh®.ïAõ?Ù¢æ9°pùîchyEû{ñºçû…k´;g>öÛ¿}ÓM;–%›Ê:ËòÅ¥¥¥¥nᘞžšžž^[žÞ±¶ yoxþùúË/¼Px?[·l¹÷Þ{¶nÝŠe uƒe 4vm-Ë?ÿóÃ:WF6åC}®Ö+òÎñ++¹ üÊìÇ¿RdõÈú•©e~úÌ…Óo\,f_þ‹ÿüŸßtÓN,K¥-Ë¿þÚ× t®Ü³g·âþ={(ÿªîäeuuuuõÜêêêê¹sy·ýô§>yã7Ʊ,¡°,`LÛ»¶=+צþüêá#¹üÊFœJ³YÐÔÎñ++¹ üÊì¿Rdõxù•ÊòãßþþãßùA®ãþ=»÷>ð[ò”;ëÖ­Ë\ö}?Ž®ý¤e©ës„}Ù¢æM³A þõo|3×±¦§§öìÙC?ÊJnTÈóÏ¿·ëåÝwßõÁ»ï¬ñäwSjE¦57°,`L›¼ö–åòò™'ŸÊ‘¼ò¡Ï=Xõ oÓ5 yçø••\~evŒŠ_)²Šz¬ýÊx'§ß¸øøw~pú‹–¿ì;vìß÷ÏâX–4lš ‘e™×¯Ü³g7}*+¼;P«««Ï?ÿ‚}§ËÍ›7}ücX–PX–0¦­^KËòôë§¿ºh›¼ò¡Ï=ØH·ÊL³ ©ãWVrø•Ù1*~¥È*jüÊÄß׌ËK6¿oٵIJ¤y`Ól‚`uuÕޯĬ¬üÖ@­ä2.#×ËêËÆ´ákcY¾þúé?ÿêa›=ß6;ûÐC6Õ³Òl4µsüÊJ®¿2;FůYE_™þ÷Óo\ø³ÿ”ͯE0~e‰Uð+­¢±ªO@³>~eúüʬýŒ_)„˜½eÛ—ÿðÓ³7o5ÿXÏŸ?vu/.u3×y䑇ñ+KµEð+czzúÿå—§§¦Ì«ýìg?{ûí·).¨,K•'Ÿ|*Ó¯ü“?þ2~egˆ_‰_™£âWЬ¢Æ¯Ìw Ù[¶}ù~'ÓµüË¿úk\KÐñµ¯ãüùóæuyäaƒ—j‹àWºÊ#<<•åZž|æY\K¨ÎW¾òJƼöŠ8}zùØñæý #y¥À¯Ä¯Ìw2ø•éð+³ö3Š~eÌ}¿yëé7.½ûó_~¸¿xï³³»z½žçy½5|ß ÖׄãZ‚Ï‹äÃ[+„ßóDFÿûÜó”ËF°i£|4óÕ¯.v{ÛììC}®ù«òÎñ++¹ üÊì(¿Rd5~ee~eÄ—ÿèSæ_ó³Ï=y+—¯ôÿÉ\íÓSˆG• ‘‚a5l¾ ÃpuõÜ‹K¦.–ø•åï …ÐÆ»óÈÃ_2oûãŸü$YG^½zµ×»zõÚ?µ¾LÔ’ñ?…Ôgv𱥎°,`L[ZJ[ç‰'Ÿ2ìç¶ÙYæ¯â ñ+ñ+SŸMüJ‘UÔø•û•Ñòÿ‡Ÿ4ü ÿî{ß¿¢AÁ{½«ƒQ76åH´"Ì~åôô~eMí4pÿî<ü¥/¾=½|æÍ·ÞºrõªäR^ÕÙ”6} ¸#c–%€B<õÔÓ†ozèÁæŠCÞ9~e%W_™áWЬ¢Æ¯¬Å¯BÌÞ²õ³Ÿ¸Çð³~ì‰'ÑÇqæÜ¹óçCÂÿà‘G(¥âmL¨–ßéééÝ»ï3¬pæÿHIB°,Ä“f¿r),ñ+ñ+óœ ~eúüʬýŒ·_-?øÉ’Z^¼xéÂÅ‹¨äزÔí¾}䑇)¢âmüÊ‘¸;{vï6$µüùÏþî»?§<¡0X–0^-°y4Š¡‹å0RXâWâWæ9üÊôø•YûÁ¯\[~ð“2ü¾Ï;åÊ)“åå«W®\½rõÚ?5O[©1ºDÌÈ÷PšgWW ],^²À)„‘¹;{Œ-—Ïœ¹'°¼*¥²Hÿ›Yy’mc Á²€qOž0Žûk|H8~%~ež“Á¯Lÿ€_™µüJiyö–­†Ž–/½üŠ!•e¨Û0›DaËêh¬®®VcHx™¦Fé‡¿óÎ;= l“Úr Á²€qgyùŒî«Ûfg›Ž_‰_™çdð+Ó?àWfí¿ràJ¿üG¦yx^yõGhå²´¤¾gÏnʧ`[§iïΞݦ'âÌoP¶P ,Kk–———Ïh-Ëf»XâWâWæ9üÊôø•YûÁ¯Ô\ég?¡Že9†˜' ¿ÏЍH[¿rt£åLÂEÁ²€±Æ™.–ø•ø•yN¿2ý~eÖ~ð+õWjÎhÉ$<ãÆ¹sÚ,–t±,ØÁ¯é»cîhùî»ïRÈP€ ŠF»¥¥Ë0ÎÞ`KüJüÊ<'ƒ_™þ¿2k?ø•YWºëæ-ËÿñÍÔ5VWÏý£nˆ–¯öz½ ·¶|5^^¿n|nýP«ÓéÄ˾ïËËqÙzž‡š7Ö<04¢æaâºXVÛNƒ‘¹;»wß÷ /¦~õöÛ?›œœìMô¢ç+zЂ0èW˜R%ÙÑ,+•§îüåºTþŠ:¶ÐËÆCK!DS],ñ+ñ+óœ ~eúüʬýàWô½ IDATZ\郟ø îwÏØð±b©«Íb9==Eùän‹àWŽÇÝ™žÒ>?]Y¡¨¡X–0¾œ1d±ü\3],ñ+ñ+óœ ~eúüʬýàWÚ=³·lAA»XNOOS>9¿r\îOT–%Œ/†‰wn»m¶†âwŽ_YÉUàWfGAø•"«¨ñ+‡ïWFÿÿ,-Aˆóç/è¾bTxN…᝻c˜„ç§?¥£%ä†\–0Ê-0¥5'Љ–ÏœyC·“úG…ãWâWæ9üÊôø•YûÁ¯ÌùÌÞ¼ù ÍW¯^ý‡ø‡H>Â55 ƒ ¿yò$×­[׺¤¼–² Mz5r®5ÜZoʹsçt›0*¼p ØÜé©©4_õ‚Þ•+Wâz2 ¹¿"U’RåÙëõäøkuc†ñc«T˜ÔŸ£½,TêŽ_‰_™çdð+Ó?àWfí¿2÷#`þ“×N¡ã€¡‹%ã^ó($~å8ÞÃ3²ºzŽ’‡¼`YÀ˜òôÓßVCqÈ;ǯ¬ä*ð+³£ üJ‘UÔø•nù•Ñÿvݼ‰„Tn­ø•ã{w¦¦èŒ •e  òÐCŸ«¯¡Xk+´ŠUð+­ÚûUŸ€f}üÊôø•YûÁ¯,êW !˜„gÌ1̽v ‰_9ÖwÇ?á½÷Þã@.Èe cÔ8 ¥dUÃhRãWâWæ9üÊôø•YûÁ¯,áWšyõG?¾mvW†a?[r[)‡Úú«WûËR:6].Kß÷=)M›îHÓVwSáü…ôá$²Ì[’ÀÝQøùÏÿŸ_ûÀ¤:¸_›]]߯${RåÙ›˜H¯0“•!ã¨B/KS¾ýÿÐpCqÈ;ǯ¬ä*ð+³£ üJ‘UÔø•Nû•ê' ‡q†D– ‰_ÉÝ{v領¡*°,Ô3÷~%~ež“Á¯Lÿ€_™µüÊÚúW@–Bò4qw*Ë ö†âwŽ_YÉUàWfGAø•"«¨ñ+[áWÛäUHžî@õËF¹q&gRŠ_‰_™çdð+Ó?àWfí¿²¿2‚^¯×étzk¹){½^ê²H欔—Y•=/U°ÈËVkÛ`àg§SYÁwÇ|2qe„®’ 4©ç•<º SÞ„º´í`YÔØ6òÎñ++¹ üÊì(¿Rd5~å(ô¯ŒâíȵŒCë¾eè#pY†×ãKK]ÝWä²´Ðàî˜a\†A†ò»œ0µÂÉe~oãÃjk˜ wçø••\~ev„_)²Š¿²U~%!1¤±Ë2C €»P1X–µ4‡¼süÊJ®¿2; ¯YE_‰_ 0Š î@Í00F¼ÅfÈ]Ußa‡¼süÊJ®¿2; ¯YE_9R~e40<€'–{AгÈ_™Ì°,§fCµn! .‡L¾”¿ôÆðÂÏèîÝ Ã°ŸË2 äÁàù+L]æzòWŽX–@ »âƒ yçø••\~eö„_)²Š¿rÔúWfX–É\–†¨»ÎÂøµšÐpZS\øxŸdJžcðƒ0g…†ÒcËs;²00 ÊÆØwŽ_YÉUàWfGAø•"«¨ñ+ÛëWý¿’“h,K€ÊŠCÞ9~e%W_™áWЬ¢Æ¯lµ_IÌ ,´€ ç$ê…á@‹³š}×zâU¬‚_iõ©ú4ëãW¦À¯ÌÚ~åPýʵáaê`ð0 Bilcó˜~#,.“š F½Õ² ¯°(ªzº[rw””šÁà¡æºäÁà0`YTÐòÎñ++¹ üÊì(¿Rd5~åˆ÷¯ŒÂìxÖˆ ÐNa³ÜBÇƵ¡ƒ_ÉIVpžrþÉPµ#³.*r a`8@ÙØwŽ_YÉUàWfGAø•"«¨ñ+0Š üJN``Y”j(yçø••\~ev„_)²Š¿¿`:ø•œ$? (Þ*òÎñ++¹ üÊì¿Rd5~%~%À(6t±ŸFÕw³ÉSi8Pææø•0ò`YÀˆ7£m2ˆ;~­WÅ*ø•V¿ªO@³>~eúüʬýàWºäW†aÑ <½è/AЋ§ßéõ‚^Oš~§×ë/Ò,Ée]*L¦Ü©£y M ~%Ô–%ŒZóºÎ–~%~ež“Á¯Lÿ€_™µüJWýÊ~*˵|”Aôzr^K9¥t_mj6¢}p¶AUëuy ›<7;™H¡ä¼:yw¥ìÓ©$Œ¹nJ†áZžÊ0 lZïMµðÁQ`ÝšîÎñ++¹ üÊì¿Rd5~åHû•DÅ0¶ ±.Î\ËÀ®5;ÜãWVrø•Ù!~¥È*jüJüJ€QlèàWrwøÁ€c00À¢5;Ü®_ùŸþÓßÿýßÿ¿ÑòÛ?{·†2›º_¡ã÷ëþáÿ3¬<ÑéLLtJœ@XMÑ…Â'“˯,rÐ ï[¿á}ÿ?{o%GußýÿnUÏÒ=ÒhºgF#1b@ÛŒ€°±ÚƉÃcƒÄ‰ŸÀ1o/rìøñ ØœÄqr¼$Îq‚—ÏùÇ'{°±cŸ±Ìì±ø$–BH£1ûŒF=[WÝçEuWݪ¾µvuwu÷÷sƸºúÖv«êö­ný~íÆtïZ×eá+á+¨WG¾g HP–€_o¶¾+o:_911INAY¥:‡¯ ³çÑw¾Ò§p~q%¿¸bLON/qCbfÒí]™v¿-ÂW&ËWÁ'9?òòi"Ò¹.]·s·d‹ÌÈ‘¨v ;:qè']ØjŸ(^!§pa +¾Ò$¿¸:vb¦/×Õß»¦|%ð•òòµ_‰'hÐð}‘:]ÄããoÑ/%¹~^yuTøTœ6<æy;vTÿìà‚ ”%P»._E’ë+Ç'¦&&§Âo¾2öòð•!Ë'ÌWš…&§&§†sâ{âð•òòx€€MRÍõÓ[oïO¶¦ôÅ𘯼:Ú××Ûß×þy;ªsvpÁ`e Ô¨ËG‘„úJkp%Uä+{rÝŽ;wÙ±¥Å•奕òõt÷t ŸëqW\Z ¬“ÎÍÿ•ç’µVÀ]ª˜Û¿ãÒÝãÎe¸KšS¬|ÿR ±BÞ¹ …1EQÖt¥×¬É”­›ËöšK÷].€¸X3¼¼Ú]–ã®ç„Ë..;¿Üõªã¼ì’sVïôR¡|Ù´ÊÒ)æµÎ‹-jI­2Æ{âçlÊ|¥[yøJþèÖV?½¸ÿ‹û4YNNNMNN½òê«}}}çïØ±~}|g§Õ/@YµèòÅQ$¹¾òÈØñëèLw¤ÓíD”í]gv‹9/÷ŒåN¯ËZóf§O›º³]Ž9÷ЗE]ÅmfÊm\iÓ\(.Q6&»Ëq»´á’Ã$"Ý¡ìˆS)ÖqGm1û!0ñ™CS'’(KEI¥Ô5k2ý½=²ƒâ²*)Fó£òç\î+ÅùN_)³Nœ—¡s÷xÙ‚ö\ê+[tQ]VX5>¥S,“b%Ëé¨%f^Žš/}äD4½LD|z%ô͘_\=z|º/וIKó‰ÃWÚo²VâyÞ×2P@LèPŸ¾÷‚·­OŒY©ëúþ/hða•¾LNNþ÷¯~ÕÛ›ëëív|ËS˜bN‹Á%Õ”jÓu³rUu¬AX“þp) r®£…¶Dê”%Põg„8Š$ÔW¾qô˜•<Ø::ÓétG¶·»¼[ÐW:žžº³kÄùÍî+mŽågD2\´ä>ÎqNá+å¾’zÓªu8r_I‚¯$Ù>—ʵq"žk#"š^¡EuðfÌ/®ŒXÌfÒíð•Õõ•^wJ¼ /5ì‹ÔJ?½¸ÿÀþ¦YéÁÔÔôÔÔôèÁ×Þ~õU}}½‘OOË^0xe T·ËG‘&ñ•Ùܺ\o7•¿~\¯t¼ _)õ•º¿¯,?Åð•e‡S™¯´­“(×ND|Qã‹M¯*~7cq¾‘“§/×%)_Yg_ @‚û"5ÑO­&+üÏožêíÍ ‡—ð•¸e T±ËG‘†÷•étG6·.é´‚´w‹á+©š¾2Ì_Y _i~•VxZ¡\›6½Ê¦WYs49}&¿¸24˜…¯´Ï„¯À庮¾~:õÖ[/¼¸||¼Å«zjjú~óÔÈðöò÷Ä=NO ^0ÊzíÕÚZEÞWæz×esëÌrð•T_ÉÜbøÊùJqOrmz6EÓ«l¦ øÞ¤ùÅÕ±ÓCƒYÏ'^øÊêùJÖ2@4J÷€7ÝÛý±Ä¯´ÿ?ê¥éýû_zqÿþÊwu`ýúçlàLw^3åm—ô3Ÿ˜œœœœªä@F¾69552<Ü›ËscLQ¤M‰*ηEœdŒ÷IaL/cäòƒã°R<‰Þq-“ÐXÜ1¬*±fʨJw+Ž"í+ÓéŽÁ³ħøJ‚¯„¯$’å1/^d¹6žVøt-éŠç¹ãùÅÕ±3CƒYøJŒ¯ BõÊ 0çœ?òËGÇÇ'"¬óâ‹.$¢õÊ~$*Š“l&0Sf=ËZn«äyÂÌW^%â““S“Sá$¦1ÜrÛ¶­Û·m##Ž( …}a¢„d¢X´’¹éDÂâ6åhW–I¹`ˆ (K þg„8Š4¶¯Ü4ÎtÀW&ÊW:“YKN1|e=}¥ñ!­èƒí4]àeÃ-W`~qurjAˆk)»»á+á+AÂ{ +‰Æßÿ¯_>v©‹/ºÐÔ”ÒÛ´¾¾Ò1s‡ñ~÷Ñ«££“SSaÜå¡C¯OOO_yÅqtÛp)‚Vʈ¹ËG‘ö•étÇàÙl¯ÙÁWÖÛWrx¯¬¿¯4¿Ê©Zšé'WSÞ7ïäL>“nˤÛåà+á+¨§Þz+”¯XÉE ¬—Ü‚Iõ•ŽF~dxxd˜8磎|-àOOÏì{úé«®¼²²n[õ;¯ð• ‘@Yqvùâ(’P_yæL¾Ò¾[ ã+CœkøÊøJc"­ð­í+'VSKÜ+ŸøØÉ¹¡³Ö•¬%|e,¾î y»)œ³ï{ǯ 5¾Ò•6 H¥X£øJqæÈððÈððäÔÔèèÁ©éiߘžžÙ·ïémÛ¶f³ÙRë¥Jk@–Rc 3§Í¦Š1¦Ø^˜P¤«baÞ‡¯‰ÊˆíA Ž"Éõ•GÆŽ{¯#×».×Û_™`_é÷b8|eb|¥¹cƒm«'VÛ–¸â.Ýhr:?4Ø_ _ @l§²´¦Ozë—>d%.¾è ÈEŠ5¢¯4éÍåÞ~õU“SS¿yjŸoULMOO==½sç¥Ùž,¹´yFEQJ:Rg¤nâBÆžÊãZÂW‚$£  €Xº÷qI¨¯$"__9¸i¾’’î+}O8|e²|eñæj[ídšÇmš_\;1ërªá+á+Aòz nóã%ÈV‚ìùø[ã}å{ßsÝõïùÿšÕWš3{s¹]7¼oxû¶ uòÜs¿Mैû$(K †._E’ë+ß8zÌ{¹ÞuéL'|eb}%·y7ß“_™ _i|l[édºûmÊó‹+ùÅ•²“ _ _ Z©/R‹ BÀø•>rÇ3leûJsÎðöíW_yE:|î¹çuÁ஠ʨ´ËG‘äúJ+„¥{üJŒ¯¤Fð•<Ð…_™8_iœÃÁÔŠm¬%wN˜ƒ¯ŒÉWâ14`_¤býäfÌNw{w¸PÐ4M7ÿÌׇÅ?MÓÝÞ6vÛ[ñvØ¿ÿ¥ññ ÃÜ00ðÞ믃¯4³7—»òŠË½¯çŸafv¶.×$nLÐ@@Y‘TB E’î+Ç'¼|e®w]:Ó_ @U›n{Ûˆ(«¸''Ê/­æËœ&|e(_‰V 4J#QýKõÔ[o½¸¿GÃW’‹[”îjsûJ£d.—»ÂÏZ9r¤æ 7Ð`àÅp ’H¨´HÒ}%MLL¹-Nwäz×ÁWPå¶Æé+YeåM­Óm©ÉéüÐ`·‹Õ€¯ŒR$±}à1ZŠ?Ùš®3ƈè…÷{¯ù’‹/"øJa»fÉ\6{Åå—=ý̳nU7;;wèÐë[¶lN‡µJUUÔÒÙQEU‹£ÍEáŠê{ÆÕ²2è’F£,€!†" à+Çǧ<–¯ úmÜWQš´,[q[0¿´Rh _ _ š´}¨É¥úâþãããoÇq˜Ž=Ìår¹¬W,ïcÇ×ê‚A㌲Â8„Š4€¯äœ&&½†Xv¦;…^{üà€¨f™4žQVYÑÚÝnêüâj&Ý_ _ ’t+ó Óܺ!t·_Rqh¤1RúÑmZ!p‡÷ï?àQ¾Òq˜ÒݾüòËž~æÙ™™·j|þ…‘ªUýa · hH0ÊÚå‹£HcøÊ3gò‹esë„^{_ @ÔM™¯,Þ‰’–Åo'gòð•øJ4Y É­B®Oo_¹a`¾R9“—Ηë„2ƒàµ |%ukªx}êœ[º¾ÿÀK…ß{ýuð•ÀW—íÜéQ™o9ºº²ºº²ºbcÕü[]- šù§ÙÀ[©á%yçe³G*ŸØ«´P–€ï+Ž" ã+‰¼Þ ÏåÖÁWPÛ†Gâ+ÿ˲eÙM]|7\:_¢Ê ‚ׂð•T·Œ7µ4A¼}å{à+­-Ùí\.›uj9???7?_…ß:4hð• ^˪ *(ÒH¾Òû­ðt¦ƒà+¨q$×j¼“ieÏÑÅéüÒ*|¥ÿQÀW‚xoW™ü*ŸväéæÂüG~ùèøøÄ©·N½û÷~϶ˆV𼃉ˆcVÌJaš1[,KUUÅE¤{µÿÀKÌ%üá†õÒã-ß=øJsæÖ-›Ÿ}Î5¢åüüüºîîX9à+A3€Q–€Ÿ,¨´H#ùJ"Êç]•e6×Mð•Ô¸ rñ•D”&­“ n7¸•7¾2Ü ÑvúðË_>6>>ADãã>ö¸ãw¶fxG±¼ø¢ =ö ¾Òmf6›Ý²e³[­Æ›:¼.ê¾T(K€üŸÃ£i0_IÄ'&§Ý–Ïõ®‹à+щ zäî+‰qž¶”¥óNË/•½_é¿B4X >ü×ýò­ñqóãÄÄÄc?A‘MKô²`‡V(D(–ë7l øÊ¾Ò˜Ø²y³Ç{ãèÑÂêªù·ºj}X)±.WKa-5óëÖŸyg¶2¾T (KÀï9x9“SS¿ù;*µ/Ö'ïÙ00P~çÁW†ò•äùnøÜü¼%šuK4;)jhM×ß á+…3K¥¬1Bå4ÂÅ_ ª”%€fcïƒí}ð¡ªÛƒ JÔ°|h_)˽9~%ú²ÄÕâxøJžåK¡)“¿Òo¾4;}¥ÁÔôt•¬¥xW¼5>áö­8ľ2ܱØw>›õeiZJÑFr]ç¼ôçô—¼4`Vws‘œ´¬a¯øJPË@3°wïƒDä0•»wÝX-{PA‰–m|%esÝÝW¢_ @%-Ž—¯”=_{=Rô•λ¼|:ˆR”éBÎ+É·ã»ܵyˆ †ê‘ÞmZü` j—VTB˜‚5ö¤pⱜýòEtqZ×ùËÇ‚ûJÃZ^~ùN÷=±X*Š8­¨\5wÉ~9YÓŒØøø¸ÛÊׯ7£X2i«…¯ôö•Æê7o>÷7ŽHëùôéÓk×®­fO²º·ñe  QyÀД{¬µ=ˆ­×Ø0¾Ò­ÐW¢k @¥MOT_99³ØŸM—„òû4ÜtT_YY~ðP¾ÒQ8ޤäuùa1Öv+]á) hKyäÑñ‰‰럚žÞ·ï™K/½Äå{›²TÅÚ´•^ˆ+º )¹õÚ¥¢(ãÂ(Ks‡‹¹wÖ¯7f[ð—tð•’ÖÅÿò ¥,y½ï#ª ”%€FâöRIVzóàƒÅ9ʾRò4B}%ú¶TÔôð•=|i–uz?R&ÃWú-ØJ¾€ÌÌÎ>÷ÛçwºZKÿFE¼Äí Ã]/þ‹.¼Ü†RÂWúÌ”´-[6oöe©sˆgš®O“fÉhFŒ2šsn½ë6Tœ㌠gœEÿMBŸÔ(KI'¸¦¬ª4¨°D ËÇï+eO#_ @-šž ã+݆q™¦€¯„¯õä÷ÿ÷}ôñh-‰h¶Bké‚÷þøH:âÌ|a¾RÒºp>™Èé…c º®›šRgº®ÇÃê W„îUÞxv*úMBŸÔ(KI$M¹÷Á‡n¼á}±Hƒ KÔ°|E¾òÌ™¼kŠ|ˆÏ IDAT%ú¸Dm‚øJÎͧѰ¾’3€\"|óƒ;DwyV“Ÿ'”0u_I!¥ªÈà.(¹KÎ ‡æ/î\ZÑ0÷M€`”Ü¥†9ñ°«õ(ãˆ_iMkÚïýî5=þß““юѰ–^x8“Ù^ WÌÃUEÑUݺ…X–ªbíá[oK#¨rÎûûûÌ¡âüâ Ëân5SŒ1øJo_¹ùÜsß8r$Þ+¼.·UÊ@²xà½õPéñµD ËW:¾2Ÿ_t+Гë–‡¯ êÌ-ëëÚ˜õDíá+唡ÆW’éÊÝ{:_å>m|%÷,LÁÆWê~îŽÆNÈܤÅ+[Þš”6¢Žw<ÃÝýW]Ìu-­a`Y Z†»)K]'¢ß}×;Ÿøï_Ub-_xáÅóÏ?ߺ6„ËCQTU-¦ÜÑ]åV\K…Yñ+™àt®;âWš¬_ßo\êŒ1cñ¢š$Æ)šÎki_É]£ú†k?êpãP[T€fåA{ñð´JÔ°|,ïƒÙ|¥%ÜH¥RÙlvhhèòË/ÿð‡?¼wïÞôø>¼{÷î {‹ö!ùÌ-éÖuT_éÞJÄà+]§]óí4ƒ¯ FÞuÍ;ûúú"/>?úå—_ôómaÞ,Ü6ÛÏ·ûJ…¯”6Ìåk÷ý‡“'ßÔ5ëÜüÓ¹^üÓ…¹’¿ºH\Í;5Ê@²NÄ~´hüJî·(¾²»ºš¦-//ÏÎÎ;vìÙgŸýö·¿}ÓM7]sÍ5'Nœ¨Ò—––¾øÅ/^pÁ>ø š‘&"¨¯¤äúJ¿ÂW‚–½½9Ñ;ß~uoo.òJ‚[K~ãoÒ}¼gTs_é\»é+7o>×£¶uݰ“vô6Ò.£íá®RøJPO ,$‹‘‘‘×¶7Ú@KäÛ‘ÙW¢¿KDôë_ÿúïxÇ©S§b_óƒ>xÁ|éK_ZZZB=7Ôà+)²¯ « á+ˆëÖ¶¦ß~u=­¥ÉäÔ”tþúõýe-|eUÆWÆÙ‘Œó€ºX–ÇÈððèÁƒI~ÐHRùZøJ§í+Ñå-rôèÑøÃ?ÿùÏã]­ÛËà ¡Y×΂úJ+ýNÀ\:\ZÆv_ÛŸóŘ•Ü%%·#ú"ɲ‡ìhôh˜\:ÜÍKêÎ*•/]þ¤iÅòªª›1 uMÓ5Í,¬éÖtÁ6ßÚÓuEøÊDQ[m ‹ˆ¹SäÁåÐ?ýŽ1„Í:)¥³ éº®Ë/ ¾¿ Îô;‰ӄŗWVÅMkšvÙÎO?óìÌÌL´CžŸ?½ÿm[·²RÊEQTÙ4'.îU[[[y%—–™½§¿¯ÏX„sn\™n)§ÊÜÌÆCð•ÄÝûiqw$«yPK0Ê@âðx7ü¦Ý»nÚ½kd8èËã?ýéñwá’T¾f¾’à+ƒtî 4M[ZZšœœ|ñÅ¿üå/g³Y±Ø/~ñ‹Ç{ ·9ð¥»Ý4 äã++½Ù¹ËB/ºùÊ r-S/_ @u,äó¯¸ü2ÇïE(Μ9óúáÃUý+o0¾RvZcö•œ´.e qŒŒ Ó^ëãM»wÑM7Ç‘ŽŽÖ/¥xËúÊòõ‡ð•­ÖïU¥££££££··÷ /¼ù曯¹æš)á…»ûî»ïÝï~7îtøYµî¾Ò§88øæ›o;p©ÔÉ“'ûûû¥…Å™Þ#h}ôÑû·{òÉ'O:µnݺóÏ?ÿ¶ÛnûèG?*F7“²¼¼|ÿý÷?ôÐC¿ýío;–ÏçÓéôÙgŸ½sçÎ]»v½ÿýïooow[¶|÷Ž=úOÿôO¿øÅ/FGGÏœ9ÓÓÓó¶·½í¶ÛnÛ³gÇzZ@oP_ɉ˜çŠBƯ,ÝÚ¥¯…è~º ÿÄ‹¶wn±,]"ßEö•Þ^•o‡s#d¥tMš®´1ktR¡`…üSEUU³ðꪯpUˆ](ÎçºnÖ¡ªªziq¦(æj‰HïtIJôÿµ ËÒl‡µBA—E%¢ååßßSÇ–u!žéªËruµ nÚW( š¶ã¼/¿üòüüéh‡¿°°0::zÎ9Cæ%¤ªªª¦Ì]/o±µ÷VÇg$¯6Žq Á>Å:,}I_ _ @e –%€†!Ú+áq„³„¯„¯¬ˆM›6‰‰õë×_wÝuæü7ß|ÓmæSO=eúJ"zï{ßÛßß_É.>}úæ›o¾öÚkï»ï¾£G.//?þøãôGt饗޹-¨ëú·¾õ­¡¡¡Ûn»í¾ûî{ùå—OŸ>­iÚÂÂÂ+¯¼rß}÷Ýzë­›7oþÎw¾d7t]ÿò—¿<<<ü•¯|å™gž™ŸŸ×4mjjê‘GùøÇ?~É%—:t¨•Å‹Ë.ó•~·®g#6~¥Ë^—M‡Ü™øJ4Y š7s Î?ÿüî7·¸¸86v,Öà{!xÜü¢_É]ÿu€ÖÊ@ÃðÀ^¾ò3wÝ™Œ&ô•RÇ_Ñ6ÑúõëÍé={öˆ_ýøÇ?–®Á1ÿŽ;î¨p—Þýîwÿçþ§ô«Üpà Òܸ »wïþä'?9>>î±ò“'OÞqÇwÜq‡8šLÊ­·Þú…/|aeE>vé•W^¹ñÆ———[ÞvøúÊ a ø•²eÃæ‡¯-{GZjdddÍš5‘7šÏç9û¸ ’ör|_ÉWrn ÂæºÎ5kºnüyPЬ?sY]ç"hä@ce  1=xÐíÛááí##ûwÝ(ýö¡è-á+åJ¾2?úÑăƒƒæôüÁˆ¥nÊò'?ù‰9ÝÓÓ³{÷î wé¹çžóøö¥—^*&©iÚþáþô§? ¸‰ï|ç;{öìñ¶`?üá½WrðàÁý×mmÛÄWXÐw¾M¨Æ\Á²ÃÃÛ“b-%÷óFƒ¯4¿â+9y„/@[@IJÐxgÝÙ½{W4šØW:ºÓá|e‹w½_z饿ú«¿çÜpà æt&“yÿûßoúÁÑÑÑW_}uÇŽbùW^yettÔüxË-·tvvŠOPŽxvß&Û¾}ûW¿úÕw¿ûÝŒ±ýèGŸúÔ§Ì7Ö‰èþûïÿèG?*–ÿÆ7¾ñ‹_üBœsÁüéŸþéµ×^Ûßß?99ùè£~õ«_=pà€Yà?øÁ;ÞñŽOúÓÞ{²eË–¯}ík×]w®ëÿò/ÿòÙÏ~V"$ÞÿýŸøÄ'ZÕvô•ÜgAǼKüÊGg¹°r.<ìÛ‚Š뺠]”…ãÙœ»éHª õÒzfÐ6ÿì³Ï–Z»v-'¦éœŠQÿ4N«¥Œ½K+«êâ’Yx!oÝPÓ3³æ´¡UbV2ÆÄø•,@ÌJIJtû• ô•0­•Úœ™ÙÙtWFºì û÷‡ý­¶~•lŽŠQ5õÒ47§5M×u½³³saa!rUäóùÇß<ë,1ʪØÀêB¾é‚5Íë§¾PX]YY1nQUUU®Jo8U±ê xer×T¯ {éК@Yh¼³îìÞ½kdxؘØûàCå~%i8|¥[w:‚¯l­.8ç|eeeffæÈ‘#?ûÙϾöµ¯9sÆüVUÕÛo¿],¿gÏqHãý÷ßÿ¹Ï}N,pÿý÷‹?ô¡U¾“ëׯÿÕ¯~e¾¢þ±}l~~þÎ;­è ¢y$¢ÙÙÙ¿þë¿çÜzë­÷Þ{¯)_6nÜxûí·ßrË-wÜqÇüǘÅþò/ÿòCúP6›uÛ“³Î:ëþçŒwÞyç‰'¾þõ¯›^yå•–õ.ö'Üx|eù´yOï?8G­‹Íýsî¹Ó9é%}¹Z°¥m9#(KÐ@tw¯“Îíµ† ­»´´tòÍ7϶‡TÝ‘·Î“·,$3†ŒÁWÊS¢ÁWP^ t|³îÜÿËÖô•þ+¯ô%EéììܸqãÕW_ý¥/}Iô•DôÙÏ~vÛ¶mâœk¯½vãÆæÇòwÃÅ9›7o~ç;ßYùÞþÉŸü‰R“ˆÞ÷>›ÖŸœœ?~÷»ßŸŸ7?îØ±ãßÿýßËÓy···ß{ï½â(ÑùùyïTøÁЧ¦¦Z¾ôñ•¶ødQ}% YY\\û™»ÄUgÀ&T±|søJôƒ‹ìÚµëK_ú’c¦ªª·Þz«ùñ™gž9yò¤ùñĉÏ>û¬ùqÏž=±¼ úž÷¼Ç1GÔ¦D”ÏçÅ{÷ÚF:ßyçÒ5wttˆ£5‰èᇽî¾ßýÝßuÌ?.--µöUãï+]š†°á#M‹h-#¶D}%—ýs¥ÃÚÖ_Y¦ÿ^(K‰Æ/ëÎðÈÈpmBË'ÇW|e…´··îsŸ»ÿþû¥¦OÌÎ9“íüä'?Ÿ+*Ïn°yófÇœLƾMÓl¯¸¾ð âÇßÿýß÷X¹ãÛ_|Ñ£ð¦²×»»»ñX%<áF÷•Å¡4¶ñS’òf!ܧ41†µ,¬DV «æŸ˜{š‹i¦u]ÓÌTÕÜœÖ4Í¡§kš1rÏÈO­‹cù8|%|%‘€²h¼³î”¿î–‡ç§UeÙ*¾RØl$_Ùz=cEQÒéôúõë/¸à‚]»výíßþí¡C‡þæoþ&•’‡¿ôÒKÏ;ï<ó£¼Rœ¾úê«/•GÆ¡‰HLÎPŽã=q1éy9Žo'&&BíI[[š¾Ònp_ä¦;ÐT,..žÀøJÆýnjøJ-¯¬?~bÓ¦ÁJ~æ¼|e@_é1¶½,À¨Û´c¯¤ÇB¥¡ôø‘”%€„9ëÎî]7Ž<8<<<<¼}û¶mº®WÃ%T³|óùJ8n¿ýö¿ø‹¿0êkuuõ¡‡úà?øÐC­®®ÚÛÛ?ðÔk÷úûûÇÆÆÌ'OžÜºu«[áãöˆiŽì:ðìèôy<_‰û€–`qq1‚µŒì+‹iÂËg–•l)_¤Â­]".$m§©’aF…üløÍ ^ P¼³îÜýٻܾڵëÆÏÜuçî]7Ž W)Ìe úJñ3|eU8çœsÄTàF–p1Wø7Þ˜Ëåêµ{矾øñÑGõ(üØc‰/¹äœßŠZ›8|e)A,ç¶„Áf K¯ Ð:,..NLL MÀ‘@Z@J suûKâºnK@®ëdÌtôf„’¥y­è+ëÐ䢑 ”%€$âugdxxdd¤ž¡Zåá+[1 ÏÃ?<77÷³ŸýÌœã‘xÇ1P±#‹¯½öZñã7¿ùÍååeiÉåååo~ó›âœ]»váäFom*õ•AžWqŸÐZttt¬]»Ön,5îD7þLK©iº¦ëÆ\M× )׈gÂWÖê÷íò‘••G±•••|ä#£££æœÞÞÞ~ðƒ8¹49|%çÚøJQGGÇúõëC·F¶æ'ôIøJG%x(Dn–)'qt ”%€Äáuç¦Ý»ê4IJu}¥Ù…¦ˆ¾ä@d³YÑKþÃ?üƒ9ý| ô¡uÖYííí7n¼ýöÛ÷ïß/øøÇ?þ±} §µ²f'~_)¼õ)ÚA…Ðä¤R©îînÛÛàšnþÙ1ß š ¡Ý0âWêöø•æÌòÞ |%…ñ•±ýŠ a ”%€d9ëNõÅA•Ê'h|eWWÚ»ãÍW¢Ãœo¼±§§Ç1sxxøª«®òXê}ï{ŸtþáÇcÜ·L&óóŸÿ\ÌäÍ'>ñ‰üÇÄ9­¼ñÁøJ@å¤R©µk×jenRj)…ô\Fdü9|¥8¾ó*¨IøÊŠ~?дƒÆÊ@‚HdÖÖy<È*à+«KGGÇ-·Üâ˜é=Ä’ˆ>ýéOŸwÞyåóãU–DÔ××÷Øc}ãßèïï÷(644ôƒüàïÿþï]ÍJï¹`¾Ò%á(1Ì€&Æð•±t‡¤êËšøJkñªùJÁ,{ô$hTÐ ’—u¾R\|e-ß '"ÆØí·ßî½H6›Ý·oß¿øÅ /¼0“Éd2™sÏ=÷†nøó?ÿój<÷ÞyçÇÿáø‘|äw~çwº»»EéêêÚ±cÇž={~øÃ:t¨\¼‚HÏ¢Á|%§YuMÙÍ"ß|%M |e}eU[Ýòu³Ò Ö|¡ $„äeݯ”¯'¼¯lNR¥ç 3K¸ÁÛßþöÍ›7û.µvíÚ/|á _øÂ*ÙÛàGÔÞÞ~óÍ7ß|óÍUª74 ã+e7Zqº/ÛéÛ  ¶hbTUíÊdbj”Ê;*¦¯”´*ð•” _ @ãe $/ëNKúJ÷~îÂìâÚž ÁWV™¥¥¥Ïþóâß!– ùñ÷•õ©òâ …4¾’ˆ»Þøñžõj%X¼¸}ef§>¾Ïý ¬¬ÖÚWvwwÏÏÏ—ÏíСááá¾²¬$c ¾’à+ˆÐ”¡ ÔäeÝ ÑE¬¼pÂ|¥ë²+K«‚"¯¬:Û·ow¼$Z‹À¾r6µÆíæÍ¤Ûêä+¹ÏÊá+pgyyyzz:ò⌱ö¶¶°K­[×ìf ç+ÝJJÖñ•ccÇbþ¹ ²Pg’—u'D±òÂIó•/†¯,­Fó•Hî;v<üðÃ]]]¨Š¦¢ñ•žóãùW î_¾€Ð,//ONME^<š¯ ÓÄà+Å(º„ñ•vúz{£·Ð4P–êIò²î„· n”ñ•+K…b¿;¤¯D_Ú›+¯¼²¯¯¯­­­££cppð†nøçþç^xaëÖ­¨àë+gÔ.·›·,]¸ô®—Ʋôo%à+¨Éô•æÏ¸|¥ïL¼å'€¦±,Ô“„eÝ ÑE¬¼pb}e_vbrFºÄÂÌb׺ÎоjOžzê)Tt“ÊÆW.)®z¢È’\lFÍ·ÀWº4MÜßE&ÀW¢Éõ¡Î¾Ò=ýôôL.—C¾ŠËWr~ìØ1óHDŒXe'MÞ 2Üf ¡À(Ku#aYwBX„Ê 'y|e&“v[han ¾€Zµ4þ¾r‘µ-)ín+Ȥí¹wjç+]V_ €;KKKãããÑì+_94t¶gƒX8"~¥ãe›so·Ó¸0Á(Ku£1³î4ÿûà]¯îòÊR¡½3_ @M›iüJΗTß!–¾¾²8¢Ç˜`¥bŒ8³Fïp¦XCsTsšsUU¬u2…1cÍŒ1Æ/>Þ+œsnî€.¤²eèrj Ÿi^M_ÉùÀEQÕÒ_±6TUM•¦SªbU©jJ:J‰óUó£¢(ªªZç‚)åçˆìƒªÃ)é)õÿñÕ¹.3kRUUU‘תyvÊ1çe¢Îºx˜nN4ÍvÍ1Z\\¼^VžæææqÙ¨}C¨=™u§…òíx¼¾º¬yxÂøJˆ¢6;î¾rÆÝWQ_¶Ã宕'²é —o'”Ó¬¾¯D“jGr|%y*Ë™™éòò’Ö*°¯”Þ¿­à+‰èØñã®Mw_Ÿ9͈ÿÑ(f-`_†£â”%€:ЀYwZ+?¸ç»áüÌÜ"|%µhvÜ}%y ±¬‡¯ °òÐM|%hròùüqw{勪ª1úJòT–¯¿~Øãä<¬¯äå÷]‹øJ¯·ÂÈ;ˆe  Ö4`ÖÖò•F—¼áœJ-Û;Sð•T¿á‘ûJÏ!–¼ÈÒ=~¥ sÌ- Â1é˜Ë*ÌŠ_©‘þ[Ô?…Q)–%qÆŒø•Œ3[|?ÆííçÂПiψa”¥ßFÝbD2…'ÍÈ€ª¢ŠmQmq-åÑSªj†¹TUE)S˲’[Ç-–¥p98cYšgGQÄš·=9§RîZ™Dsœ,cîÂÂÂØØXô'ùT*“ÉhBXLâܼ>1–¥ªšW#ç¤(¶KËÜ1EQr¹¬R =ɘP†ˆˆfffúûúíW#£RTJrU“L6ÓíÌØnâæó•D4?ïúV8YàlèPjL£eÝiE_ID2ei-˜Ÿ[jïì¢`¾Ú€ŠÚŸ2_¹¨´»±ä™ÎT&òÊ·cþ‡8gŤÄ9³§â)ÙHNÄŠj’sbÌ–aFWJkfŒqÆô’òãŠ^œ6’èâ~é _é¦M_i*HEµ¦UUµ+KkZ´]Îô;%e)®JôMÆI÷ ÊÒå sßù\çre)øhç“s[*Àåú´¦ÛRî,,,=z4úc|*µfÍçÁŠÊRH¹£ —–3ýŽp;·ö¹›Ï;:¦(V`þÆáׯï_︭,:ÌGMÂWx¼e €ógU –4ZÖõ•ö$<¶W—µ3sËð•T½ý)ó•Üë•pND}ÙÎ`w}p“¾e¾^1'OmßG»ªËÂÂÂáÇ#/núJ'•)l#bOO[™™©…”J=øJ¡¼5ãØ±c§ ÓÕ%†¢4#PŠ1*ÆÅúsžCD­Í”%€ÚÑhYwZÚW’MYJÌÏ-¯.­R _‰ç*k„l¾²kIiw+éLe:Õw}5}¥mT¥KŒKøJÐÂTâ+ÛÚÚä¾’ˆÈJºâ±æI6›õXÖŒhIûJ¹Âlj_IžC,{s9{Æ7ÄŒ;Ѝ))wJdû²ç@]Á‹ájGCeÝiu_iÐß—˜œv+?7‘ïÝ´¾€ê6B‚¯\RÚfSk=J[‰w‚Ý}Œìñ+-©AfüJn_)¼IÊu]-mˆ3Æ+>ý+ ×K’‚s›Åй£y°½(.dñÕ½­Tôt IDAT ’¸Ü7h¦åt¤ÛQÅx}Û|;^|Üñb¸ÛËà)÷X–æâLx«—ˆÄ$Áˆeà§Ï5–¥h¸¤ÅÔTÊõÅð±,—´.„>`ºu²†‡‡ºõö ½½=—Í® òÛY¸ŸÅ ‡*^™e/†[ÓæU½yËæ±±1sqÆóJ;tèõ;FÈŒ_é¸]ÿmƒÁW’ßËÞÞÞdÜ>hB@‚€²P#*ë|e‘þ¾ì™|>Ÿ_r+?7‘ïîK—Ï·ùJÎOL:¶Âmý{»Ã<{¦ƒ—?TðôY~,\þÐÂuÇÞê\¾Ú²L§²þ|i˜©cÓ®OnÖ7\€Æ<º¹›2‰U^?Å*’»¾¼\àÄ3goüqUa)…õvµ÷fÚìG¥»*!ëpyÙ9·þë2ôF¨*É®–?> µ+~e›( ÚÈí§Ò9èÏ#çµ¹cŽ£àN¹æXCùѹU…­&¹ìŠv}|Ii;ÕÞëqÿöõtï8¯|^–ùƒY;PüŽ;¾p…L1¡ ‡«Ü)Œ;T约›—¢Rº„8çn Iì·ŒN¤FiÇ‚úJéz¸tWïX–.ñ+S©”›šl ¶9bY¦Rå«‚²¬Ì¹¸Æ²ä¶ ^^âvÐÖÖÖ÷hºÕ*šµÚžžž;v¼úê«¡Ž«½½½¿¯Ïñ;帄p´b,KAYêz*%W–æôÖm[?neïaŠ /Ù¾}Ï\uÕfK¡‰˜™^‹9~7Å}$â¬õ|%÷b™NF®pøJ4ðb8€ÑhYw‚öîôùÕWô÷å<Ê–´ù‰E§G³»!Î%j¾²–¾Rò@å%tà+)¾’ˆ»„°´ÖÓ—í´U…ä÷&5çAZ¢Ùáa^Ç®“¯ÄØpPº»»wìØ¼¼é++ê²6Rç ¹}5===55Íe‰†x ÞEËů4æxée ïÍ9ûZŽWÃ…—»Ë‹‰0%‘/‹_ˆ‹ÃW‚e  4ZÖ ÿGò÷•DÔ•Ig2."€ˆ¨°¬-ίÀWú=ÖÓWzªºò˾2‰¾òT{nIéð¸‡6tÙªBr¢cñ•.‰È½´`2‘Ú1øJÐÈ·–µô•DÔãÑòµ×^#ׯš¿ï\þƒä¼G›ÛWÎÍÍÏÏÏ»Õg:6r…‹Z‘3³ì8bTŠñ+ÅT<ö´<Âó{@2Á‹áªN£eÝ Úùw}‚`… úúJƒs‡ŒÈç—Ê}¥ÁâüJªCiëPá+“é+¹Ç…á<*øÊ†ô•™N5“NYUá}³»˜ ûÇâKÇöX–\xÉÜ|…–sq])h>×s›@(Û1.¯òÌ Ä½/i6áO©ŒW,KEq‹_éˆei›vya\Þ%·½ΘíÅpñõ[—iàëæÄ—Áñ Ì«@UU·X–mbY:[Ex1\czù5—Ëå.¸àü—<Çße2™ÁÁÁååeß[‚ !ŒKµxPŠ¢˜W#çná Ä+¶¯·7—ËÍÌÌë"Þ2b4=3³ïég®ºò FĉËèq·6¨}%½ôr˜!–5îˆñ• ±@Y¨: •u'hï.@Ÿ¯I|¥A_îèØ EOO,¥:”µýiøJ©X©³¯$gÆkÒ–8¾²Q}åÐÆ5VUX>‹J'ØÃϱ’’äâZrÎ+yNÄ8WlA0Íif­P×m>ˆ“Ü Ù/]2?P;“¯äòá¨ÌEZ™¾RÔ”¦ôñˆei›B"¦Ü•¥¨#ƒèKà×{*K댸+˱,›ÖÅX–Š&]¤§§çüóÏ{ùåW¤ßf2ãmÍE˜:®ó’PM©(JÊŒeI¼-eˆ8-^¥\ç—]vÙ#<â¼Kÿ˜133333³~}?#ƹí}e^Ü/Ne!-¥}–¦÷•Þ¯„›C,¥]ÉšÜ/h3@rÁ‹áªKCeÝ Ú» Ðçk*_ID]™Îs†Îò^´°¬Ÿ6âZÂWÚÅJr}¥x:à+šoÇÇWQ_O§UÞ7{˜‘†Ò+гpئI—ÌO€¯Ä7¨1ÝÝÝçŸ^ù|ÓWVÔeñ¾IÅX†elÙ²Åcѧö==95U¦ÿ$÷‘ùƒÄË÷•ÇŽ÷x%œˆúz{m¯gË^gŒ)¥?Çkâ®A.#ôHP–ªKÓeÝiE_i”/µô\´°¬Ÿ_‚¯; ¾ÒSî¸=­ÁW&"?¸¯¯ÚЕI§ªì+å+,ËýÍ%Óð• ÜZÖÂWÚ™þÐÀ[YÑÁƒ¯ùÿjÊJm»×¬¾rnnÞ#K8årÙ®®.SPZbR±«I1N%SD\Ï MF;²ñ”'ЖZŒ¾’;N€Dÿ|eý}å›í¹Sí¹€¾rhãšJòƒÇ¿’‡lšüDy¸=¤jøJü|ƒ:ÒÝݽiÓ¦J»,±^Æ;w^ÚÓÓãQÀkÉ]~Z0~e0_™M§Ó_ €'P–ªBseݯtn¨ÌZº®|ùtaþäÒòéÁWÖÏWòÒó™ì|É´ |%ÕÚWΤּѹÁEVR=|eØÂAâWÂWPý.KÙeÌìAm_ b„D5¥¤TëoÛÖ­ÞšžþÍoöMMMyþ”Úv¯•}e:îëí3BSªŠjþ¥Ô”럅êNÊúS˜ù'F¼¬Éˆ»Ä”%€ªÐDYwà+嬥ÿÊ—OæO./Ÿ.ÀWZN¥†¾Ò]Öȶ_Iµó•‹¬m&ÕõFç†ÙÔš€÷ïІ®šŒ¯äÁ[ƒðA3ÉåüF]!|%Õ¼Œs¹ìežA-ɰ–O==UÊ!Þš¾òÀK/ð•gW<6òõÑø[­bYˆŸ&ʺ_鵡s‡6{3Ÿ_ ¸Þ•ÓÚÊé‚ÚÎÔv¥c­ _éRíUð•þ|e­}åŒÚµ¤´¹«”Ÿ²ÈùvÊcÉS¶¸–œ•VÂŒœ²füJÆŒÅ)Å—L¦$•Ï9È·¶ â. ².“Á¥6ÖµQU\Æ"àŒALŧ)!®¥8MDfŒK"jŠóS©T[iEˆe© –eEÒ$t,K³&ÕTJQUéâbpR·Õ:cY ?@ª¢ ›³N®ªZ±,UUM•¦ …BAÓ¤[,¤4é¥+£/!1–¥ÎyGG»üj\Ó4ójÔu]缿¿ïÊ+.æÙç˜ËhL?µï™íÛ·mß¶­üöjn_i$ŸŸŸ÷½>ëuc4þ@Ëe fš(ë|¥ÿ†ÎÚx&¿xtìÍà+×V¸¶¢­,hj;SÛ¥º’Ù⃟¯,œ)Nhgx[¿Ý•ÀW†ºà+Iâ+O²µi*e²úb%¾rFí"N¥•á‡L§Ú×ÓYq~pß–¤‚ø•9?xØ£ðð•ÞGÈWâw)¹\nÛÖ­¯>ì]ìµ×MMMoß¶­·7g´“Œ±æö•ÇŽ?vüx:<«N]ZøJÐ@Yˆ™fɺ_tC]™ô9C'&g\Òˆ»®D[áÚ '¢•…¢hPÚXÙ˜3.ÍSÈË­™vƱ4 VÆ2fß.#ÙXNæa‹¸s«Æ3c½ªWåpÛSX …{+*§ˆáå¢uµ =rSAçgVtbš»Nòxט—}’Fög.²0¬¬q|ek.ê,Âݘ¤;Ϲüº®££%Ö¶Tê3Ϩic¢“¯¦ù*gÂj—]´´ÄÌ¡”ûÜo}­%½vèÐk‡mÛ¶ÕxO¼É|e(YI‚¯¬u–ój‡€¯ÕÊ@œ4EÖøÊˆÏðý}=™LÇÄälÙpKÇ);þ«fçË?hZÈ•Çï+cºHü–åñWcÕ}¥ÿ¹ãqܘÏo_OG_¶S˜‹¯Ôåq-­:ÆÐàÒ¸WÆœëbB,Ëb¬OcAÇC±îïÒ­Z˜°OL2ßµÚÅø•ŽÚ‡ñ2÷Ämû¯» 7b cF>åÒ`(1b ‘õ×,,Æ.LÙæ+â´¹ˆªªf(LƘ-–¥8` ±,ÃݶùæWŠðª2éºnE^«)5vÓb—µ’¦(šp Xq-S©”ËR¶®iécŠËR•sÞÑnåTSâef‹ei.¢ë:/Õ'NÄ.Û¹sffæ¹ßþß Wà¡C¯:ôú¶­[s¹l.—£Æ÷•aeegggoooÝ|eÕ7&T(K±ÑDYw¼;dð•®+1†[žÉ/ â¾2ÔÊá+ƒœ÷¸«ËõÜÕÍWf:S}ÙŽLgJ˜¯ °õÈÃ!m®&LÝV2JT÷[ÿ†¸ßžd³Ù·]rñó/¼°ü¡×_§×)—Íær¹­[·Púʹ¹¹ùùù€1+M:;;Ϫ˿.ÀW‚&Ê@<4QÖï|¥ÿFqéà2ÈÎÃW†,_µz“à+3©¾lg¦Sµ/X__D/ŠH4"|% DÌ]n^ª˜ÃuÆôÒ4cL)¸ìëë»ôm—~ãÈììlÀÍMÏÌLÏÌzýu"Úºe mݺ%ɾrnnnnnŽˆŽ;¡zÓéôÐÙg+Z^-Ĭ4ÇVQ[›Ï;¥ª©”5üÖf›ò¿‘"œA1~% £°cæWŒX\þ¾Ô(KñÐ,Yw¼;dð•!6Ú•éìÚhqa=ð•!ËÃWF­Þºûʾlé5p^–*½ò›Wî+]6_ @6Û³3{És¿}>¸µ412›ùÇ·lÞ,k›¸ïO€ô~æ.I±-â²VFH{g ¨¬°rÎÞ´ÉxÜÈ™^Û¾+ÆW‚&Ê@ 4KÖï|e¨çweÒ]gwÑÄäì™üR~q)Ìzà+C–‡¯ŒZ½uô•}ÙÎLg*“V]V^s_YÑ=(ÙÏGu!~%ó?I.iޏK—gå›§`6–ʈ1kÈ#Æ‚äæ-[Þš”­–³2>Ìšäö*µj˜:ož—´\’äœÅwÜýÚc.m”m,-­u FÌUaÚŠ÷*æ˜v ™j»Ug²Z0u祗ÌÌÌþöÿ>_É©9üÆÕïÔÕH³¥Óé³7mªñFky˜ð• f@Yˆ¦ÈºãÝ!ƒ¯ µQÉÊûûzú‰ˆhbr–ˆ&¦fýÖ_²|t_É#Ö|eT_™éLeÒ)ã¿¶25÷•Brf­°˜™„—i #Žn© ›TÑÍÂÅŠÆK«”‘OBya£ö2ö¼=ŒÉ ˜{nxªró¤ÛjÀ[²2"Ç—²ir.ÿè¶x@e •æ¢,=¤s°Ú¶IqE¼jëÚß vnT¸Ùù9çŠïN‰¢Š»!$ŒâœR)ùkÅbÂ(®siˆV”1fÞÏÙlÏ;Þñö±±cÇŽKj§®FšMÌ _ @…@Y¨”¦Ìº_YÁù Vêïë1ÿ{&¿”Ï/óíc0á+C–¯ÈWFª øÊÀ¾RÈúm›v–¯ÛøÊç”»ÄÔýŽÈ£Jy˜£ðÝyyäMv<‹ƒÄwOª¸n7™. ²T˜BŠuO‰CDE9÷Üs²ÙìÑ£GøIêÔÕâÖîëíÍõæ¬ð ÂûàÌ6ЕT1¥`ŠE—­¨6élFUÆ·!Ó˜ÛþŤZÝcª”%€ŠHlÖуG†‡ãèÁW†Úh8ýÑ•éèÊtÓý~+ç¡m‘t¾k„«+¼—£«äýbÎãX0Ìðè.’G“²Î£nˆS€Cß~å“å+¹ËhHøJêHˆK”1Ë@ÚCØeV°ñ¿Ö|E1Ç~r²)Kcf6Û“ÍöLNL;~¼òpñÔZõoí¾ÞÞ¾¾>³âŠí‰P‡Š¢(ª\YºN+öq²6})_œÍ˜dènÅ5‰{Ô(K‘¬;{KÚtïƒÃÃÛG,á++8¢p¾2œÑƒ¯„¯ Uçð•ùÊPýQ›,Î+Ö¶µ‰¥Éм×]ÓÚ.F½°Pû3^¥5÷ööfÒi3fe€ßåF¿wÐ~€úe :µÉºcJ"=èaHcïžU\€à+C®¾2äŽÁW†»¼á+#VW sê"áA”‡8<¶‹Ïå qÔ,…‹kò(bV„D…+Ü̺.ĵ$.¾Ô¬©–«UUõì³Ï6¦ççççOÏŸ8q²ë-“Étuu¥ÓéL:m‹&Iv÷-d's{1Ü-~¥jwi~¥ªÖâŽ1³®/}Ûr»3VÁnøJPG ,D¤JYwb”£nß¶-Z÷¬â_r=ð•!w ¾2Üå _±ºà+¨#U÷•±,­ÃJÀ£p.¦ß!feN©V㣠ú’§¬CX׳nm÷ÚÁÁA"š››7ƒ]¾ùæ›I«7CPÓýýýŽoeIöŒê¦[d sK¿ã>­˜UU5§ÅX–nƒ…Ó^c‡ÃtN¨9P–¢P½¬;££½×œƒ_‹ª,}úo1•¡¨Íð•¡v ¾2ì•_õ"¯ŒÞ˜Dô•Õ9_>ª‚oy· Îqy%ňl¡%5Ý*¨ºý8ŠW-gŽýÅp!ë·â[€ _q·ß+ƈóÈNkíÚ5kJNpㆠâ6 …‚ЪÉׯ» hjpøð• î@YˆBõ²îŒŒ ÓÞDwc*C!5GÔ•ÃWÊËÂWÂWJÏ|e,¾’—is(–52–‚c–fe;àÈ.)`/#ÃxñSPNÂ;³Œ\ÂTzNK#*ÚcY ‹ ýÞriÕ ‰–ãÜœ}+òT< )œqÉÍ£éL•Ÿë Ê’lú’De)¾}l»díÉ‚Ìp–L×­-Š«%8­ú_ÉÕܪÔ(K¡©jÖ3öøPï†ÃWYy¨õÀW†Ü1øÊp—7|eÄꂯÄó:ˆq`8ZP &ɹm”œùÚµbO/c»7Åi— œÌ “‰£Å1ŒŽ¼ÒÖ˜<ò&é:î‘òÖ²I¶ˆö$ (K!¨^Ö7F†‡GF†‰hd¤"sttÔûÍôØûo1•¡š#êÊá+åeá+á+¥ç¾¾¾$„†_‰{¤>õ_ Z(KA©jÖÛªF†ï¹Kº6øÊåá+åà+ýÖ_ _Ùê¾î ö$ëªs阣=ÿ²®«\ˆ½h°),n L©êziq]HD.N“}Dª8¨³|¨i]2ðp#¤§päb%0Åè*ƵŲcV¶ q-ÛÚÛ¬zãW Õ«(Š"fé Pí¤/á+A2A,KA©YÖ‘‘7ûé=̳œŸþôá*??ÀW†Z|eȃ¯ wyÃWF¬.øJøJPW0¾2Z­ñV8U_ ZŒ²ˆdÝ ²Á£XÖ¤S _j=ð•!w ¾2Üå _±ºà+á+A p‹kIdåãVìñ$m×k€_æ•3ÚŒk)¤ÿ&R&ì—/BÄåÍN=á­±QøJÐê-'ª€/µÉºSá> o¯m§¾2Ôzà+Cî|e¸Ë¾2buÁWÂW‚:Ò¡!yo ÎkÃÂW6ÓU Œ²àOí³î”ãýZúððö‘ááƒ_+ÿꡟ>2i8|e•‡Z|eȃ¯ wyÃWF¬.øJ©¯äp— &ÔÉì¸Ä´¸,F<äœÆHˆÉè€2ÕÖ&]•j^jŽ0看÷¦‰­cDªäÎX‘î>:5H…ÚÖc_)‰kIöX–ªË2¥¦ë2ëӥ˂… Ý9mì«4-e À‡šeÝñàž{¾î]೟¹«†U_j=ð•!w ¾2Üå _±ºà+á+AiŒñ•»T¿Ë£Ê‡ _ (K>Ô,ëŽÞΔJ¾r÷î]5©øÊPë¯ ¹cð•á.oøÊˆÕ_ _ ꈑ?Z3F42V¯g¢*ŠïŸ±|9Š¢¨ªõ'âØ+øÊ:&Þ ”%/’uÇï•ðá‘‘a•% ÷a»„𕾮$ÔŽÁW†½Òà+£^$ð•®@øJš‡DÚ'Þ5_ €bYp% Yw¼_ ®M$Í(}eøJÿN-|¥ï3|%|%|¥oýÇí+ñì Zi GιŒR|GYa$kIDŠbMëÂZUlÏt36¥®s+,&çn΋ëºy‡–å4÷ífT\?AʃUÅ@“Œ1&T£XÕbÌJq¾¢*â|žfë q‘`ñ+Í%à+AcQ–\©{ÖØëýJøMö—Áwﺱjû_j=ð•!w ¾2Üå _±ºà+á+ðë¿$îÀèøNnK\0 ™€² §îYw|ÇxîÞ½Ëû•p“‡*}7¾2Ôzà+Cî|e¸Ë¾2buÁWÂW‚äê@˜þ |eÓ_ ¼@Nݳîxñ¾©,ßÎðð0ÑCñ÷–£w á+¥ZøJßgøJøJøJßú‡¯ЍÅ{3”^Œeë¶»EדVQ0bñÕ$ÎhH ,H¨{Ö{îùº÷+áwßý™ò®RÀA—¡úct á+¥ZøJßgøJøJøJßú¯ª¯Äs8h9¼õ“ ‘™7#æëQ ï¨pñ¦³>‰‘0ÎuÉpâ¤s1n#÷ÿ«¹GóŠeiV³™b·—®ÓÂ90òÉ'ü‚ . ,8©{ÖïwÒ)@ÍááíÃÃÃDtã ï«`Ô|e¨õÀW†Ü1øÊp—7|eÄꂯ„¯À¯ÿÂ)èØOι©älÎÒvO)òngÖââ}§s®py#£ëÖºì…ÜzÕ„ç·rQèðŒríèœo¨â®/¥«bèeäÛÍ”%'uϺãýNºwÍÉ"9 IDATü‡o‰)øÊ…á+á+}æÃWÊËÃWÂWÐø$R?ñVØh *¾4:P–lÔ=ëÎ=÷|Ý»ÀÝw&qÝVøJÿN-|¥ï3|%|%|¥oýÃW‚f£6ñ+Ýû/É»ì9|er7_ j ”%õͺSù+áõî[ÃWJ;µð•¾Ïð•ð•ð•¾õ_3_‰grÐDÓOnŠU\Y ˘¹ŒÂXi’gâ}ª¸Ý¿áûVÜ£#È3ß aeïvK¿°/(®eØø•ð• 9€²`Q߬;£££>´úi"tßÂt™á+m¥Ž=ytì¤ñá‰_=—À§¹Ä>fFXf||ÊãÛ®®tWW&øã]ã>¡×ñz¸æŠsŒ•¼ëÊsÊÖ _é[Ïð•ÄÛñη:§¹Ý܉w”-Þ%gBËSZ†[bÎ}c<®›SzÄåq<ÃâXÔÒ­JÍOŠ--"ßâW‚VÊ@‘ºgÝñŽ¡92<\í¨¬ó _)íÔ:ËšòȘ%+û4×ê;_÷Q?ùôÑÒÄ]sÅú¾Ò¿žá+ˆ·ÿ±KÔ¤¾4P–ŠÔ7ëÎìõ~%¼Úï¤WÖ%„¯”vj­iÃT>ñ«g¥Ãßê;_Yý£6Äå“O vŸ3¸î]W ¹l¾¾€xû/ˆ_™ÔzHdEÂW‚:e €¨ÞYw|xÞýÙ»’õJ8|¥§¶8ýÄ“Ï6˜ÊJÎWÓí|emzìÄü؉ù'Ÿ>64Øý®+†ÎìÙÀWÆí+ñ|š—êé'û‹ËAÞ+gÂ}'NÛ‹q×ÎsûZíomWrà,ìW¶ùân«ìä6Ò@ ,Õ;ëŽ÷Ök¦¼‚.!|¥´Sˉè‰'Ÿmœa•‘ÏWÓí|eýŽzìÄü}÷ì~×gŸ3¸¾¾€¸û/Ñòí° kp×sö¦•cœ¨”u‡ñ ¶ŽëzP#ÄÁ ¤2ƒ®ªŠM|%¨;P–êœuçž{¾î]àî»?“¨þ~˜No‹úÊÆ”•_ _™„£;1ßý/ v¿ëŠMç ®ól à+á+ÞI`üÊÖø¥7Ž“±ÆªIøJ , Õ©oÖïÒ©ú14+èÂWJ:µGŽžxâWÏ6Ôkàõ|ŠIÖŽÁW&é¨ÇNÌßwÿËCƒÝïº|“ðª8|¥oøJ|~©“Ø¥jâ_úê'|%hb , Õ©oÖFz%¾Ò¯S{ïwÒ˜²’à+á+“yÔc'æï;ñò5—oz×›à+”‰ËWâY4‰ÔO¼56 _ @E@Y@KS߬;Þ¯„ 'è•pøJÏNí‘£'¿ý½ŸTXÇ—\qwö•9ç¦zàÂpN4qrjâÍ)ÇJv\º•s*ù £¸4t¿9e•.ýGœÃ¹¬ω[»"ì·×/›SZVð)’]2÷žK/ÁÒ|a?‹_‹‡äÜôÒš ÍÎ;j¬­­­½½}û–¡í[Ï)U'[Mp²êG¨a7ì‹Ø>rs=âq8®sa»å'Á¾ Û¸ëjÅ5ØÊpÛn—M8…sNO˜sTÚUר޷¼Äã ~üLáD^‹|k<ùÌñ£'æ>ôþóËïGøJøJüú/q^ÒÁÝ6¤(LVÒ-ÅŽ'ªO?®65OÜ|œc•½.]¾4=P–ÐÒÔ1ëÎìõ~%¼Ú9¢ösá+Ú'ž|ö‰_=¡^ßvå…DüÒ+/Ô9熜$Òu,1Æõâü¢=2çsÎéÒíÆŽèœ¿üÛ׌2#—l-,®S‚ââÆF¬íßé\/ù«˜¹”°Œn®SwI0vzÙcÍ\ç–Ø+–àÖÖÈüàÐuE¦sÝrlÅi·.ì:mÜÊÚ —)Ëööö®®Ìö­ç¾ëí—׉—%ç¥êÑÍ*)Õ·P¦´¿¥EH\ƒ½$Yë%ZñO/]TÜšY¬9áLZ_ ëfZçÊšcí·í¶xn¹MŒrnßÕâÌ«ÏZkÖ±píÙv›œb—sêk76w<¯ÏŽçµ‹¡’Hð±“ó_ùÿŸÚóçŸ3¸ÖåÁ¾RÐaö¾41‰|¼);–÷œ0¾€€²€Ö¥ŽYwühV9çOÔ.!|¥³S{ïw8:v"Ô1ï¼ê¢ g­ß°i½±òˆ¾²´#F™ó.ÝJDºÎá+¥¾Ò4n§˜ÁWºøJºz°Û®#CúJas›ÒʦÎ6ʵñãyíø¢¾oºüƼïÇ/_sùà»®ØDð•_ @ãé'øÊ$|%H P–ТÔ7ëŽwÍ‘ááªn=j—¾ÒÙ© å+7nØyÕEgmÐuKDÆâ+‹«‚¯ô𕜸÷³ˆ(þà+¹lûJëЈsNƒie°“]Ù£î›)ì›ÑÞ˜O>sâè‰ù=pžKøÊ{¨ |eˆP¼[„¯ > , E©cÖ{îùºO–ð„„°„¯tïÔ9zò‰_=ÐWnÜ4pÙUoÜ4@Äá+ëê+¹ëù‡¯¤šúJ«Þˆ_Ù£\¹Ží›Õ÷ÍêAîÁ±“§ïûñ+%k _)Óð• µ©‹~r‹ÕÈ9÷Ý¥ ãÿ¸d-á+Cìm°2:ÐÖ ¾²^‡Ù›€¯É/†@kQǬ; óJ¸ÿcvðîå›7ßÎY›nºåú’{¯L„¯Ô‹µÃ=Ÿ\à+á+o7uð÷÷ÒÓ§éÄŠWl³±“§ž8}ÎàZÛý _ _ ZÄúÊãT&V€V;b&|%hY0ÊZ‹zeÝõy½š£;#ö»*-à^¾y}åeW] _IÉó•Eõæq±q‚¯´ï'ÕÑWëÙÔÎÿW/l÷¹£¿û“Ñ£'NÃWúï|%h^0¾²~‡Ù›€¯”%´u̺ã­JG†‡«7º3b¿«ÒîåÓW9zÒ×WÞtóõ—]}1|e}e±®¼¯ øÊùJ*åØ}VlóñhßýÉhˆæ¾¾4›5ƒ¯¬×a6ä&à+Ae -D½²î<ðÀ^ïW«šð§ÈèåÓWÑ·¿÷ïõÞtóõg=_™L_Éý¯øÊ$úJcNkyßGá+á+A _Y¿ÃlÈMÀW‚ÆÊZ…zeÝñžy÷gïJØ+á~³ÈåÖWÞû]øÊF÷•ÜçB‚¯Lª¯4漿§àm-ÇN.<ùÌ›>-|%|%h6k_ÙÄ'·1Î|%¨*ÿ½7³£ªóþ¿§êÞNwºC’^“N‚ÐAÐ!+a DDD!AÙY£ÎóÌïÑ™×̸0ϼÆg^Ž3:ãLD_ÏKÇu6a@‘dS ,1ÁQqB‘¤“îìY»oïïºUuªn­÷VÝõó~µR·nÕ©ªSUçV½sÎ÷ e -A ³î„wíÌt4zå/ß°¾ò­m;·m »xà+­§ö:ö•‘ý,á+ë×Wš _;y,ÜZ>·i4¬Å€¯„¯ͦ´ZÎW ?ju°Bÿ²©ÉÆ8;ð• k , %¨UÖÕ«ï _àŽ;>Ó@oå/ß°¾’ˆïºwMH¹ý3{à+©A|eÄë |eûJó㵓N äŒsxÏC¯¿þÂWú.#Óhí¨ú úWÖk=¤qPQQð•  @Y@óS«¬;áÛ¥,Uiå/ßȾrÝs›BÊíŸÙƒüàÔ8¾2ìíÂk!á+ëÎWš«/i¹%·ݶóm¿×_øJßeà+Aƒ*-øÊ:­‡4ª1* ¾T‡ªšžZeÝi¢!á-ê+‰hÝóaÊrñóá+›ÁW’]_ð•uí+‰h /?Üuò‡GÛƒNås›v͘än%jà+ý—O¦öÄõ¦Ìþ­™kCã¾_™¬…7/vÕ*«×¾o‹6í÷•»Il9j8Ú·”|eÖCÕ_ ªzY@“S«¬;áCÂç 5Îðö•Ï…ûÊóúfö|ecøÊ8ïIð•õî+Í™z¡_îêhYûþ•iûJ®±¯ÌöuyK@-ôS#î|e + ¾T(Khfj•ugÍšµáC³ëÚ™þƒYÙË7¸¯¤Ð.–ý3{]pÁW6Œ¯äèWøÊFð•æßµÇCÎäs›v9%øÜøì+]ó3ó•P‡ N•|eÖCÕ_ ª ”%435ɺíI3ëÚ™þƒYÙË7¯ íb _ÙX¾’™\nÑ瀯´Oo½ûJóÓ’¶SA·§ÕѾÒw¿âøJ¼“ƒ:TZð•uZiTcT|%¨>ˆe MK­²î„{ÒyCCuíLÿÁ¬ìåßWRh˾™=ý3{Ëö•g÷œ‡Û³f|UÐ`ìýŸ.Þønoþ„“?›Ôly{vgÉ_ó|;ð•”õD_Y¯õÆA5FEÁW‚še MKM²î¬^}gD–ðÆaÙê¾ò­m#!X|Áy•ô¯Ä½ @â7Z·¯4_—äOþl¼Ý÷fnÓ®K÷¦â+¹¢”Ü>ó™)Fq5§MJùÁ)AF  –*Q 3ýŽk·Òï¨éhš© J³ƒ_Š:¯ øÊ[÷†¨(øJP+00š“šdÝ ï×I™ EOÿÁ¬ìå›ÂWRT˾™=•$y£õó•Ì´$2äfnÓhÂfª¾’ˆêßW¢£%¨§¾²Në!•Ö½!* ¾Ô(KhBj•u'¼_gvCÑkûÀãí7I!± OTNb_IDÛ¶ö²\tÁïT¿w(É^jý|¥ù¿%ùA7ò¶‘£Iš©ªùÊÈf ¾ß_êÖÝ%øÊV|%¨-P–ЄÔ$ëÎêÕw†/Ð CÂ>ÈE¿ý&)$Vá‰Ê)ÇW†$Þé›ÙÓ?³§Â|;¸C(ç½¶ÄWђ܉ yûȱØÍ|ey­7™Þúð•uZ)¶ëu^Qð• æ –%45ɺÓ,CÂ>ÈE¿ý&)$Vá‰Ê)ÇW†S¹¯„² Ü\¯¯4;`ö‹ñö¤nÓ®K÷„7Sì×ÚøøJöEkÍ k­ ƃÇIøÃå'ö)©Ø$í¡…¾xTRš­I–ÒÉðã© eÏ¥T§íÕ…R+v(Rí@PüÇÆ¼®ÿ"~œpŸ ô_Ùp§,YsÞ_ êô²€f£úYw¶nݱÑlBgÖö3äý¼œBbž¨œò}eH Ë翾€Z¼àúûJbÐÆ¾"GØ=¦diR_Y¾-¨í_Y§õNsÞ_ ê(Kh*j’u'|ú¼¡¡ŒBgÖð3Æûy’Bbž¨œJúWn´o ¾€Z¼àúJ"^¢Zõ¹M»C›)øJøJP‡w<|em6j§:‹ü«¨9o„Š‚¯õ”%45ɺ³fÍÚð!áYôë¬í£{Œ÷ó$…Ä*iE]«ëJ,KÆa/lHgºàšï¨^!¥¦|e£i„¿$%hc3ŵ 9pÈqâW6PÁWÖj£ð•þ¿Ôèe MBM²î„‡°Ìh£µ4ѯÍI ‰Ux¢rRð•om ÚdßÌžÊ}%ž†Hü¢ê+Ão)Ÿ¼áåŽç€Ö†Sé_Y_¾×¨þ_Y›ÂWúµŸÔ P–Ð$T?ëÎêÕw†/pÇŸi¦·‰¯ÍI ‰Ux¢rÒé_¹mûhÐVSò•x  éËn¸¯ä%âhì¾¾Ôá]_Y›ÂWúµŸÔÍ@õ³î„wê¤lÆ¡×ðÑ=Æks’Bbž¨œ ǃ›Ì?ÿ]ð•Ô¬ý ö•I^ˆ#}ePËàŸK‡ËoÖà+ðý¥næ]ʨX)Ë,6¾¤$ÜQR;ä2ÀW‚–ÊžšdÝi¥!áð•öþ¥à+Ë~µ¨CBÞ4t]Ïår“&Mêéé9çœsn¸á†+VdUíÍ7ßüã?þãµk×Fî-¸7–Òˆð•Á'ó¹—ö\²¸›bů4?ZK¸âβ§’Y¹£Xº’ÎìÕ¼ÍQ«#gd „¾2è1#Y ©Ä¯ ˜&"C_0-„Ь{“Ýw¨«nÕû·bYzŽ+N,ˤ1.=5\»›ý+kõlÇX“á+ñ€ÒÊžêgÝ >oh¨‰†„ÃWºMÃW¶Äƒ¬a†aœ:uêСCÃÃÛ6mºë®».¾øâûï¿`` ‹-ž÷Üs¿ð…/ÀW6ã»u´¯¤ˆ¨ eƯ¬ÈjÄð•¸è@UîløÊZ=ÛÁWfVð• mÐË›*g݉„žAÜÌš=ÓF¿ñ&)$Vá‰ÊIßW®{nSø³r¾²¥g·mÛö»¿û»?þxºÅ®\¹’@ó¾^Gûʘo‰ÉâWúÏna¤_xJøJ"©›j—b–S|¨(w›e»¶ò“Ý ïÒ¾‰’°˜Ñû#„€¯- ”%40ÕϺ>}ÞÐPq3k% ¢Þx“«ðDådÑ¿2lgÎ[rN*¾²‰;q؇&¥?zôèÈÈÈøÃ¯}ík´{â‰'ž~úéË.» -Hò^æ+‹—žˆõ2¿òGÏ û5&ìZÛ=írŒÊÙ§iñ_Í¿bJ§)‹.‡ƒïü·Ý92ªiš=rYÓ4]×Íi3ˆ­½p[[›2=Á™žàÌׄP‹RG=‹8À}`xÉœ÷½ïrr ª µºµ®¯¤”úWÚÒ¥¹Ñ4m„ &L8ýôÓßýîwßpà —\rÉþýûíî¹ç(K¤-‰á+Cï,Ž¿òÑuèoû.öôè±£G¡~²ã²ËÞãù9S•¥G_ª§É/Tq•:{¬€¯ŒýÔh‡™‘[$R/³áO'h , Q©rÖð ™”MžŸØûöÚÖ­[ÝoáôðÃ|úÓrÖ™g¦ôÖÒ¾ÒÚB ¾²ŸjßùÎw~îsŸûô§dãÆhÁ@’·àÌ|eí„­y3c—šõ0£/$|%h4 , !©~Öð ™Yl±x˜[½‡Þ·Tåµ×^/_Y¶¢¯ 4 _Y +W®T•åðp±/Ûž={ …‚ù±¯¯oçξ}‚˜y```tt´ø—ËŒŒÌ˜1Ãwáø!Ÿzê©û·{î¹çvíÚ5yòäsÎ9çæ›oþØÇ>–ÏçÃèÔ©S>øà#<²yóæáááãÇwttÌš5kÑ¢E+V¬¸öÚkÕ!±‘»·mÛ¶ï|ç;O<ñÄÖ­[;6eÊ” Ü|óÍ«V­ )§…G¤¯dŠì…ÈÞ@Íïå&Ú¥ u#Ë´Z2ÿ5 š®÷š77ÐX¸›bŽÑkRš³C©« {\ÂW‚†Ê’*gÝY½úÎðÒþ?ÿ×ÿWó§ørßçÚW†‘ž¯lŧۙ3gªOœ8aNtww_qÅ>ú¨ùqtttãÆ\pAi 6l°}%}à˜1cF%»ôöÛoìc{àì9{öìÙ³gÏ3Ï<óõ¯ý‘Gô¿¤üÖ·¾õ—ù—{öìQç=ztË–-[¶l¹çž{úûû¿üå/ßvÛm‘»!¥üâ¿ø¥/}illÌž¹ÿþÿøÇ?þñ¿ò•¯¬Y³æÌJúJ7ƒç h_e-CúWÚÑðÞ êññq"Ò=±,­B ýéþ $èŸp<ërÆ¿Qè_™à ¨áý+È U G•³îÔóðì=á+Õ'Ý|ekAUm#uwwÛÓ«V­R¿úáè[‚gþí·ß^á.]vÙeª¯Tyå•W®ºê*»ï§ÊÑ£GW®\ùÉO~Òã+=ŒŒŒÜ~ûí·ß~»iB¸é¦›>ÿùÏ«¾ReË–-W_}õ©S§Z»Éç+CºHÇŽ÷NP'B1ÜHåcP»Ç øÊØO@ w˜Ylùv°€²€£ÊYw¶nÝÑ£3ƒ¼äud RX¸|¥½Xå¾²5Ÿpðƒ¨ìéøÃ]]]öÇ eùÐCÙÓS¦LY¹²ÒÛü¥—^ ùö׿þõÝwßí™iÆõ×_ÿ£ý(æ&î¾ûîU«V…Ÿóïÿûá…¼öÚkÿú¯ÿÚÒ~_VÉð•Ôö†¯ŒýÔp‡ _ @Æ@Y@ƒQå¬;á››74”z^òúy¤Ocá†ò•Q;˜’¯l¹ÇÜ_ÿú×ý׭ιꪫìé‰'^{íµöÇ­[·¾ú꫞¶lÙ¢&˜ºñÆÛÛÛ•jeŸ3£WÑYgõÀ8pààÁƒÿò/ÿÒÑÑ¡~ûàƒz–ÿêW¿úÄO¨sÎ=÷Ü»îºkçÎccc###÷ÜsÏ»Þõ.uï}ï{_ÿú×#«èŒ3ÎøÁ~ðöÛo>|ø«_ýª'Yséž´žóˆá+9²@¾ª~ï6¾¯ŒÓe5r¾”f?éüÅÀ\ÑYÁPÿŒð?Ã0Ü˧úgRœHxTêšõbWUƒ\íLÁ? ð• ²€F¢ÊYwÖ¬Y>$<õ ™õd*_¾ÒÇW¶‚+aæS§NíÚµkÆ ù—¹téÒÇÛßêº~ë­·ªË{Ɔ—:Ïœ81"#éîî~þù篻S§N™2åãÿøßüÍߨ ¼òÊ+êÇC‡}ñ‹_TçÜtÓM›7o¾í¶Ûúûûóù|__ß­·ÞúÒK/}ä#Qû‹¿ø‹ƒ†ìIÿúõ믽öÚ®®®ÓN;íSŸúÔ§>õ)u-[¶´¶óˆã+}sgqÂæHâG€t °KlÅÂæ` çÑ”ÑiOÇW†*3€_é9E^:ŸJØþ"ð¡-ÈL&®øJÐØ@Y@#Qͬ;‘#Ðïøì§3>oh(•r~ø‘ªûcéØpuÎܹs—-[VùÞþÉŸü‰R“ˆ>øÁª÷íÛ§~üÿø#GŽØÏ>ûìÿ÷/MçÝÖÖöÝï~÷ì³Ï¶ç9r¤tŒ¹«%¹ãŽžžuÎ-·Ü¢~Ü¿‹kh_éÓÆKo_r@©?0ÐõÔÇ?~Ìü¿âßñ'NZ§Nž;uÊùS(ŒÛá÷ªæ¾²VmØáà€P–Ð0T9ëN¸M½G§«ðyC rNZÅWZ[NÅW¶ôóîŠ+¾ð…/xfêº~ÓM7Ùö³ŸŒŒØwîܹiÓ&ûãªU«Ô¸eóþ÷¿ß3GÕ¦DtüøqõãÚµ®öçSŸúÔ„ |Kž0a‚§›¤Ý—åË—{æx’•ŸóWõW¹œÏÓתU«¾öµ¯ÙµöÐC}âŸ0?>ôÐCêy¯ø ­,Õ@–^x¡gPyÙx´ 麲¼gœ¸šô¼Ï·{÷îM´'ù|m~ICê+þËœðm9yH÷†mÅþ•®3Q|xòäɇZ·n½L*‰wŠ€%=ÃÇ›çóùB¡P^U„—\S¤û®ÕWº¾Òš!ɹuñF ê‚ãÇ‘®; ”¦kö?¨èºn·]9ešˆ\¾GµH=×êðW€2ЋvÕlWƒŽÁÔ”;vìØ±cg£\##£##£D´iÓf"êëëëïï[²dQzU%R¿ ë½@=¯¢  þ©fÖð!áó††2|e‚ðŸ_Y×Üzë­ÿ÷ÿþ_³¾ÆÇÇyä‘[n¹å‘G7hkkó$ã®&3fÌØ¾}»ò7òŽw¼#ha³ïŒ'»ŽÊè¶"ÒWrò†A‰_ _ @ 7*âW49ÃÃÃÃÃ;Ö¯ßÐgytttttô¥—6›½//^œÁódE§¼Þ  !P–PïT3ëΚ5kÇ„§«Gî[9ãÁ·¾öÚY) ¤ }¾l_h2_‰§ß fÏž½lÙ²çž{ÎüøÃþð–[nQs…_}õÕÓ¦M«ÕîsÎ9ª²|ê©§B”åÓO?­~œ?>Îoe­N¤¯ ¸yá+¨ÎmÚzñ+Ù½’ìVI ¾~ýÓW6ß7{_nÚ´¹¿¿oáÂ…}}½äþ8]éö«y†kš¿Ò^EMíq©ö–êøqµ(÷¹I<~<Æe[Ôd €º¦šYw¢·•vRòø¬^}g¸K âµ×^O×DÌol_|eXµj•=ýè£>|ø±Ç³ç„$ÞñtTTã…¥Åå—_®~üÚ×¾vêÔ)ß%O:e§2Y±bNnE-O¤¯ôíÁx³Ë€& ÷'eÝ¢­¿ÒõÇÌ,¥´•å /üô+_ùÚúõ?mJ_©222úðì]ûðððŽ‚Š¡þêŸQ(E¤ ³S{!x›õbûÏè_ šô²€º¦šYw·5oh(ݤäñ‰ô•CCCóæ ­Í6-OKûJR†žVè+%KÜ×AÜxãôGdªÀ£GþÙŸýÙÑ£GͯN?ýô«®º*hÅ|>?66fúÑnݺ՞súé§ßrË-8¹•‰,}¥’dwõÿYhÞàDdÒÎý}jlÜ^ïÔ)ez̉pj¾~§¥´³³”žü]>­œ2•° Z&zÚ©¸’¿yß~ßuϘÞÝ=ÃQ1—Ëçòö¨Þ;':Ó鎎{Z×u;n£´Q¡¤ U ΘÈkpÉôK›¾ùç/'*ïäÉSD¤çœëJ×”X–¹œaÅk,—SÿyFSˆ©ÉÄ\=Ô6ZIxŠòä`œµ¸ü|u>ñ+ƒÊJ+4‡]þððŽûïÿ~«µÓ»ví~ì±Ç{{{,˜oö¸ »eÒ¾mÓˆ _ êô²€ú¥šYw"µ`­BXFŽU'¢;>ûé•+®ÎØ$zUNTNøJ{1øÊL™:uªê%¿õ­oÙÓùÈGJÓÔØLš4IýøÝï~÷äÉ“/¿üò† ©Åøä'?©Î¹ï¾û-ZtÏ=÷ŒŽŽŽïÚµëÞ{ï]¼xñ}÷ݧ.ö÷ÿ÷]]]8¹•¾:FøJ¾ÙUdŒ&+²Y QUHd‚ ºð•ľ;ëð‚áZÕÃöíÃ÷Ý÷½ô•6»vízôÑÇ~¬é³¸`2(Í ¨# , ~©ZÖp7JiwçL´ck¢úN~ö3Yï|e‘T|%2ð„£Ž W/ƒð\ágº¶þÁüAGGÇ‚ þñÿ1Å}ûÒ—¾´páBuÎ+¯¼rÛm·õ÷÷·µµõõõÝzë­¿úÕ¯Ô~ï÷~ïãÿ8Nk /ð• ö¾T羄¯têá…~zÿýßoúaàqøùÏ_þ×ýw3ÏxÖ |%hz00ê”jfÝ w£évçŒOœáŸý̧çͪÅÓ?|eÙ¾OÃa\}õÕS¦L9tè:shhè‚ .YëƒüàÆKç¿ùæ›)îÛĉüñk¯½öù石ü'>ñ‰o|ã8§)¼@FúJûÆb*»Éâh§Ù:¾Íˆ¼/›ÄW–ˆ7`"«O†3ÍÅVixxxýú •ÈÊÁÞÙý½Dž†ŽÄbæâ7vÓ§tB·ÖUŸMˆÙu³;¥9%Ø*®­Fbøðñ‡O”wP<òhOO÷W¼Ü‘ „Bûiš°³ôhš¦7ÈéNƒ¬çTi㜳,ûÜÛcÃ…g¼üT<ð• þ€²€z¤šYwV¯¾3|𠇝Œ»þóSö•Ö®¥à+‘ž8œ &ÜxãßùÎwÔ™á],‰èþèî¿ÿþ-[¶xæ§«,‰húôéO?ýô׿þõ¿ýÛ¿Ý»woàûçààW¾ò•o¼'45Ò¼¾RF_Y·×dÔïs™5Y U)6§8MCÉžÌJ\Kˆ°é)¸ì‚鯝Œ9?NôK÷GçøÆ²4·Y=¦¯¼ÿþÿ*£6.Yºˆˆ/=óXÁÒš–Ä’Š1p­ÎbÅ?å£$uÚ<)™¤Óf:¥1Iie 2?Z6ÓÙºÙTð<Õ,ü§Ãw>¹ã퓉Žq÷î=O>ùcÓZfñ„Šø• €²€z¤jYwêvHxx P±ïg´¯üѽúª–û4XæËŒrÎWRZ¾Ä‘¬ZµJU–Bˆ[o½5|•©S§nܸñ«_ýê<ð›ßü†ˆº»»Ï9県=å?;ærŸúÔ§þàþ`Íš5<òȦM›¶oß~ôèÑŽŽŽY³f-^¼øCúЇ>ô¡|>S™Ú‹i¤¯Œ™.6_ÉA¾2ak_ÙìnyIŒÍ~|Âpϵä™0¤VŠ'Mjñ4eú–~™]t²(©BïFËÎÃÌeŒ¿ôÂ%ƒ}³gö‘ôˆÈºó•¬~ÁÀdê?˜w9¹açáGÇbïîÝ{î¹çÞ+¯¼¢··7埅Ì.Aê (K¨;ª–u'²'cºÃÏã™ hÞÐÐw|&Ëw’Öô•õz’НlžÇ⌮@;K¸ÉE]4wîÜȵ&MšôùÏþóŸÿ|%{ÿˆÚÚÚn¸á†n¸!£zƒÛ.©Œ(_ÉÑÅ9"¿e¼dâf ¾4áÝX_»T“ø•D‰|åìY—^¸döÌþ¢l(_©n}fWÛ CÓ™å†Ñ·7î:óðüÉ­%|%h) , î¨ZÖðžŒó††R~žàðãùÊÒùCCg½öÚëY> 6¿¯œ=ø<ýêÏßš?§r_‰çâpNž<ù¹Ï}NÙÅ4»"¡ìúWÖÚW&Ý[øJPÛ›±i}eÈ5~¥9lŸ‰î¿ÿ¿vìˆå+gϸô¢¥³gö9°a}¥5Gó=tOÜ°ëØÆ=ÇãÔÃã?¹ü=Ë»»ghš¦Y tM³Ã\ªó‰ˆ]#”sã ‹i­¢ð÷Œת¿)H(K¨/ª–ugÍšµáf0E7šâ^QplÍyCC))ËD¯Ê‰Øê½åœÁ¾OµúJ<ÊzÙ¶m[ooï©S§~õ«_ýùŸÿùË/¿l5iÒ¤›o¾UÔâž$ÚWrhb«d¾Ò~&ÕWª¯¼êû¯úž¬ b;Πæ¼533³3­iÂw³Î´j¦„³¨³]÷|ò™ÏÄ¢8Í1âó•¤ëØ…BšQQÖ M¨b'ÙðL«bÂã/4E@h®¢ü§ËÖ[™ßб3s{g&uʈo!„´ ’BJë+–Ì’=+–þªªÓ¢ê5Ʊâ~–Dõ«gYZTéÀõ:!Á¹ÝA5{ÊϹ7®¥ú›­ÞWñ}åm¹~ö¬~—”l _i7Èôt.ힸq÷±{£³ô¬{fÝò÷,ïííÉòΫy‰xÈ)e uDÕ²îDnèŽÏ~ºúCÂ#÷ŠÆÖ\±âê”úD´ÎxðÈü•úJ ø-eΜ9A_ýáþá”)SPE-M,_I#¹‰IÕ"6ЦCÓ1™ÿs¦É4ŽÂ´˜Ê|"Ç?2³°Ý“¤9_ï{fB²f·(Ì¢dÚ7«D<_é;ßšfŸ’…]ÅÖ‘ÿ+•¥fúJÍRBš’óW(BÖ£&ƒ¦uÝY]W”¥Ûxú%5Q–?aªŒ–Ì\ÆîY?Š‹”,,õÆšd«—®¹ ÿ= Ò—µ®.ÿ½rï8;w„[YJé)Â÷URHÛ¦Þ{é áü¬³“CÛY>¤5Šé+gÏšyÛM×»Íc³ùÊâ9’réŒö‰úƽ'w/„W˺gÖ]öÞËʳ–ð• 5²€:¢jYwÂÇž§.3>qR„‡‹Ô•+W¬\¹¢äUGzŸò+ykU_©ì/|eõ8묳<ƒÄA+ÃW2Óh€²ìëð›]æxðÀQ¨qF[nÈo£±áãÄ9¬ d¼mƒÆkª¿Íؾò¶›n˜=k E|¥¹î̉¹™ƒl;ºó„^9O?õô{/oOOwO8|%h , ^¨ZÖÕ«ï _ häu¦Ç^¡¯Ìò=Ñã]ûJJÅWâ‰6&gŸ}öÃ?ÜÙÙ‰ª€›ˆô•!7–¥,ÕêÇWz ©_‰† Ôw›PÙ3MxLûg½Ø^Xÿî»~ýO#}åìY3o¿ùF.jÇVñ•VC-¯›5qÇñƒ;"‰?õ“§.¹dÙŒ34¥¶®ëº§RÊè¶WÓ4ÍúÆŒXáL+ŠÕšr¤Ûå&È(K¨ª“u'\ŒRª}9ãÞ½”jÔñ¾Råõ—ß:ó¼Ù•ûJt´,eéÒ¥¿ùÍo>¬iÚôéÓÏ;ï¼ë®»î¶ÛnkkkCå€X¾’ã7M^_)<÷1„% ာ‘( N¤ u ²óž, ¶F†Ki Èe"!|í2±ðkˆ¢ãTFOsP¹_ÛÙµÑÀášÐ4M×u%¥® W¥ƒ=âÛôÎtÎyË麮çìå5;G= ¹¾ØtR=³‚™Õ!ðɶèúqszè3ûOSpüÊcYt4 Ùtà`pVƒ«¨®î qÏw©T. !ìË[8Á\½˜!XýA·*Ü)ŠyxxÇúõ½Å}¥tf‡~í@ûƒ;O†×Õ–-¯Î˜1£âû/åÛ¹®Ê ø+‰*€z jYwêpHxÙ)«÷ŽÖ:¾’iùÅó×½ðrð W¥¾’Yâ~÷°aÃTm["|åK§Ç+JZþBØ‘™HXvOV§©æÌa]sréèÊ|A¶ec!H3Sî°d¡Yi<yz1—xFõÀ#}¥ç脟úòYž½…{7¤m_éäùÕiO?)u:§jJeZ×õœ¥,Õ¢4!\¾/N*žì.Á §(KõÌJ)™e»çNÑÞQÍç’Ñ.!XŸwrP*ÕÉJ©(K÷ý Tuýt)KrþÙAˆbÚh.]]8ÁC­ßt.¹‰èþïýWøÑÁWÚg¶‹kûÚ^ûÜ{ÞsiYÏ–©<ÚÖu™8?»¨¨9U˺>$¼&f¾2á`¶¾2œý»ÁWPƒæ(¢eX/Ëe‹¦ÙɯYcw«Â~ížÿ4Ç.ËlŽbûV‘¾­­ïòœ¸pªÇëo¼ñÇòé×ßx£>š&¢ûŒë+ÍÒfN×öæÚÔý¾}ûöîÝ—üÙ²’“‰ø• ñ€²€ÚS¬;kÖ¬ —ƒi=Oq—¨5KÖËz`kT_9{°7è8÷JÃWâù€„o„Q¾ò¥‰3b”RÚ¬EǯÌÀW&œ®¯D3jÃëoüæßø&}ãßT­%g3†Ý¦¸ [ÕIÉÌRJÉüÂúŸ‡†°„¯ôøJ’f`¾¶;bHëºuÏŽ öŸŠa ‹BÁù3¤ó'¥!-üêCâ 8¿-Á‰ÏÊ{ü ] , ÆT'ëNtGÎôÆž§µKT£Àšo³‰Ø¸åœ`eù›_n¯ÜW"–%‰_ ÃûWr’Ü;Õó•\RtðFá+ zýõ׿þõoØÿá¾ùÆoP¦1 ý|–ù[nY0þéO7†_ä+Í}»vF„oyvÝsŽ‘4Š㎮ô"Ý0Ûì~Þ,ñÑü4PËjLu²î„wäœ74”ÖØó˜ÔqŠpß·ÙDl =<â¨÷ï:<µgUà+12€ÄmQ¨¯|©czЪƒ}öM­Æ¯TÃ;jj`GAŠŸ(H*é_8oçÒamö„Ô4{š¥ÔXZ>@jv*s.y™öÕ…ìmšböÇÔ½óVä$ÉÇõ€¼1¹\.ß–×uÝŽG™Ïçóù¼9ÝÖÖ¦fÍjoo·§;:”ie¾Z” ShšËRðXýô;!?gj2bÃ0œùR²ä\.Ÿ´p5gQéñº®ÜWGÿøG“¤Îh•àŽÜêÌ7¤»®˜ßxã ³¥Ê?üÃ7ÿ÷ÿ×gÌ%"iHßjwÕ•rI¨Ù¨…•ÍÉ<º\>OD¦•s¤iD–å#bæûïÿ¾7|e¸¯$â títñà¾À‹jÿþýûöí›>}z²ÇÏroØÔÊÄO3¨èe µ¤:Yw"ãEVyð5|eÂ'ÀjûÊåÏ*äÀîCT©¯Äƒ.É›£ þ•Ì#ùΠUûÚýš5Ok ßŠqéÛ”IßV…ýûN5°IûWFM§á+¨2¾¾ÒäÛßþΛo¾YåFÇüÏðððŽÐ!á—^t|eˆ¯4göçyIWXu¿¶õµ !øJÐà@Y@ͨNÖðçT‹Á×á]>©F‰Ë>Œ5sÿÊÙƒ=A…¼ùËá }%†PÞ;§¿¯ÌMm V–ý%o­1‡fWî+ã¼?×­¯D3ªÇVüÊ ¾ýí®ÄZZ­EØï/³9¶Ø.™åúõBнí#7 Îꇯ ÷•fŸßÅýma-÷ìÙS( ãNÐJWüJ÷Ðp5˜¥k¸ýçÎd÷ˆ @Š@Y@ͨNÖðþŒÕ—ƒõš"¼ò¶¦Î~á,î>\‰¯„² a{è+‰)ÄW.[4U-Á¯5HÙW'"W(;~%|%hB<ñ+ƒøö·ÿùÍ7›és•t§ºax{XËÙ³fÂWÆô•fçw†5,¯m}Íã& ÊŸ™gÇ•m'ÜRZ¿\òH*K4Ž ú –%Ô†êdÝY½úÎðª,á+“<ÖÆWšÿY~ñyë^ø…ïÒ¿ùåðÂ÷S¯Ä/ _IÄ/uv­5Ø×!³±¤9r>Úq5A,œ0—‚•=ÈÙ!#¥&œð‘š&œö.§©•X–®Úó=- s<í˜ÄW–-C5]÷wÊåòù|.—ScYÚñ+=±,'L˜àL+ñ+Û;:ÔýcY ¡FtŵÂwºŠ¤ËR*•X–fž\NOZ¸®çÈ ³¨£VÕu¼›2›‹Sl‰ RëÊðÔÕëÁãÁKùçù—Ûo¿}Μ9ž¢TÔËFÅX–L¬¹C£šÕkîiž‹ç‘Yš—¢éÂÖÿtƒû-\[Yõ‘ë,íhý¿”Bõ†ð•Н$â°5§¯4ñ‹hYäО·î>Rž¯Äƒ/ÉÛ_¹vÊÜ•.Y85üNÏÀW†$‡¯ÀEy¾Òä®»îÚ¶m[™Í‰mØÿ潘^|ñgA%\zÑù¾RÂW†ûJbÈÉþœ ªÛ½{e€ÉÒ(‰liûÊÊ5%DP' –%T›*d݉‚ÊÀó˜ÀW&y¬_IÄË—·î…—ƒjäOm½ô¦ÅeøJ i @Òö§ÄWŽä;Ã…÷µGÞévÀ;K Zñ+­4Á¬Û¤Lˆ”ø•2WŒ½huÖbrËžX–v#ɉš,±^n2Ÿ(©þSÑu½4–e>Ÿ·§ÕX–mj,Ë6gº]‰k™ÓuÿX–îàƒšñê±,Ã3MÛÓ…BÁž¶bY&~Ù4Ã_*%‘ç(Õ=qí°{šÝAKç3f&‘’cݰ †ëSþä'þ÷7ÿñŸÊ>#wß}ÏGþÇÿ˜5ky;ËR³bY2kšf^Zæ!H™';x[cY²Ìç‰hÆ…‚!¬P˜V¹Å­\¼t‘”†í+µbõI3}­1|¥ÇWšrÆHÁ¿yyóÍ7ßñŽ3²¸OÓ}ü š —%T•*d݉ìÅYe?®h©YËÓx`kr_iþgùÅç…ÔË/žzµ _‰Œá$nܾ’˜žÒÅ’—»XúwW î_dôú9 R8è:F³S¾€¬9óÌw|òÿ»’îÿÞ÷†‡‡+jZ~ŽwìŒb¹ì‚%Žì3óƒÛý+¥t)BøJ?_IÌ‹ÛÆBNÊS}XMÿñ€*e U¥ YwÂ7Qe?Øø)Â[×WÑòeóCªæðž£‡ö¼ÜWâ€d^Áã+׆úÊÁ¾öÙýuà+ã4;uë+ÑLÌ©­µ ù×ÃÄ;Ë–.vùJò•|¥¯¯ä(kyðàÁ:¹>Ñ‚:ʪG²î¬^}gøÕôƒð•±ŸëÑWšÜ~ó•!ô«§_?´çmøJ²l‡\¾r$?1xH8Ñ­+ªè+CÏ(¥_ ZžZYK'€s1s‹iø¤±}ûö µúÉLçbùÊâ_‰¯,¶ð•~¾’˜ûõBP%¿ùæ›cããcãã§ÆÆì¿ÂxÁþ/ŒÛ…Âx¡ F¿´2óH–ê“ýÙjspøajbY@•¨BÖðQçTݔܑñ4©ºþ´ O{fÓúJ²R‡oÞTMÛ_}×{Þ‘ÄWâI€¤M‘ã+žvFÈrËN¾ÆçNªÒ,\!òœ2ã' ÖÞ2 _E¢ì‹½€TKW7¤,ã9 ·}u•¨0m-;QBššÜSS‚Njž”ê´4߉_©éºfO×s,K壞¸4h9»'„ûCï€ø•AÓ²h¦Šûf¥NS¼À¡µ¥lQ‰_é;ÿŒ3æþþïÿÏoûŸË>;ßûÞ÷¯¿þú~ëP„çbf]×Ì­KÉLl†£5¬íe˜Y±cDzB¾H¥®„³úXJAŽd–ÿ ÃüÊô•ª14? øJ%΀Vè× #²NUŒy%¤ÙbPP–P%ªu§~†„GúYª®?-OÄ›Ù̾Ò|ü¿tÙywÿçA5udïÑWžyãÜågÄô•LôÃ_ü³´^È^¢øXÏö˨+%‘ó†A’¥æ”‹Ó¤n•ì?Å×këÒ~ÏqƒX¯YÎVWweX•ö¾“Oö_ݯ¶l{‹U¬Û;î¸òª¤$nf%뉽ü‘ãD4é—§{Î`gû„Îö Î~ÑÜéΙRßÁÔ3èzÓS_-— d¶öŪ(Wáj Ûk1+éè=Im½å°oÉÖG—†Ps§:WœÒkOÝs»‚Y1#Ê›ªrªÉç¸X=.*©4%B%©9j8(?¸9'ÜWöµ_²hª¿¯dV4K©[Ð1³² ±Û‰âª‚kþ.’å'T½hǼµ6¤¾ KgÔ—·-õ”ã;S(+–ÌOì+myøÆ.Ì´8šÒãm>­*K½Ñ”¥Jzc~«%Ò'ŠÊRTò³íR–^Èþ^FIUa]ËSòU™sfÏùøÇ>ú¯ÿöïeoô¸æškL¹h_Bº£#-eÉLD…‚a7pºaU&³®ë;wì´Ûv·ËÎ_@Ò`óf*v·äJîñ•EEH.ÛØÚ¾Ò\1DY¾õÖ[sæÌ©¡¯¬y ¨``8TIáeu'|Hx5‡`7EŠpøJç…eÎ`率yxŽì=öëuoÆô•Ö›|eU}eœ øÊ:÷•k§Í o–-œâ+#Z’ÄËD6$:]ËÈ;PîðÄËDEç 3æÎûñ}´’Ö¬Yƒ2¾K žü! IDATü®8|œ_ÉÃW2|¥ÇWq¿6^¹\„¯P–Ò!ë¬;‘£°SIì_Ù‚¾Òdù²ófÏê ·–ÿ½î·ð•uë+Y5’þg¾²Þ}åh[W¨¯œ:»¿½*¾2Î`søJ’iT¬åˆm-Ùþ! ü'+«9SZ¥`ßtñ’>¾’_ÉÒßWb±6bó];ÉÒZ†)ò8…ØÏ*6hšÜ£›µ€ùê*šö`poQÊkQOÃY଎dçb,‚Ä»'"NËÁ%ŠiÖ°TªNeiX‹IS½ùaH#òטÝ6Õ­†´÷ÏPæK)̓³gßvÛmwß}wÙgꡇZ¹reo_/±+¶€ahfÌ뼘!(%3tÝÜfÞøó—c/†¦µT›!¥a0³F$œYï [}ÚÑBUEè´ð•Öï s¿á|éI’…]™¥®ÉËRÄH}#’øJÿd>®Ùj’×>úùJ÷š®J±0˜x•¥¦ WÊ€”;n•i¯"Ç$}éY½úÊ2ä00‚dÙÊ2tv5îPªA$U9¨iš_,K³Ç™Ýµ–ª t¸ÞëKÑ—†á¯2¥´?Κ5ó¦›oºï?ï+ûì¬]»öø@o_¯’~‡t]·ÒïH"2³<•e!gý±TvW(ÿÆÀL.üÃ0Ì›]Âô•ÌRöÏ‘Þ_vÿ ÀW:¾2DYÂW ‚á-Yg݉´„US„ð•Mì+Íoæ ö\zñï„WÙÛûŽoúÁ«oï=_Y‡¾’#½ÂW6 ¯¼åê¾D¾2`™ ##V 8 Ž/{øvùGÁAƒÐUgppð¦›oª¤„Ç{l×è®t/gKüvÿJw8KIJØÊ_‰þ•^_Y?fãÁ”%´.YgÝ /Ÿª˜•;2˜&UQžÂWfá+ÍÿDµ4yýù£¯î‡¯¬'_Éð•å+×NÓWÎî神¯¤8¾’î |%hJ\ZiYK§eµC’mâG$%†ÚÉÒ÷"ÜW*í¹tûJ _Yâ+CÅå[om³ƒT F1~¥¹Ò™]0\JüJvý‘ëOµÉð• ÀÀpȬ³î„—_µ¨‘‘ƒß«)Oá+³ó•æôí·\q×½OnÞ^ƒ»^=pt߉޳§uNo‡¯¬_õj_Y7¾r$ßùðÔ¹qZ§”|%'0z%ˆðdu¡àÞ“¿³yeg?ECéNBĈúYº¾3éW,Õhx¼ª‘Ói:W>)Ý!íiC‚­N eu¡œ¡¬Î’9`¸Q0=††Ké´…‚“q¥Ppí¡½“Ã0¿êï¸þúëxà²ëíG?úÑ>ðîžf–ºn ¹£ºVŒk)™s¹q"’†”RnÞ¼Y=ƒjç&Ã(˜Â‹… ÍÉ.”^8"”…ý“!% r¢(ÖKKûJf^$ŽmâÎZ>éÂW(Khe²Îº³zõ–°*½›1E8|eD1­åÑ}'Þx~gϼ©Ýó¦ÀWÖÚWFdeõüÀWÖÈWŽä;_êìm묦¯‚”¨‘ޝôt’ÝfW¦×L±ÒŠ…'Šq0¸áq-™YöS£%7‹åHg. Â§ÜÑ9j"ÚÒ —’ M¿ã¯)ƒÒïÄÌäd k‚dS•†Âs•šÇ9’+ýŽô¹f‰¤áŠéì­ßUeî®ËR§ß1Œ¤jdL·H•¾ÊRîíë]yÍʵ¡“á<öØc—_~ywOKÉfüJb"*èkߊ"UV/LçÌ:Qo/œÿ.iŠÂQ#fa‰9É$‡(„ÓÏ­Yí_É _YÉðð´žtE¥w%|%¨Ê¯ ª2y™fÝ©“!áÍè+ã<™µ´¯´­eœâD´{ëÁ_­ùíî­á+kê+GðÂWÖƒ¯ÉO\;eîÃSçVÙW,Õ¿²‚ø•5Ü›9é`sYn $í@ù²µ<úûûWVö¸ø“Ÿüd÷îÝ).K²Õ¿’Jǃ+¢Ð¥Í%á+=¾2ÞÅ‘IüÊ”ŸŠÈô²€LÈ4ëN¤(¬¼ g*‡IUœ_YM_i[Ë»î}bÛðž8u¸gë¡=[Íš<ñô §µÁWVÙWªUäç+]»_YM_9’›Õ³¾¾²…øæ?}«n÷íŽ?ý?X¥Oýä'W¼ÿýý}}¾¦ÉÓ:ÞõvÌJ¶…d—³¿bfNC*•ñãRi![ÞW†utd;¿<{²Þ«½‰‹êÓ‘wÚsÒYí³\n7KøJe MÖYwÂEἡ¡ »pƤS„ÃW&Ù1æÛo¾â­í»ï¾ïÇ1+sïk‡Í‰égÖqz[Ç´6øÊ*ùJ1Þ3_Y _9’ëÍM|©³;f;Tu_Ið•4 O>ñÄ•W^ÙÛÛ[Á³•¿Ehޝdr‰Bç7PZÓ’lÝf)΢¾„¯tE±ð6yj˜…@ÿ )K”e@ƒ_  , 5É4ëNdnîʳúÀWÂWÆñ•æç öÜvÓûâ[K“}¯¡×‰ˆ§9‰ˆ:¦µuœÞVž¯|û·§Ì2Çc‡ŒîË:á+K}%G\f_â+ÿI›ÙÏ'ûù1õóÉ~>Yž¯|©}:ä&Žæ¥\(îÍ`_û­+ú}nÞl|¥!! } ±CIŠRÿèÚaá·UÓ[¸:_(;&|<¦½¢yt¾Y€_¦;2NjWÊR‡‚¬Û´<>1:ë5kP òøã¿ç=ï™1c¹"[¿JÅ|Ó†rshv³1>^3WÐ5‘Ó4»Ù×(ç¸H!lE¨ áøJçW×j*á+kËùv”%€l³îD†È¬NàÈHmJÕJþ_Y[_i[Ë¿øÓ[ïúÏ'cW ?ðÆÛ¥_´OÍ·OËÛVLÙ&Ñ©ƒ…S A…ýí˜w&÷˜h÷lR#±+×3 báA}Š… ¥Ò1ý õE°¤zÙU2“»S™gTWéL× eòvª`"¢Âþc¾'t¼`;yêÍÇHj}{/Vç°ò¥kŽ{-?e¦LppÉÅÔ(ÅjVž^¶%;C^?ì=‘.£ë¾s­-#¢}D´é“ÕÚë3NôË%Û*ö6»RVØ™ÿY¶pê%‹¦úܼÙö¯LgC‘ý++: ×ü€^¢¾£Ýãl€&â™gžY¾|ywwwòUýl]QÌ9ò‘‰ÿHìï+Ý«ÀW&{´Lí™¶6%PP–&YgÝ ï¿YÀ‘‘ÇHÕJþ“)ð•1}¥=÷ö›ß÷ÖöÝwß÷“D"Æ—“ÇO/ïÄk<Þv‚¾Ó±¨håø¯Žeqr¯ÿƸaüöàñ=cµzájU!§}"yToÕÛ3{Ûd"ìk_¶pÚìþvŸ›¾¾€$¬[·nùòå³fÍ"«—¥3X ü‘ÈI°£¹}¥¦tÀg÷´y£IWOvøJwü€Êv‚T’w¸ô,>íy¦uÏOð# _ j2†@šdšugõê;èBÇÆI_™ÔWšÿ™3Øózó¥¿+5s”‚ð©éTñpDUßg¼&¢e §Þº¢¾2Æ|øJb±nÝ:Wñ˜Ù«Õ„à¤8DÛQ:ùÁX–dú?'\†²JËûJ–ôüÉŽO.êKûσKÛqIJ*)Õ§bª zY@jdšu'¼pªJÇÆñ•ÁZ0Ñü–ó•öôò‹ß={V÷³/¼0N¾2ýCÊxÝú<¢Ún(s_¹láÔKM Ôuï+ͨv‚BCLz›‘l‚âZº– ŠÈvDáïç Өì¶€Ò¼1f,Kg%~¥'–eœé ï‹æâÉ'Ÿ\¾|ùôÓ™YÓ4"’R²”jo>!œp´Fa¼P+Ú/]#ÖIJÓ÷iÂvyÒŠÝ*‹Pºº3ö°Ç~¶®¯ ÈÞ^o7/nP[ , 52ͺSCÂÃûVm7êÀ§ÀWF¬8g°gÎ`Ï[Ûw—ˆKøÊô)ãuëóˆj»¡l}å²…S.Y4ÍSbãõ¯ŒìYAòñ„;“¬@¼ŸƒV`ݺu—^zéôÓc7{ªa”Lnáè¤V“dgß²²¨ §c¦ÛWš‹µ®¯ô»æo|%h| , 2ͺ>$¼:¹¹[$Ex ý_wÅ9ƒ=sfuѺ^yvý+ð•YRÆëÖçÕvCYùÊÁ¾öÁþŽKN--¾¾€Œ[J½ø•ñà’Y÷ñ•vŒEs«#uÑQÓ¹}eÑf¶¬¯äà–‡Û%Ï´´F—KéJnÇ®Áþ\öE;Ô(KHL³îD¦ç®°ÿfà+cχ¯ôYqùÅïZ~ñ¹omß³mxϳë_©Í©ƒ¯l’#ªí†2ñ•ËMìë˜Ýßáû† _ _ @êØÃKn• ¶ÁÏW²â+Yilò Ri7”øJjq_)¹Â–§$åŽÚò¡%h , ²Ëº-C+ë¿ïè"œ)U%óOøøÊ²Wd"š3Ø=g°{ùÅï2Ý%¥¯/á+à¨á+‰ˆ–-šFD—,šêwËÀWÖÜWJ<Ø„ Ð ñ¢:6Ar.¼è¢Ó&O>ujŒˆtíÒR†såkšæÄ²4Œññ‚¥u¡¤—š°»XJ)íQÞ,…­E1§L1÷Ž0Â(‘.[ÔWrÄTß?øJP?@Y@¥dšu'\†Îª¤ÿfÌ£ w¦T•Ì?uìYà+#Wô.cºK"^~ñ¹DdL{ág×ÿwøÉ˜=kƶάÑá+›ãˆj»¡2}å²E§« ]²ètåKéwËÀWÂW!‹—,™>}z¢U˜,¥XÔÂ뉼YtˆíHžतߡÖõ•ƒñËþ±JÁWâŸ@ýe •’]ÖÈáØY÷ml•áåëøÊÈ£w`ÎàŒ9ƒ3Ô—_tnÂú÷‡8j£Ü:/wCL ELb£ä·¼waN¡ðHç•°º8‘¢â˜W §ºcIÏ—Œñö_éweÂW%‹—,™6mZ9A–éc—@T|%KbÍÑŽäXIÊXrµ³]‚¯Líqý+A³e ‘]ÖðΛ”}ßFøÊØ ¾’b(¨r¸ìñøð•ËÃWÂWÂW&»á+A3sþÒ¥S¦Lqß—Ì1û¤+éwHSD)fPÕŽ$\ R8RO¨éwŠ­]KúJÂR¤ÏÒ™vÍ׉\Q¤½ºzZ¥²!ƒW„P#c*³ÑãÔ(K(ŸL³î„ë ǛÇ!|Lzuö¡€¯,{EøJßs_ _ _éweÖ•¯D?#Ðt,^²dê”)†´‚N1Kf6§ CJ)=Ù¨UUü–% b©±‰’¥’QG*©x4]*“Ø3rÜqvð•™>¯Æ^í¨K , |²Ëº³zõá d=$¼õR„'zþƒ¯Œ\¾Ò÷\ÀWÂWÂWú]™ð•MÚ =³êŠ… N2Å–’neYœ#Mè{³˜ß²$&–,…$k<8KÍmýÌÅ\ ²xSÁWº}ešŠ°òñàfVwê(K(“ì²îÔ|H8|e<‹_I1”GT9ð•ð•𕾠|%|eãsæ™ïø½ßì¿W¬kÃÊ(×ññqßéB¡àœÉʈWÏ9r4‹¦ù SMÓ\ûdå>×ö%±wß¾}ûöùÎ9çž›ôÙ]Wz_JuÓjF{Z²4EÜ«[¶TræÏŸï^ΑqÒ÷í3Üö•¤úJf&r§ß¯¤ø#òcœœºó•è° RÊÊ$£¬;‘$+‰‡5kÖ†ûJʾg_YöŠð•¾ç¾¾¾ÒïÊ„¯ljÎ<óÌ3Ï<³P(†aÎWuäñcÇéãÇìé“'OÙÓ†aHY\]º“–Ŵ躣&5]w^w•iB(Kà ¸åÕW;æ{8‹/Ž%kØÿ‡ÀìÏXœ¶*„ˆŒbGsgÚ0 …‚ñܳÏVr æ/˜?eò”€ÇƒØ]a™Ü¾Ró‰_)=™Äýç´%sZÚW†68®AúAó¥To éˆïÄÏ»®±êêmRÆ4R*P–PÙeÝ l>oh¨’ø˜‘DFç¤ìûxÖ1ð•‘+ÂWúž øJøJøJ¿+³~}%^¹SÀ¶B1 ÂmCÜÓŽsJ—Isñ£¦ž3Ž%MÔN$„m9=¿˜¶TUû„zoc )LY*‰S 5¡ŠD’¥dE_>÷ì³A]>ãpÞüó¦L™Â’íÎ›Ž³#«K§§Ã£ë†`R¾3"kŠ_)'¥¢ð$I&¡¹2†«éwH5˜-ì+ë@í¥P¾¤ ”%$&»¬;‘=Ëî¼ó¸"<Æs|%•)ÔU%*¾ÒyøJøJøÊdW |e\<]®âX‰ M© ¡©šRóÁ­N3³f•–R‹sžØ?]21)K§·Z ±,GY­.£†3ó³ëÒð•¶“cï¹³nžâÿ•œV÷/&[ÃóÏh©L)C'‹U ê?;ýŽ5m%—‚ZØWÖÚîÁW‚†Ê“QÖHš©.¬+_¹fÍÚª¸få ?éä|:khh(›ç:øJ*S¨ª(JT|¥ÿòð•ð•ð•É®@øJP¿0ó3Ï<³wïÞ²K0}e’§ŽØ!ÇWÚ Ôq”¤¸K¯¯´µb‰¯¤÷•Íe,á+AF@Y@2²Ëºn +)9á¶ ;`W£†®«üã7¿‘ÞS|%%|ɇ¯,÷"¯ŒuÂWú._™dÏá+A‘Нœ)P–—ì²îÔpHxdÂʸƒg_¹"|¥ï¹€¯„¯„¯ô»2á+yæ`~ê'?Ù³gOÙ%ÌŸ?ò”É=±Ë0*i¾m¥¨~e­SÌ^<>'ɸ-(‹ÙÃ[ÒW&–}ËÁÔõ"|%¨P–—Œ²î„ Ï´‡c¤„¥Œ;xÖó»ƒßûs ‰Kô$*¾¾’¢ª¾¾¾2Éž3—ÛdÍ3Gå¾rÁ|ïxp±Éø×ù®Co»}¥°â_úûJóÿ¬e¬ø•VžqGP|e‚K¤òk,õ‹w.¨ªâQÖÈNŽeÇŒsDõ“"¼dß^K¥œ¡¡³Ê{ ó{Ž!qb‰žDåÀWÂWRT•ÂWÂWÂW&ÙóX¾¯â ª¤â+Õ9Î0püƒ.„óGDD§Ÿ~ºoù»õé_IÒíãXI îö•ÊŠÄR”²•}åh~bìÇRøJк —%Ä"‹¬;Ñ#ÍË ŽgÓuë+‰hÞ¼¡yó†Ô9‰²„Û Uò<`0à+á+}Ï|%|%|¥ß• _ÙÔxâWªÓjtH;ÍŽ'He[[›sžî,Ã0¤Q<‰RJ¶®ù<×Ìì»Wê|O(LC8{¨kþs¹èwgO€MõƒTb £`O?öè“•øÊ L:µ¸9Á¥§€%ëºnÖ¼’ˆrùœ9Ÿ™s9ˆ„ )ŌݴV×Ô3¨k‚$[ÁHI&$HÎ%ˆ˜HØÝ*ÕŽ™D¾‰w¼B°uúW2¶*Ë®®ÎoRøJÐÐ@Y@4eÝ i>oh¨ìà˜nš2 Ié«s"ó¥ò<–ÌyÅ=‰Ê¯„¯¤¨*…¯„¯„¯L²çð• þxìÑÇvíÚUöê‹-š‘Ó5bÖÌ—lº0¡ 2s18 «åÅ[ŠÉNžŽ™V-µ˜¯ éÙÓÛS‡r¾Ô (K#‹¬;«Wß¾@FÒ0r„;eÙ»³Â=L”ö£Y ‰Kô$*¾¾’¢ª¾¾¾2ÉžÃW‚z£bõsáEyòƒWÑÃÇ,['\=+ÍÛ%³ìRW’ñb‹_Yüjói=Ù]!©7eð• †``8’EÖZ ¯óáîy¦ïÉT|e²§gøJøÊ +¾ÒwøÊ${^¾¯Äû9È虢xi­\¹²¯¯¯Œ.¼è¢éÓ§—±Å@܃‡Ï>ûì GséB²µ´<%9O´¯´Lb ö¯dmÌ ÞÕÕ%œßaC»]ýšI«¿k™gÀæà+Am²²ÈºißÊN>^áv ¾2R3ÁWF–_ _ _é{ÁÀWÂWPÒž”a-Sö•‚H‰õpôÐ1¯dÕW²¿¯´C^–øJ¢Võ•Ä<ÚÈrÒ¤I ™2R˜bR™OäZÊš“ ¦}-x¶@Ý€áàOYwÂËœ74T^òñ ·KYFϬ„˜¾råÊD´6jÌ{ïÉT|eôÂT¾ó‚¯„¯,sÇà+á+á+3Dµêéðˆõ+5Æ¥«(5ö¥®+×¾´W—ìŽeÉþWTP¿0uÏê…q+–å„ ¹¼ÿ;rG{{‡’ÊÞ†áÙÜ7ÞøÀìܹ3NU_ºüÒ3f”V{.§ûÞ²šNT2ëºVŒe)%YA9͘ÐÖFD†aÒ˜0aÂüùó_{í5"B³Ï”¦ MÓ~±óÀù³gèBèB³oÕœ¦‘e³}üª\3œ%+bQ0 5{xq[ÆWnž8*¼««³üÇÖ „›W:PCÐËüeYêYw"Sß”3’MÓW~ö3Ÿ¾feõ_ _é{.à+á+á+ý®LøJB)‚]Òõ×_?00YÂå—_nûʘ[,ûjÙÐKÛ÷Yí\©+”Šû“j~p÷ÂV…´jÿJ’a],»ººR¿ÆÊ¾jqç‚zÊ|H=ëNä0óŒÆe7¨¯\½úΘ¾rÞ¼¡,^.’©(øÊè…)†q€¯„¯„¯ô]¾2Éž§ã+ñ®Ò{¤ˆR?þð‡ûûûC¸üòË{zz’l±¢îîîújôÈq·‚$__é–•ñ+ݾ²s´#ÐKöööfq•Q"î\P'@Y€—,²î„ ¸ŒÆeGöë¤Ì²“WB¤f-îùg£}åÊWWøÈc>|e™U_é¿<|%|%|e²+0 _‰×u1]Ò‡>ô¡ k™®¯ôÆ+ä"$HÓ4¡ ¡‰³ßœçȉÓZ:±,=ŽÒë+‹šñ+‹yóÔ@)©Ž W³ïhBsþtçOhÅa÷jXRwtR!H¨«“S‰és¡P¼§å\…$±,ÀEYwV¯¾3¾eà #„2ËN^ I|弌“ÂWF•_ _ _é{ÁÀWÂW¶ºžSê„•ƒõH%3k4kš¦k™$¾V IDATZqZ×u]³oŸ\.GDRJ–Ò Ü)¥df3Þ%3wwwïÛ·ßÞauÏ_Þ?{ÊDó~DÂj¦Š±,¹¨ôŠ{i=5EO ûÊÑö®ÑŽIA×N£Â™ê/~%|%H(Kp‘zÖ𰘔7lÐá‰|eÆû_U|%|%|¥ï_ _Ù설ÜQç«íi)e )Ê7åNÊÊÒ~=Î…(Ë||_c6 š¡øY0ÀÑ:Ön¸á¿þë¿lkyÕUW©ý+ éÁÒ°ç !4Qìy§©ùs¬ô;¦ë4•¥é‚̓’RJ)Íeˆ©»»çÀšæ(KûŒŒ99räÄÌÓ:X0 fµ f!ÈNÂãò•ª%$­ê+‰ió´°qßIG…×ãxpøJ6YdÝ©þð&ö•ó††þù;ߪݞÃWÂW&©.øÊXW |¥ï2ð•Iö<}_‰·nPáãB™—×òšk® pëA \Î=÷œo_Ü~@Î¥ºÐ#‰|dbëùÊÑöÎ.–½½=É®1øJР—%8¤žu'|HxF©oÂ;ŠRf¡3ËfëÖ­kÖ<ÇWÖ4ò&|%|e’ꂯŒuÂWú._™dÏá+AQáXÝk®¹Æœ( q·˜pƒÅ“BÕ¡ÕìGiöe™ùÜsÏݲe‹ïê;œxqxÿùSœaà.éJÎS¼{í™Ô¢¾’˜7Oë 9)=½=š®†P¦ýºý !4MÓ´bh3©ý¹ú8Ç»†* Nð• + , HêYw"³ß”á@#i¸áqú„|%|e¢:‡¯„¯Œ®øJøJßûoÝéà‰eé×’JÂ\úÎî¹Ô3§Å®eOëº4‚ÛOþ“êùM¹\ÊêeìÊâ; ž‚† )ìmšÞ‘œX–ÅØ”º®ë9g`¸9œ%K–Ÿ–†”BšË˜ÌŸ?ÿU—²t ÿÙÎÃ3Okèš œº³bV’×WZ·a«ûÊщ],»{ºkx·²oždE åYe Eq–nÖèËÊ<|efÏr庳(o_ _àM*(¾2ºþá+á+}ï;¼uWDP,Ë ¸–¡„ƒ¦9XG\3þËH%«Š®çcY(K¶®4ö©UYþú»«Ç¿®¤«}pÄ«æÎð¡,™‰¨-ßfÖ€di”!¤”Ru²º®Ÿ7þ/ñ «\o\ËŸí<=´‹eí”e ­|%È(K Ê ëNxó††ÊÈ<µÅµ‘c«›ÕW^³rÅÊW+%¿'Oòg9øÊD»ð•ð•åÅà+á+]Õò•x•=.Ti‹_¨çwÞî]»vïÞíûíηO=¸uÏõgw[ÂN² ÿø•­Ý¿’˜™5RÏÝð•e ég݉ì혺:ŒìÔIÙ¤&¯d‡ãøÊkV®(Ãí 54tVÚÏrð•‰wá+á+ƒ®@øJßeà+“ì9|%¨3ÅWºº¸ $̈B¸Òµ Í (Î[0ÿ‰Ç*mçѱGNÎìj#)­Áàð•%¾ræPøIéíé!"MhjD‚œ2H_×5»eÊéJOXM×­¬îš.4Ýê « w_fõ¤‹4/Z´– { , å¬;á”2P‡ —"<¦¯¬õ>ÃWÂW&©.øÊXW |¥ï2ð•Iö¾²ÑŠkIÁ±,C†\!ž‹:z`¸gO¤)RhZÐÐu]÷0.•Aáž- Õ©›óîa@²åCNi(\õ ݶQ³¥rr³0kº®ëº}µ›À™¥d¶æ ÒÍSÀÅÓ¡ ­¿¯¿§§gÏÞ½ÊÀp×~?øúþëΜ6Й·î*øJ¯¯ aIDgœ17Ö#iê-WÁ­%¨ P–€V'õ¬;á2.õlÝ ç+#» ÖÇ>ÃWVÃWnÞ»mû>súÙŸ¾Ú ×hÝس÷`È·Û;;Ûëµê’Èõ»ÃËœfN-[xš2¾2ÉžÃWÖžø•AóãĵLª&3R–𦉀X–ŽÝs_L‚ƒ7­ ß_ÿ’'G*uâÊûÌþm…û ÈOYkš¥,ÙL¿“#")™™ÍÞ|BB9@suˆ®xÿûï½÷^M èµGôƒ7\÷Ž©3;ó®ø•ª¯”’ ¾Òï—·³³««+úÇ#_Y눔% ¥I=ëÎêÕw†/úðð ™”$­øJøÊmÃ{· ïÝ6¼oÛð¾Fk0ØW6rÕ%)ëz‡Ÿÿùóƒ9±lÁiD¼lÁ¤°­ÃWÖÆWJ< ´ì6W\ñ¾Ÿüä©~ð›ƒ×1eVgÞ×W:÷|¥ÂĉçÎ}¥.á+ACe hiÒͺSý!á•"¼A|e8 ïío1­ì+MMùìú--ùf _Y•¹ÁvØ—ÏÿüíÁÞ¶Á¾ –»„¯„¯-Ú´e‡°úkþ° a* DD‚zzz{zz‚òð˜üàÍC×Ï<01çõ•¬Ü¡ð• ==ÝBv—^3x¨ý­×Ò앬ÆLpuRÖŠ¡HÍåí¯4!‚‚¤qÝÃW‚ªe h]Òͺ9@»Œ1æáÀWVïe¾2I!_ùìú-VŸÊÆ}Ì…¯¬ŸÃo_©²}רö]c¦»\¶ k°oBèÍ _ _ š°iËAö€q¸„&œTv`°7¿l~×`_|%|%¨‹¦­}¥aŽ+¶b#jf—K!ˆ¨··÷òË/OÅCD;O?>qþ´¶¥Óò­à+GÛ;7Oë‹ NDsϘkwMUGp— 3­ùů,eYihKøJP; ,­HºYw­\º »á+kôŽ_1ß|¦¿ûþgKò€ÃW¶âK}• l^_i³}×ø½\6¿sÙ‚Îð¾2xaøJÒÝX¾2FÎeYQ\…HhBcÓziD¤ë޲Ô4­··çŠ+.òÉŸDnÿÅc/;jnéd½‰}å#gŽvLŠsBÌþ•BÍə㼯뺸@‰àšV ¸Ca Í1¡N±L™!æu{Ô(K@Ë‘nÖÕ«ï _ ].[)mCZI%ÃW¶”¯Ü6¼÷îûŸ«ð]kþùçߊÉFm'aÎ`×î2íÝ¿wô€§³œÁ%6Ä™Ãδg+äúÀö·ÖòêrÌλ«³Šû¤Û¥xu »ÞzÙµKîrÜç„ÙûÖÌ겺 Ïl"":9i"y󏧯òù|[[Û™sgžõŽ™êÆI©~ëp\õ¤lӻлÖYÙ}ÏLOÉž“Ã.-å­¶vÝ÷œw»®kÇYž½{îSÇÅÿÛ°óˆ§Ò.èëô9:öž»„Ç ;e·GÏ¿|tû®S·|p|%|e£dF<Ö/h±2ÄJLŸh—,ÝW‚’Æ$pÓ¶i Û r€N#oksŠ5 iŬÔ5ÝŽki†!·uÃ((Ód)í@œÂÊÍÂ̶Û2k)—Ë‘”’¥Ì·å‹ÓÌæ|ÃRJaí¼Ù“¯8mõµ|ÿûßÇZÑ‹ /,œ?E?ÿ4­É|åæ©½›§õż>ãŒO?~%a<8hx ,-GŠYwÂí'¥P²QR„Çô•׬\‘nJ¢ñ’XBøJ.‰\¾®Ã‚¥ï2ÿŸ¥dË-IÅy1³,*>K’±ó¶At¦9_2ÿ÷Ko˜Óó̵E‘4_x,Ee Dûu‡Øž.~%¥ýRb'jp>š‹1w²X¦µŒò²ÁL,¥£;•}0S¨U•éåÒƒî]²ô˜ýÁ®2u]‰‰ÆNëdæReÙÖÖÖ9±ó¬wÌ^~á|ç±ø)í÷Iµ"‰ÙzßWÞÉï SéXB×›*««y÷Üï“ä]èˆã’7[bçJ!wïbõ•Õûÿì\rä>– û»È¾(”­²&.-(oîÀŽã…ÇåŽÆÎ2Q{´}×ø—ÿm÷-˜b¥å¯ÌÔWâ-ÔüI¤®]QOOÏ%—,Û²åÕ}ûöÅYþÅCÆ‹‡ŒóOçŸ&ÝWŽNèÜ<¥w´£+~uÅŠ_Éé_B–Ô(K@k‘nÖj o2_Y'£×cÊ‚àgIøJö ñè½pé»{fôÎì¶DAy¾’l_ÉÌg/<ƒˆ˜%ÁWúùJ×Ú¾çŠ ¾2ÀWÒ…ý“Šþ«L_éìÀ@‡>С-¥1ï8n첿™™)­ÿZyÌ…ó«g¥20Ü^¸·····wݺu{öì¹Íð‹G¸¿Úh òä+GÛ:GÛ;7OéMZÓsçÎ9í´Óìê¨mM׊ƒ¾™4]Ó•Aߺ{¸2ßËÒé¬)!2cuRð•ÊêŠt³î„ OW Ff$§´G —G¤W…¯le_Ù7³gáÒw÷Ïì‘,q³iøJËèÁW†øJÕ;Ø+øJ?_i ²|%«£Ý™gvh3ÛÅÒ)úƃ…‡dÌûèù—mß5v˦ÀWÂWøÊ²}¥©¬ì •‚4²‚š³]šÒOYêºNDï}ï{_yå•W^ùuüŒÑÈýŒ‘èÏó@^‰%…zó•£ù‰£mf·ÊÑö®òjúŒ3ævuu©ÎQU–º¦Ûñ+ÍiGMN»ô¥ SØ[Q´õ‰ü¦|%€²€:"Ŭ;‘1Ñóp"M+¥=¾¾2rþ[Û÷”d\·ofÏ¢ ~§o ›ˆà+ëÒW|e•}¥zz—NÑΟ,6’/æ8âlû®ñ{;tó•“á+á+Ž 3‡ûKZrPJeQ’mÙÆ^°úAJe‡uõCٴΖ“’†¡KkZ—R9^ÃÈEÖƒtRUcÌ$D1 ¥Ùýÿì½y˜Õ½ÿÿ9U3Ã: 3 ³1ÌLzÝLÄ  bDaÆ„DŸ¸€Ñäææk4î>¿›ësŸÜ<7yžÜ‹`³³™³©˜™Á-.€D4šåj÷0,³±‚8[×ç÷GuWª®:µvwu÷ç‰TWW:uªêÔ9¯þ,er™VˆŠÃÔX–)4Æ€)\ÂIÒâZZ´ ƒSO;uæÌ™7nòqA‡ÆØÐ˜ ¯—ß[vƒ9Ξ4eÊ”ææyAniÿÏH¾#õ¤¤EÈ’D"•ŠB̺ãl­éÑÁ\|¬‚HN¼JŒWZ%۱ط¦®ú¬…§×ÔU«ƒrâ•Ñã•FÐC¼rÍ+Ó‡†³OdgŸÀ¶Qþò¾ót¯oïØÃO¹ú’ÍÎÐ3¯´,Ü™W"¢£Ó$‰þà#ûP-+G@˜9sæ§W~ê·ßyçxdÛ6—‡kjš[YYé¢å0j§©îÏ—@"…%B–$©TbÖ±µf[,VVâ•y⺌•˜?¸+^¹`áé ž®¡‰Wæ•W¢ód†x%äWê—'ÀÙ'°ÇÞÅÁ‡î©ŸJ-+mzâ•–…»ä•$R‘3µl ”æŸ<æÌ™ï¼óÎK¶m«ªfUUWèib˜·‰ŽY’H¤’PˆYwñ\¨!,üÁÃÍðãOnxeDR…1î"^‰¿þýfÇ}5ãJ ^%^)2è@ ^™w^©=›Ÿ™ øþ ƒ¥Kÿ¾±‡ŸzÿêKNÈèˆWZN¼’ÕG®n<)’’é}R†Ç·Ä$ƹÀ+2j~åååå|U—sµ@6ΠfNͬªY{÷îKÄ,©¶:uÊìš͸RâšT.Ó©‹,KZüJIÒSîÈer·YÅ„ úry…ŽoÊôKP&Ëee©Ý%I’ÒádIâÃ_“\žB@Ûrê=IÙ!K‰Tü 1ëŽØ»B)ýáñx¼³³›x%åÛÉä•—]þIä¦ Ä+óÎ+ÅÑ7‰WF„W¦’óLÀ•'á_޲ÁQÑ,²ßxßÞ±†ÙåVt¬‰ñJg^IŽá¤pdˆýˆüzS$MÔ8rITw3 ‡ÆÃbY2Ií\RuÌGgÀÒ5TÿQÿ›.<¿ÒúaÀ™3gΜ9óÀy—¹á•³gWO:uêÔ©L’òR+Œ.$^IÊ’Y’H¤âWˆYwÄnÚ!Ú<¯ttZ‡Bå•cÄ+Mû.XxúY O'^5^éf E¼2:¼Rݽ¶WÎPÖ”ÇDøì·O½jYeÃìrâ•áñJš}G–EÉhäq%³lUØ`ɨ±-DQ³ÈC‰i73æQQ¸Ý È’Çš\î mY5†4ÔD5¢D`ê¢Â¸r%€ÑZPk!4ÑX+Íœ9sÚ´iï¾ûî¡÷õööæaÜ–e^9eÊ”©S§ÖÔÌöX«È±BŠ_IвY’H¤"WˆYwÖ¬Y+Þ ,6瘎Bu?÷תÄ+¡ÄxåîþwyeÇ埜SWM¼2’¼ÒE,Kâ•Qâ•Z+§¯?$މŒw~ûôÑÿïÚéÄ+m '^IŠìà"7æ·Ä3f̘1£¹¥ù½÷Þ{wÿ»»wïÎUÓf«m§L™2uê”)S§L:•åÛ‹¢I B–$©ÈVÖœ¹„;º±C¨îçþjH¼JŒW€UÊâ•Ã+Ñ9-ñÊ(òJuaå´ñõ‡ËÄÔòá§^½lªÕCJ¼R[O¼’©ÁEŽx%ci‡q»“˜æ<®€ˆš¹¤èQËËÊT7sPþÒíFeI’ådjY–“ÉÔrMMÍI'͘rÛ{ï½÷Þ{‡’Éä®»²Ö´!·mee¥¤²¶vޱá˜1 (·,ë=¶¤2¿RÖMʲ\Æ-ó!/'NÐãWVT”[.K’$§"I’œ¾ÆJ¥B—f‰6RIʶY’H¤bVXYw!§>i`~ón»ä•—u´‡•6=ïS ›µ¥Æ+7‹÷%^©Q¿hóJ͉WF’Wª +O]¤B@-û÷¿ôÖð¹gL$^¯¤i8)|1Ïn^búÝÇLq*¹8‰Ì.–%ð±,MdͲ&©: ÕLz#äL;?>qìñc²ÝÃÛ¿o¼oïXÃlÙ©÷ ^I¼’”§9J¼’qùÅ1mh Ö…’$i_–qÁù']’$-f¥,ËeéådYR[2ÞQ‹çããú6Š’Ô¢dò—¦¦ËÄvYá•Ne2fv¹×9K™¬ÕJ–ô“•dI;wc\K©Œk“Š =–å„ ôv“ m¨íΓøX–’u‰1-Û;Ëþ=F"!K‰Tœ +뎸œ°Ì‰WFŽØpÛ¯3²4ï{ÖÂÓ=úƒ“H$¯Sfƒ+z­<þé)Çÿ`²ÝƒùÒ[^={ª°÷ȯ̈³fƒ G·<¨Ý±\T̆WZbK]­®òoäBïêb Äg¿ŒÖ XÊ&c¬ŸeÛ´Ëȧsñx?Ø•“ÙÖ©=wk.P8Ž*àÓ¤ø•¤‚!K‰T„ +ëΚ5kÅ„‚ÏÃ:?•¯´¢•&¯ü?Á¾ ž^SW ^âWRïD"ùŸ9§C^ÖÊÉ9òøP²ÌòÁìß—ìÛ;ÞP-»èm²j_ ¯tak™[^éôFÈ3"!Š$&o'‹Koæ¶N·Gæ×Ô2£&¦™ˆø•Q.0”‡‘âW’Háô–Ô$©ÈVÖ±k9x‰†¤¶aÈŸÜðʶXì§ÜO¼²øx%n~å»}kêª,<<æÛ¡ŠDò9s6¦úY9ù¸à¡~é­a½M®üÁ‹ŽW’H‘¨d­ó ¹È’à•@ñ+I¤DV–$©ØVÖ¸„G9Ex<ïììvÃ+óhš«i@ÉòÊ·ûžåW*4Ü%‘|Ï5 Ä>V1üÚèËgºo²oïxÃì2â•Ä+I%ÑC„ZŸCŒf˜|„D‰ªhóì˜B=&Ųãá·QÉ&–¥i’öÝÈuÙDښ׫ñ+e>~¥¾,K’”ŽéÉ£”%‰yiØ1LE’ —@;:c ¸Zj«^M²¯$¦Y’H¤¢RXYwÄ.á¡pºˆóJǺñJÇ\!óJH›XZì[SW]SWí•W ‘|Ο3x%"~¬bÄY¦²¾}I=  ç!^iß’H2Pñ"\Ù½”–p| M>÷‹dÜ)ÍθeEQ4²‰Šb´S’xd©`z3²Ô¿ ·¿uÕŠÖÈR’ôˆ |žYJ’¤}%˲\–NÅÃ$~-Ó"J’uÚ"‰IúW\Mxzi¬nNî1)T²$‘HE¥P²î8—tŸ½Gxð°rûxñJnˆVº¼rwÿ»‚},<ݯ¤A/‰äo mæ•ê?+~ml¢å³þò[#çžQáÐóy%¢{^é˜æ[1žZÝ7¯tIÅ»O²ðôÓ‹Ëô;†ÊÙ¤ßá™C1ýücÇRŠò'.;ZÈc¿3lƒ†qˆU[!¢bÀĨ ª›¥?ê…†žþ(ôëÕø•‘;ͼį¤Ÿ¨I!ŠbY’H¤âQ(YwœCaºÎÞ#PdS„¯ä†h¥Ë+!ebi½oM]uMm¯$‘rÙeðJY žõ—Þõ<èÁ1D^isôhñJºçH…Ñ5D3ŒcФø•9;§ˆÜ–¤YY’H¤"QXYwÄÆm±˜Ër*t^yYG{ðF(Bà8x+Z^ º•¥…>zöià‹WÒ–Dò9e´â•êÂÇÊ>|m|’åÓÕ·7 gðG‡î‹x%‰”ÏŽ!l0Ä€! 0ÆŒEc5r–¿¼ã³©.|€E=”YÃý©¢ÛçŒÞ¢Ð"‡!¨·‚›†24¿’?_Þq^’õ—$¦å‹—eIâbYJÆP¡Úy™œÁ­/M†.ñJRqˆ%‰D*…’uÇ&'‰Ž^ç¡Ň\òÊ|…×Ì1pñM1óÊͯüŸ]ÔÔVÍ©«öÇ+i K"ù#†–ã•ø±²_›d¹_ÿ¾¤u‡ZüÊâ啨Ð}GŠv¯6¯d U‡Œ/߀&¹N@’è˜R[N²ÌL0ÚÜZ.–¥YÒïØõ$®¥ÉËÞ¾\5—‘Zj°C–2Çjc’–K‡KÅÃǸDÛø•’MQcTMÛ8¥! †sw[’H@È’D"‡Bɺ#.¼dBl ÊQ|È‘Õju+z^énðV̼RZsØØ–[îõÒ[£çžQ!æ•ægÑ]¡qÅý¾9Ì·ƒ>½Ýy¥ztª¢(Z‚c›öà„Ï›ÌíÎSÒ<‚)J@ªRH7¶Ù¾›pŸÑA*v×Ð6Æ¥ñ,|Ü»ðw}^ÐX/ý|d,Ï~ hIŲ$‘HÅ P²îˆ ž '²)‰Wz¼?¯Lç ·ÐGÎ>Õ7¯¤á,‰hJlÅ+aŽ4æ¶ûrìm\ý’aCö-^I"E¢7yˆC¼2:×7%Dã¶$‘T‘•%‰D*x…’ugÍšµâ :k¯,è¤ó@®ì+`v9©­à IDATmU^IÉ?¡°á•ø±²ãÛ’“-÷}ù­ÑsÏ(ö6ÞüÁ‰W’Hùï BâX•i“ÝÜ.®¥$1LÇXdLw?z+Ë–Å*ˆ¨èÎà˜ŽBi:w»‘ ŠÎÓ…c¸è;>p$³<c¼77ãÚÇË’éŽáL *1I’˜V¤,YǬsüJC®y­U|›b¯$EM„,I$Ra+”¬;9p ‡Ú„0¬8}ˆx¥ÇÁ[IðÊͯ¼m×5uUéy=ñJ)§ ÂŽWª+ç°Ñ!¬p(ÁâÁϯ„Âã•Ô_‘"Ø d!~%jñ+­¹¤ù´‹kÉ3G`¹S˜¢HŠÍÑùRõ¢E1ůLw~&d©x¹¹9´Wê_ÈÌQ”>ƧÜá%ãø¬Ä$‰@©¡^=Æ%ØÇ¯4Rc°$¦õ¿’TÐ"dI"‘ [Á³î8Ú?º´Ó(š)ÂÝðÊ|å.ÎÁy W|ö•öƒÏÙµUˆWÒ¸–Dò3#óÊt8KkdÙ·7ÙP-™v»ø•ébõÒõM>~%Z‚ëÆð‹…7^‰®x¥%jô”*ÝŠWÚCTP>™L*i²ÀŲL&•¤žõ(©èËã†õúᘢHɤûàòƒ1ü¥Á̪ˆâZ Î…Ùœ²›ø•…ÕD‚—¡ŸHVl61ê†R(fé<\bò'«Y’H¤V(YwÄг-sc§)Pye<ïìì&^éeðV:¼w÷°k‘šº*â•$Rλ"^)~¶ŒÈÒ¤Ý6^, ]VòJ)z}@ØebAÔ3‚­é/ýQ–«æ7·‰”)B–$©€<ëNgg—˜Ü¹LÝã»|%Ó«ÜDÕâ•¥Ê+`÷€-² È+Ý’Hþç£"^ù1éØ6eŠënÍ ¯tÓe¯$‘ròô‡_fAðJŒbkFÏŽ”x%©¸EÈ’D"ª‚gÝqŒƒ0†£cùF”L¯U"^éqðVZ¼R 3?þ/y% pI$¿óQ±}%º˜³zB‡nº¬¢æ•ÔY‘¢óô‡_¦E¡Œ xh²Üf\ EàH\°EEQ4»Cdz”K&1¹Œ4’¾ 6AQ ±&,Ç Â("48h4²´Žiˆ`` F©}eŠ_É ±,%þèÍ.1»*QüJRQŠ%‰D*H…’uG ïæÃ‰`Špâ•¡nKWr%ùç•Å”Gà&ËrYYÙ¤I“*++«««O9å”Ë/¿¼½½=ÛñÔvìØqË-·tuu9Ö–&…×9òJûkúò߯Î=£Œ»îæ^…_ÉÓ õQÖÖ+|ÌJÅ&–¥~sñUâAòGvÑÝ)6Ý”^i¢×žj½›M?¨(ŠÂ.~¥Í2$ùø•6Ë<³@{@ƒv)ƒ‹4–¥é¼ÜIJôã#ÿCZáØWbôO|¸¿¿Û¶m¿úÕ¯-Zôûßÿ¾¶¶6Gþö·¿ý?ÿó?ÃÃÃôÜgWäÄ+á,8º *…‚³?8·AvÔËõÚz/\¶°†¤ö ƒW"جGqŸi×a)ˆ **« KÃrÒn}2ieU"ÈÒ®þ|Šd°Á‘¬Y Ï=ñÊŒèòX+ò'‘܉%‰D*<Ϻ#.ûk‹‰*6áôÑbnxeŽ)jä!ûbñóJm®„W¢Á³´ôòË//Z´hË–-³gÏ·äîîî[n¹eÇŽôÐooäÌ+]t>âWºÙײËr“¨'Ò¼’DÊ÷CŸ•ޤÊÌN‚ðÈUŠx%‰äZ„,I$Rá)xÖ¬º„G-E¸c}TEWªiÍÛÚbàÎÍ?sˆ¢â•›_yÇ~tK¼2vïÞýùÏþé§Ÿ·ØŽ2‹.xœW†¿Ò[\KA­¢Ï+ºñHÑk-ÓC¡Ì‘7¾æc2¢¢XZ¼M¯ÿ‘ É2"PIj'n²Üöw ™•w¹{¦­®xGÌhl¿!w›]Ø:°¾C(~%©DÈ’D"˜‚gÝ»„ä‰Ä+ÃR<žˆ'jåÕ¸¥m±X> fiÛWÀ;9 ¯,âÑ®ÖàŠ¢Œ;vlhhèñÇ¿÷Þ{:¤möÌ3ϼð \põä$o“np╘i,)êUìâW>õʇÚö&jË ÇT,¿@SePÜ›¡>õF4NÃ%Ÿý°-Ç´Ü ÁÞËøè±cƒC{$IÒ<—%I’ÓyHÔP¶ÚÆÜò}y‚¾^bŒ/Šg@Ìx¡;†g¬¹è¢¥`t ·k ¾¹L2í^@Ø¥Pì+¡üÁ1ûÊð ,mnüÜ5ñJRa‰%‰D*$ϺÓÙÙ%FxnŒ4}ÁBdzUáòJ뫟A0c*ÁìhÏú±K…W¢`(¯,þ!¯$I&L˜0aÂI'tÚi§]~ùåçwÞÁƒµ zè!B–$?]¯4€>{_o‹‡ŸÕ?õê‡8[f³œãgÙzý±c;öÝ’ÙÓœoz‘™2,ó’G“vT½€b\¯ ³ÌèòÊ|6ñJRÁ‰%‰D*$̺ãL<]iú.‡Èô¤B畎©Ì®®nˆÅZÛb1è`–¯tµâ•JézO>ùä»ï¾ûöÛõëÖ­Ô““˜6mÚG>ò‘«®ºjÕªU‚rJŽd8òJu w¼2W¼„D"k-3œBùЖ|XjÉÆ|•ÿIÄôž²°„EÆP’›ÂÃé„pâ迵l^~n^‹v[IŒ¡Í·¿’Tô"dI"‘ F³î8R<ßü.R¼RÍZSè¼2,A#˜ñxüÖ[nuQм‚óJ„RûÖÕÕñ?ü0åx[UUõÉO~òÉ'ŸT?îÙ³gëÖ­ .Ì,aË–-¯€K.¹dÖ¬YAªtôèÑ믿þÑGÕÖìß¿ÿþý7n¼ï¾û6lØÐÐÐ`}(Êý÷ßÿ_ÿõ_û÷ïç×;vìí·ß~ûí·zè¡9sæ|ûÛß^½zµó¥(ßüæ7¿õ­oŽŽj+<øì³Ï>ûì³÷ÜsOgggKK ½û\ñJ´ç•†çÞhªÿ£©))ÙË2ý‘q¡?S/'Ùg†Ak:“óG¡`òƒÄ™‡Õç†Ú‰³üVˆx%©p%QH¤‚PÀ¬;b Mæ²-6Þ„À)ÈÝK…§E`_'Â*ªµµ5ÔQoQóJt¼¯ô,ž6@UU•¶¼jÕ*þ«ÇܲÓúk¯½6`•.¸àžWòúÇ?þ±|ùrÍö“×±cÇ:::¾ò•¯˜x¥ICCC×^{íµ×^«r®¼òʯýë<¯äõöÛo¯X±bdd„^iV 䕮椘Sp@"ùÒ¸PI£î](ŠÏ6ñÊh•IþàÙº…H$"dI"‘ @Á³îˆ­ ƒ Åè¤wcì EçžÛ9DéòJm‹`¼²GÀ=öÿ±¶¶V[þô§?=uêTí£²üÓŸþ¤-O›6­££#`•^ýuÁ·ÿüç?ýë_›V&“ÉÏ~ö³O<ñ„ËCüú׿^µj•xÎóÇ?þQ\H"‘øùÏN½Qx¥iYÉÚ\D"yk-³x%¯ÌÕ9å…Wð$…(B–$©0ëΚ5kÅøFxÄ+³¤Ë.ëøé÷ÿôû/ëh¿¬£]Í«ãOí+Bà•8¯Lm„W–àöŸÿüçÿ÷ók–/_®-OžÎŸ?ÿ—¿üef:Š|pþüùÚš÷ß?ÓÇÜÐ/Ýu—Æ+U]}õÕüǃÒKЯt-6µUêOÑ>*ˆÚµ6)ï:þÁñã¨ÿIýÿðÃáôßÈððèȈþÇi|lLûSì…0—ä©ÖŒœ7xÁæÛ¡w)"dI"‘"­€YwÄåm±˜?ŠákÖ¬uÉ+s“ÿ'"ç«)À£ÜjÔWº¼’'ÚÆ^ye‰GsoooÿÆ7¾aZ)Ëò•W^©}|íµ×†††´ƒƒƒÛ¶mÓ>®ZµŠ±Ì@.¾øbÓ›ÀñãÇù]]†~ø¶Ûn›0a‚eÉ&Là­5@K‰n©%K–˜Ö˜’•Ó{0^iß•).z$‰Þó\²¼2’­I¼2¬[ˆ^ ¤,‰%‰DŠ®fÝqtÜ[h j^éÆÞ°8xe<ÿ×/}™üÁ­F–¹á•œW–lÒðŠŠŠ¯}íkëׯ·$}|ÞpDä“íüéOâ¯uð\᪚ššLk&OžÌL&“üÇ·Þz‹ÿxá… 7}û·¿ýM°q]]iÍ 'œíyxC¯D/]™öQ¡é&‰”ûG9ì2 †W†¢Â+Mµb~ÿ ŒŸ$x%©èD±,I$Rt$ëŽ#îô òĵ‚`ùÇÝ«¤x¥Ë“ÍTGû EQÂõ•:¯ä&0AxeI i%Iš0aBeeå¬Y³šššÎ;K®ºª¾¾Þnû~ô£'Ÿ|²´qýúõ7Üpƒ¶¬mvÎ9瘜Ê}Ë„@–eÁö&?q>éy¦Lß¾ûjR^^Nï>ë~FÌ+Ñ唑x%‰”çG9ì2£Á+…1.QßÊ?•3í›L&ƒ2>Ä0¢W2½bCj¦Š tíòp Ñ „”U²$‘HUÀ¬;bCHßT1")ÂK‡WºŒÔ™™…ó°y%º±ÕòÏ+‹wPpÄ¿jÕª»ï¾[]Þ¸qã‘#GN<ñÄÇoÚ´IÛ&”Ä;©Á_Y™x*hRyyùøø¸¿¦—œ“…dfR„¨#T^©>¹©e…ŒYIÒñã€,ëÝ”$KÚÏ*²,k=X· L’ø®DßÝ”•9qÔ^=6ÝDøƒ‡pâ¡7]ºQ╤¢9†“H¤ˆ*HÖ5kÖŠ ÷G ˆW¶Åb?}àþBç•^#Wš”…Ü;vc´¢á•ÎM„W±Ó5×\£¡º±±± 6À† ÆÆÆÔ•ŸûÜçòU½Y³fñùh›™à?š²ë˜D€ÒíŒ00¯TÓg9õô„’HÙzˆ³P&ñÊÈ\0\Ô1çMM¼’T"dI"‘¢¨ YwÄæ™à7‘·cdLðKBÝ+»ä•9 §Ù>Óà‘+Ãν'ùEÆ+óJ$ b£ÆÆF>¸š%œÏ¾bÅŠ3fä«z§œr ÿñùçŸlü /ðÏ<óLº¾!ôC޼R8q$^I"åó .U^™3×N’ž¯$–ÈÊ’D"EKA²î8ún‹#`ÚÕ‡xenäɸ²-»ëÎÛ/ëh€D0ÿq#À¢ç•.¦4Ä+³¤éÓ§ó\òþûï×–?÷¹Ïe¦©ÑTYYÉ|ðÁ‡‡‡ß|óÍ-[¶„U·ÚÚÚ¯|å+üšßýîw ,x衇öìÙ366¶wïÞ‡~ø¬³ÎúÝï~Çoö¿ÿû¿S§N¥‹OóJD÷Ýš±WQ, [Gr'th MËv…ä“W’Ù))„ç–xeÖ‡eym:â•$RNDV–$)ZòuÇÙ\ÓNb“O|Ü¥\òÊË:ÚÅþòQ–WãJíd¯ri08ÌÕ‹‚W¦· Ä+Ó[$k­Zµjýúõ™—^œ+¼¥¥åàÁƒÚÇo¼ñÆo€k¯½váÂ…aÕí[ßúÖæÍ›ßxã mÍ?þñqžøÅ/~á _ ËÎdÝ‘W:änðJË.+t^騝$×SK¼2Ô.0rMG¼’DÊ•Y’H¤)HÖ1[l‹Å|½¼§wÉò²mæ™U¹I(d¼Ž†ÛÀŽS‡಄x%Ú·Ap^IC]‘V¬X1mڴÇ›îd1y¼ôÒK·nÝš¹~ÇŽ!ÖmòäÉO?ýôÊ•+_zé%7Ûßpà ßÿþ÷隆*ðJ´¯*Nƒ ;²ÚFd_‰Â†x%©ÈÙ¬tá—YP¼Ò·ó>"†ëøˆA2¯$‘\‹ÃI$RT$뎣K¸°˜w^éÒQºpy¥×´à—u´ßu×y8Y╆¯ýóJò k„ W\q…i¥Ø’n¾ùæ“O>9s}¸ÈfΜù /¬[·nÖ¬Y‚Íþð‡?üð‡?”$d†7=væ•ÖØÒ¯ä7Gݺò'^Y<7¥eÒ'4È'"Iº–’L&EûCþÏ^v•ÏÊ!ÄN ô2ɾ2_¯$‘¼ˆ¬,I$RTä;ëŽ#ëô‘ËÛ‘‚/ ê^.m —W4®ä®¾u!Ári08Ìá‰W¯ô®U«V=ðÀÚGÆØ5×\#Þeúôé[·n]·nÝ£>ÚÛÛ UUU§œrŠ cÿQcYÙm·Ývã7vvvnذaÛ¶m}}}ÇŽ›4iR}}ýYgõ©O}êSŸúTyy9]ʰgíAy¥=Ftè%²Ë+1ª¼’f硼>}QAEIK×j‰•“ŠÂR—5))—~C4! BlÇð;â•y*x%‰äuðIM@"‘¢  YwĦˆ>bM:2Pð…AÝ«¸y¥ïÈ•–ÊBîp1 všç¯T· È+±ˆÆ¼Y:-K¸ªO|âMMMŽ{UVV~ýë_ÿú׿¤¶îϨ¢¢âòË/¿üò˳ÔnHs#K¼ 䕈"B™;^éÆñƒfš*F¼’T(ï…ÂxׯÌ[Ä+I$ï"dI"‘"!ßYwÖ¬Y+.Ù«-dÞS„7¯ ˸R¬prï”$¯l¬›a×ï¼±#væÜ ¼’P”XÃÃÃwß}7¿ÆÑÄ’T Ä ¯D#F4í¯…]óÀ+=/#³‰W’Jì1%^jŸµ¦#^I"åK„,I$Rþå;ëŽØ6¼ÛB¯Ìv Í,)\ãÊÔ“½Ü;%j_‰õ'¹˜êøæ•4ò5k÷îݳgÏùûßÿþÿño¾ù¦öUeeåUW]EMDrÅ+휪ì+Ñ´™-"D>ÛDj$m™1dÀÔ"HLÝN=(Ãô6©.‚ R μÀÁÕô¡Ò²¡®Ìr=Zíhê­×ë«mÒk0Æc“´„“˜”v[–Ÿèƒ1Ér™ô*I’öQæ–c’¡(ëe5¯HˆCíö}ÔNAÆy|3Æ”tA S”ôW¨ *hÚ1ó}Ê/³ì´ñÊP+¹¦#^I"åQ„,I$Ržå;ëŽ#g·”8ž&ør3÷ÐÝÅÊ+³d\Ù‹Åc±ð}ÃÑÍ(±y¥»©Ž^Iá,35wî\»¯nºé¦iÓ¦Q‘R­˜W •M·¼¹‡™¾Aå9˜‚‡)À ’:Ãrš?JêzmÓ¤’‘ @RÒèPAtêlÒ´lˆiìhà’–¼I1¢ÉÌùùEîƒq™Ù"KIå•RA2‰iœQ’t| hÒnY–õÝeY‰',Ár}žîMþ6cÜzÅ_¦fµ@¾XEAÆÒ7­¤`Ú†W=„uMìð%ñÊhŸ9ñʰš…x%©hDÈ’D"åY¾³îˆwl‹ÅmôLÊcŠp—ˆ…È+³a\©7H[ì®¶ÛÓ÷CĉD¢'´Ü;Ä+­ÆÀÄ+s ÖÖV““8©tå‚WâžòÉ–¼²¾ ¼’[‰z ø†‹·wáëí*8&ج·; ´ê2ÝT†Drx:³P&ñJŸ-~ÓÙèž}¯$‘‚‰%‰Dʧ|gÝqÌè-i)â•ÙPn"Wªêèh€Ž0Ǭ%É+…MWR,K—š?~ww÷”)S¨)HúS)ä•O—þ©¡Úb=š“Œ;õ0x%¿D¼’TlfØe¯ŒLÓ‘}e¤ž R©Š%‰DʧüeÝqô%÷nÒ€‚÷4>.U¬¼2«Æ•¹™‰¸X_Z¼²çÍÝ-g4ñÊPuöÙg÷öö9rD’¤™3gžqÆŸùÌgV¯^]QQAC2<›B^é­çAûJÕ\[fš- ÄÒ]IÒ<©YÊYe©—É€KãD¦3Ufêiì{&´ðõ¶8#«mÐb´YoW ­c¸Ä$I’e™ @)óŽá²,kkß`X_¦OÁÊdY–Ë´í¥ôfR”ÃÝ7‚Nòo7†ˆ¼ ¼oâ‘zÕ¤_š–Ë`¿Rp.,À‹‰xenn°¼5ñÊH=¤!K‰”7ùκãèî‰W:PðžÆÇý¡Ýp½èá<åÒ¸2·³³’à•‹¶lÞ²]0öÍ+É7^YÔÆ•Pjö•u'Ù5Dï[ý-§×à•4œ%‘üuABûJû‡«¾ÊMoÀS?Åb½[^™y,╤âz‰WFéÄCo:,̈HK  IDATW’Š[„,I$Rä/ëΚ5k{yMP“¯áÅÇ+‹Ú¸JÐ\<Õ9¸÷ÈôêJðÉ+iDK"ù˜ÛóÊm“gÙí™JŽjlJ­0teSƒN"0ÆõåÔóÊ™#iÑ“5¤Ä€ «‚ÍPAÄtqs’CÈAfÕ;¦ ´êÊ䌮ÌD]™ÍŽüz´( ±,e›¼1eeeåå²,kñ(ËËËËËËÕ劊 >wÖĉµåI“¸en=_ “IË’ð˜ûô;D›Ï'“I}½¢ ‚eeå^ çsež¯ém…èáÄMZä¾Êe Š_¦#^©;„DÒ_»Ô$)Çò—uÇ1©·è´ñÊPTìÆ•P²¼ò¼…-/Údàyoß‘éÕ•þx%iI$Ÿ“A}%ÂPùd»}ë«ì󃻱»´5{´¶¯D´3Ø´ëš,Í$/]zódG»íÉÖ’”ûG»8y%z?q¯Má2#|¸ Ço‚—ÿ¥ÁnŒèæ×╤R!K‰”Sù˺ã¼W‡Ã=Gú sø¸”^™%Tš »q%”²}ecÝŒmÎgÇßæ^ ¾x%1KÉç„PÈ+÷TL±Ûµ¡Ê ¯´îpЕ?¸eç#¯$•òcMþà~[Ã>þnöÎM‡aþÛÉEú¸ˆW’JD„,I$RNå/ëŽx¯¶X̽íž#ý9|Ü´³»hxe WB‰ûƒ7ÖM4Í¡}ïŸ8«Ò¯¤¡-‰ä·7²â•€(0±\tªKûJ ÓHâ•$RŸiâ•‘©UjD¼’Dˆ%‰Dà rGÉ_ÖG£H÷˜//)Â]¾Bá•%`\iI J*~¥êÞüâ–^Ëë}kà£Í÷Á+‘p‰ä{fhÅ+àõ©Uv;5T!ãj´ë%RQÕõê"2Æ@‚t\Kåe|üJÔâZʲ¬•iŽ_É´LQnÄ…h¨¯&ùv„¼Ò¢@´š)ɲõÜ©¬¬¼¼¼¬¬Œe©Å¯4Ųœ0a‚¾Ìůœ8i_ u,KÆøñ•!®%c>Æ`Yj*ŠÞž Ë2™L*ŠRV&{-\–˹{ƒ1Ƙäé|£lÔO¼2:µ"^©;„D²~íRH¤œÉGÖG£H÷‘Ä+ss.ú-HãÊŒY€ÃȬ8y%¤|í‘åáýïÚ÷þ´êJ¯¼’ÃI$Ÿ3C^¹mÊ,Á~©Ü;z ;kD»üà6 Çѱ@»î.Û¼Ò±eH¤pŸæâá•èå|NÑy\%úÝ}µ›ßg¥v´(Ù~°Ø´Ÿ…rýJ‰@$’[²$‘H9’¿¬;bFfg˜iSpO¥¹Q1ñÊÒ3®„Ræ•ÐX7½¡nzßÀ!ËŠíüûà™UmÄ+I¤ÜÍ/3x%" L,Š®{ pÞÆ–WòKžÜ´‰W’JðQ.EûJÞàÚw]PûåÆÛx,;g‰Ìº[`>ߦL×ÅÍbòÚU¯$œY’H¤\È_Ö5kÖŠ‹uOúrŸ"Ü%¯Œ¾)bIWB‰óJu¹±nF²LmsxÿÑCûÞŸV5•x%‰”Ô‘ú/Ç+»f4 ö8÷4pÑ3¸¢~Ä+I¤à1ùƒG¥é Ö<——†s¤œ‹%‰Tcš¼WøOÞ³îˆ_‚—ðÈòÊÐýÐCWIWñJõŸÅæp–†BÞz>±øÊ€7^I#]ÉïÑÈ+ʼnÂë«xeÚNõž@=J*|¢ "h1+S·—€IºÅ“œúW3Òý@¹GŸ§›  ìÔgr!&³¡›ŠÂ™.\â¶a\4û%…ÉK–åÌX–åååÚ2˲‚eY¡/OäâZ–ɲu,KIâë ñ¶U9e)éñ_kËéX–ž'›jøK®$`L?cÃrií‰WFæ,sqRÄ+I%+B–$)ëŠÇ Yw‘Ÿ#y¦³÷€kM7r‰ù"Î+KÕ¸ÒÍȬ$x¥º|ÞÂy/nÙawÖo=ßsú-àšWRúÉwÄñJ@ì>iž`ósO b_icSiÝ«X'%7:qy¥sŸiµMP^épFö ‘‚?¾Ä+£ÒtaYGæ8íñJR)‹%‰DʺîYë9ëŽ8îd[,æ’Ž9:¤ƒkM7*^YªÆ•nFf%Ä+!eh¹Ãî¬ì?vxÿ±«¦¸ä• qÉ_wdä•b—ðú*l¨F¸¯t•oDz´é‰W’Jññ-*^)Èc:SäƒðZí®(Öc>co¸-¨²‚J¨géT3 KÃ2ðë[–ø³Óva†¢¿Þ>ˆŒ3¸œ…Gͯ$¾I M5‰DʪBXZK:ÚEZRÎLå>ExðÊx<þ¯_ú²{^yYGû]wÝQl¼ÒûXÝfrRؼRÕªËZäï/l?¼ï¨K^IV–$’ÏŽ‡ã•CSöL˜*Øüꥊ‹œx¥°dOD åÙ%ûÊÈ4]Ì ýœTÍý‰SÿI SdeI"Þ`% Gwé'º„Ȳ££¯$cÌÑ.Ò%ï#^™½SPUœÆ•~F`ÅÌ+ ±n†Mêð”úþ¹ï_Îo"^I"eï]¯»„/:ÕŽWZ¹rgô$ Rµ0ÔËaLÍV `4y0Á˜þì#Ÿ!Wá¢öÒß·I-göÌTHFçÌuኡÓf`uPÛQŽÄ˜Ä$Æ$ÍlJâ‚NJ¦”ü²l·^_)ɲ¤-G9–%÷Qá–3€ú¨žº‹q?V˜.ñʨ4ñÊAƒ=RÈ"dI"‘²(1|¼ó lG—p×!,üÁÝåFn`_èI~BE®ô;+r^©jñÂæ‡Ùf×4ï¿ûÁ?7îeDó£¥b\òËüîZ0JDà MáÆh—ÕË¾Žˆ®… Ê-@<31t}™ÿiÙ\÷£cLb’éˆé]8 p»XàŒñ»„|߯$EC„,I$R¶äu§£=ƒý%x¥»<99æ•nŒ#Ë+ɸ2À¬Tx¥–‡g÷À!{øûïÿ¿M»ç/®çg Ä+£Ã+4Y8?!^ å•â–‹NUª!¯t¿²Dx%ͽI¾9O‰ÚWú.sËÖ¿h¯-S1(´áu|`C9ËššÙ555wB@Ãd╤Ȉ%‰­AI¸Åf©†ŒVe#Ç–™&–bÄéÒÛ1u¸6Õ ‹÷E–W’qe€XiñJU‹Ïž÷ÐÀë‚Æ:zàø;›ûæ/n ^ Qå•ö=7¯Œ,¯ª˜òúÔ*1¯L»„‡Å+5goТ=Ú¤î弫™i¢¯GËõ^{$Ž]¥ñL­„»>‹J°”´A‘å2˜l¦ìR s»HŒiÖXæ¢$Én÷<ŒxyÄÙI†¼Æ>cYŠÎ+ââ!„q̼÷Bì(z¬rA¸úûû hDøÑ~jjfÏ™m‚ü†!^IŠ’Y’HE‡X²ïÖ„ˆâ!c<žèêÞ Ø ÓÄRÌþ\R?ÇÔ=àÚTÓͱ —W’qe°X)òJ@l¬›¾ê³ zT@-ñèãïlÞݶ¸žxeTy%:ÌRˆWFWŠãWªºz©â‰W2Æ4«CDW2}{©—±)™~7ª€)3ܤbXÏ˜ÕøAáË4Ä£ÔbúãzËí¹ø•z®bÈ,“üdnÌTin›d)IoPf“rLj2µ]¿’qøÒ´{î‘¥`¨‰vëý#K&Þ‘q^µÅ?Ï2¯Ì剿úêVè ð7þª-/X𑚚š(²Kâ•$B–$‰ä¨®noYw:»ºBs¿Ë.kÃ…•­Û%ò‹&é#ãJâ•>x¥úocÝôóÎnzqëNÁöG|¸í±Dë¹uSgN"^!pDpð.$^Y¨¼òª “Þí+y¿o'i›„ãˆv}£â¢ÏtãŽ6ý¤Op›NÛ®éH¤ð±]®ñ“/¶dÚL± ¤È÷º|ÈJ>è$"ö÷lÙú—Â2¨tÔë¯ÿà¯púé§×Ì®ž={¶ºžIºyº$ÛþäÀÿàÁ}Åøø•2Êv»ƒñÝoõ…ñæq÷‰W’¢'B–$)dÅã‰D¢ÇîÛ̬;ñxÂ…y[p†˜c^ÖáB½4d\pVÒ¼Rý°xá¼Ý‡ú‹[¬ç¥ÙógÌž?]S¯Ì7¯tšˆ ¯Œ¯t ^©êª “ ÕÄ+Cç•4'yB‹%j_)®ä«[¶nÙ²µ¸/ýßþö·¿ý ª««Ï<ãt \úhëàÁ')~%©XEÈ’DÊæhÃ×!¢SWŽ „µëî”Ùѱ´¯Ø•;‹]ÖÑÎïbY«ÎNp—¡0åÒD1‚¼’Œ+Cí—,¯TÿY}ù‚_?ò:G-­ËÙûÎ{ÇŸ=ú”“&¯Œ¯´íº Ç'^™g^9T>Ùq%äWÇ †ÁcÖ}¦a=ú:if‘6A±”iY1ôÏ+ù‘ˆÁ1s0c}ѪXˆp GC%Ý!i!|eë7§Û‰ƒýi» c¢¡@úÀѾz¥+yíÛ·ïégþ\]]ý‘œ1»zvÖQ¡õ&^I*N²$‘HaJ²£}E[Ì`bÙåäî&ôdÎR„(¯$ãÊP¦$6““’㕪8j)³;0¼ý¥=ÕmÓªÚ¦¥xe¾x¥qOÛxe>yå`ùä×§Ví©˜â¦W ‹W2f×Å ÔbS¦cfl“JwÃ2dÆ…hÚ&’¯9ÓkÎìÎŽ¥ÎBwÄT˜ù¶N—c ™™œÇ3¯t—~'c·LÌç=“D‰ ò.«Š_2_•³ÞÉ*~%‹ÀégÛ¾}Ô!£M2+)n7 pâýý¯¾ºe`` GŠûöí{ê©gfWW_rɲ,¢ÂlÐC╤‹%‰D %Ýbdi̺㘥'3ꥌXâ¼’Œ+C™=ØŒÑK”WªJSËC΃øøá}ñÃUmÓªb'¯Ì/¯tò '^™7^9T>y›kX Y³¯´ã•ö} :È®'1òJç.ÈÁAqÑ•) '‘üb»Ü£p*™™:I°Y꿊ù}ôÇG+MXÉkï¾}¿|ðWgžqÆigœ¦­”ù`”“”ÔG‰1&¥šQ’·ØüøÁmcü á+™õM•I«‰W’¢-B–$)4‰yå·ßfZ#v!ÏŒz™©ÎÎ.GW²¼’Œ+ÃíÛÏÕ§Ç–€ÇB¢È+Õõ«/ÿ¨ÑC\¤ýñÃûã‡gÅN˜|Ò„É3&¯Ì9¯TÊÒï¯Ì=¯*Ÿ¼mÊ,÷°ˆWfœEø¼iNN z-4"•´"ÂuÜ6ýýý|ä1º+4½ùÖ[C{öœzÚ©ÕÕU€2—KGa˜Æ”Ș¤¿û$0`J½y%ûQ™Ñ`VûŠòíŠG„,I$RH€,!κÓjâ÷¬uΖãˆäÄq0Á_¹£Üðʰl9ÃW†5Ú·Ÿ«;N-7…D—Wªÿ¬þìGwz豿ºlÐwï« 3[+'T1iF9ñÊñJtši ¯Ì%¯*›ùõ)³¼öJÄ+MgA¼’”‡ÁAQóJ¯¼ê|d`ÀgBðó>þQl¬Ý0§õÝŠ¨¨O:¢’_Nÿñß>¨Ûk½½‚;¢övSø©ì?üAÿá`Kÿ!¯§¶ÿþçŸ{þÂ¥ªÔ27!£|;¤!K$rw”€µrtÓÈj;^ŠêWëÖ}G°{{»!뎘oÀwÜ&>Ùx<ázÄpñx¼³³»°x%WfcdN¼Òz{Dh¬›¾ê3qO-Uè9 =3Z¦À¤“N*÷Ç+íUË=4>zX©º`2ñJ^Éœ¨%ñJ°ä•?–ëçàð€šÔ‚^¹mÒL*Ÿ¼§|й_}^½Tq•ä:l^ ˆÖa"ùt7†Âíú>TÇDCàKcÍ™Õìrì rS ØnUPbG2ûø•ü²žrt·MS,K7Åæ_Æ´B¡U,³ Ûxœ»ËѾÛDš6ë“i-ÕùȤ¦£0Æ2Úß?ðê–-^yecÝœ†º9‹Ïþ(‡•òJ@¥î„‰u•ñœÚiýGŽùpàèÈÀÑ÷'ûüsÏŸzÚ©gžyF䦺Ä+I„,I$R”H$b±XÞ«!v o7f݉'b¾ÙѾB|R9ã•nØ_¤x%W¯Ì1¯LO?¦Ý}óù¿~ôM—Nâ¼ÞÛ~,såÄéåg”é§füÏÈ¡ñ‘CI»³HLaAfBӀ߂‘iù7£©ÁÖ‹ÈMX_1Wò ú¿<-3¬Ö ¡ý ‰¦Û€+õvÀ±÷F-/ÁØxòƒáÑï W ­Å—œ®‘ñl,ÏÏ\ 1 .rŒLß=Ýøú‚¡ÁÐØDÆã+l\@´|nÐòÂêß2À!6aˆMOäK©I~8G¶i4*›´§l²Õåyî·èTåÜÓ0_¼Ò¦æv…ƒÃö® 6MÐZVqq¦6¼Ò¶oD¿sxRé  Á¾ÒÓqÑÅ)3%#~e_ÿ#^œÁëjëç,^xÇ#Í+M»ÔUN¨›ZˆïÞ²ç˜ËÿÇßÿñîþý\pAj´!1-´¥Ä$I‹e)£ds5xô,ÉúM(SÒa1ãÀ°kéó–£¾‘”7²$‘ R*"ìÞð„¶fÅŠåоbyî+“H$øšdª£}Efå틵š²ôdÊѼ-+5^IÆ•9™œ¯DqXýÙ3w~è±7ƒ7ûð¡±áCcþFÒÇw{? ³Yö3¥ðõaŒ¹2Øß?à29øâE âãi2Xl¼R ˆ¼°jòÙ³&=ºóÈàq±“x÷ÝM›6iâù˜£G'╤ü‹%‰9usÀ1¸žxò)í¿¡áKNßýÞ}‚oM–žb{Ì̬â&uvu'œl sÃ+ƒÊ Ed\™«éñJ¼R]ÙX7muÝ™»mÞº;kà’xe¾+\äMš7^¹èT<÷4Åþ¡.^i±ìѾ ùĽÚWò…{Á¾d_I*Ø÷Šûñ ˜òiY {ÓòÂéÏE_ÿ#¬w;·ràƒ±Çv_ðïØ´qãyç¥l-%I’åÔW2Jv£c8ŸûHá~¾‘˜„Zö‚V?fdì³poR╤lˆ%‰ %=‰žžD¢§§§'{G _>!4ÿ\±by,Öª}\·î; áÙÝqûm‚oãñD—SË»î¼=Èé¸$€Qà•d\™ƒ½Í,W<.#^i(¼±vÚêÏL€Í[w½ø—¾Âœî¯$^JÎÖWaCX娱|îˆWf“WÒÌ›”»aF„*Å8ÓdDdÌ`ªÜß?à†W.^´pñ¢…)˜X¼R-¡n’ü™úÉ[Œ ~˜¼ öïwãÆçž{.H’ÄÕÊ»q÷Àá݃GÂ`—Ä+ó]á"oÒ\óÊE§bC6T;>ÔÄ+Ái=ñJRôGn°íÛ··´´ä£Ž˜‰½^ݲÕqÇÕW^ÞØPW‚¼Rý«›$×ÖOz¬ÿCŽZZ\óÎ;óçÏwy-rþæ%^IŠY’òò¦.òÀ”vëQµ£LôôôôlÈå7^± ŠdZÏGØ¼åæ›øŸ×Ýû]ÁƱXk[,6ñ+ãñÄÚu÷ŠÏâÎ;no‹Å,ww£Bá•d\™ã¾Êz™x¥G^©-5ÖMk¬›–f—‡àÅ¿ôç'åé@Ä+‰WêZtÀ¹§‰ášåsG¼’x%©€FÜíí&~¥¢hË?øáz{wÜðÿ¾ÜÜÜ FS»¤¢p¡ÝÐ] ©eucYâ%I«’öŒ#¦‚¼&“ cì‘GŸl‰óÊtÉð™Ú‰ |88¬^(ñx|æÌ™UUUÙž5kƳÙ{ R¯IʪY’HYWnœ¾s¬žžíÛ·÷Ú}ÛÚÒÒÚª»„ç»ß‹µŠ]»ºüÁc±˜8¦X…Â+ɸ2׳ Ëeâ•~y%¯ÆÚkO€Åo€ÝƒGvã]Ú£ÌT sNèz?W7@6 $^­& ŸW.:ñßœ›úè®Y>wÄ+ ŠWºC|Êzû–ÈÀÂQ*¯€ýø~Zf”˜VdUæÀÀñJ—¼RÝñ3s*Ž'×ï´ØË/¿¼råJá¥À,\Ì_‚Ä+IÙ!K)[RScw‡”EÇNõ MÐß·3Çgwß÷ øvùr=Dæ†'ž›”¶·¯|{ÏÚu‰„õÆb± !,ÝpÀàYÈŠŒ+£2Å ^¯ÌDµ'ªSÛbñÇëÜ7—«\IN¬$cŽÁ¯£‘yº^nŠå6h}Ãx¾ëì¶QüÞÎÛ :eAAÁˆ*à\o¢¨ñ¼’YÝÄ+ùF‡WÒ,œúhÂÃM¥ñJU*µljj —¥h2­‹`)€¨}@_ÿúõ‹Sµ¯äy% ¢‚µÙ§«Ëß7&h·Í›7Ÿó‰sÔeY–åqÝúUAÅÓM%IL5˜Àgì~3W#ÞðojɧY’Ha*7˜²¡¡©¡q^C}S}Ãü¥áu :©w$åX„,I$‘TL©¦üÎê–,¹–,¹@ÍA`^‰a¿W~ô£ûß^²l™¶üýïÿP°ekkK,Öj ì"À+óɸ’Wgg—ÅÊ®nè‚¶X¬­-Öat¥ÉÕdƒx%ñJâ•!ˆx¥ýÆÄ+]7/ñJRd•^©ê¿øåç?íܹs裨’[…Q,W_ùY#¯TÉWšy%ΩÀM×>°…‚;zw´´¶½ší%}tuÔ;’r/B–$o¯®|"`­Üì®mÓÓÓ“3L KÎÿ$ ’ÂŒuüî.–eïÎ~ö9Á©]üÉ‹PQ€±í½½Û·‹^·|õ&»ÑÕº{¿+ö»ÅZï¼ÃOÊ—Ö‹ùâ•d\™©x<îf³®®î.€X,ÖÑ!Ê>oyW{—¯$^I¼2„¯´ß˜x¥ëæÍ3¯d^ÃÅy¥¤œÎBe6èbÞá&ߎX¿þõC õW_}µÝHÞˆ,%Iâ=‚™6I&™Vë$SÔ©¢²õ/¯ñÎżêkêjTÉT^©š["G‰WêUB„MVþtD¶» ;wìhÅüÞÀžy¥÷Y3õ¤üˆ%©¤•›,:sçΛ;·yîÜys›Í“‰ðx¥i¥‘WbðˆòÏ>û¼àÛþß¿© Û{{(t¿ô’e­­-–_Ýûq,Öê/åN”y%WÚ©³³ÛýƉDbíÚt´¯Èš·8ñJâ•Ä+C8ñJû‰WºnÞ¬óJ…ɤÀcWòÇ+Uõõõ?üðÃW^y¥ð¤€æÝ•æy*lDªÅ%þåµm‚BÎ;çã˜"ƒ 2°à• $^©ˆX[ŽsÊØÐ¸5îééihlÔ>|Ý$Î"˜C“J™‚éɤ,I²”â¡(K†ßT8¨-IñJRa‰%©ä¤úwçS65ÏÛÆÛëÖ®ñmh }}ý¿ýí﮺ʵt®æÐÐÝWõµ uµi‰©NCÉ€€€Ä+y^©nù©Ê±š`?áÚ1oÞ<·œ×ø•Ä+I&B–¥ôÍNã¯Ø •ñ˜2…Ã6<¹À”ÍMMÍ*¬4ª#Í+ÇÇÇùÌ*'Ý?ý™ „‹–^¨$“ðçgŸ¹.Yv±¢(,Ãͤ§gûO<)nÞÛo¿ÕñÈôºŠ,¯$ãJçS¾¬ã²Ë:âñxgg·Wv™Hô¨IœÚÛW¬X~i˜sâ•Ä+‰Wú=ñJû‰Wºn^â•%3oqä#ÚFŠ´‹kÉǬäbù1ÆÑáí;JÓ6†ƒsÉšyoî/ÿÛ—~ôãŸì´7«¿¿ÿ·¿ýÝg/ÿlê\¸Q±î.Iª—·z\ãh_­"¢š™zphhpp1¦Õ‘1½àEgŸ…ɤÆþ$¦‚B@Thñ+µ´<Šê!@¼AA8kâø¶aksèСl?W¿’DÈ’DÊ&¯tY,"&====œ@XPLÙÔÜÔ¤’ÊL§oŸ¼’‹þmØÏ7¯û…›Òï˜RîÀ 7‰yeSÓ\EQvìpvyãWnhnn6…4gˆ==Û¿{ß÷y¥‹97¼²-»ë®;rüq¥{µµµÝuWtvv ²ñØ©»{Cw÷†[nùjÌPsâ•Ä+‰W¯$^Yb¼ÒËH•1Æ“/71+-&ùùóƒ»IiD6ƒn¶Ó²´© â—¾ôÅøYjù‡?üqåÊ•ÀeâïFI’$IÒÌ $ƒÓq e"¢êY<80˜®¡ÂßæÐP;§~ÎlÔL,QQAžÊøK·°n_™Â,Õø¥Î+qŽœ´ƒ0‡³‹,ɾ’DÈ’DÊ·Ô4ß*¬Ìê.¼ðb¸à‹ z^© å³Â+ƒIŒ,/¼ðuáÙçDÁ.›››[Zš3×÷lßþ½û~ ®Àí·Ýâ•WÆã‰Î®îòJ2®ô-Õè²³³+OøpaxG¼’x%ñJ¿"^i¿1ñJ×ÍI^I"AHÖ©åÐààúõëUjé?i¨{¶½þº]!‹.àL&SŽáhZÎà•鯈W" ÖÊÊ99”´ÎÃÓ»}ûܦ&0Ú”–ùlK cJú÷`Œ¥®%SPa\üJ™iWYð ‡0F!)*"dI*x%‰D¢G…•Y=Ðҥ…K—™ø¤o^© y(œWr•Aã(Äÿkç‚ó—ØQË/~ñzuá'ül‡pŒuÓ7X®âɧÄGµ¶Æ¼óÊ{Ö:cÁÜóJ2® .àzjIõ.ò7±¡OÄ+‰W¯ô° ñJû‰Wºn^╤¨*Dï±àÔòñõëW~f¥e%Åæ¼èn¾Ð0§&Mô=v¿lÉ+Í,¯ty¥ú'@–‡jhl#¦äÃ(Š_€) •T¸-dº±‚È &½>îjz²IQ!KRA*‡˜r.]ºL{╊ö"á-"ƒòJsìôaŒ‡ð¯ Î_rÁùK~þË_íÚµ‹_ßÔ4w^S8%ç€nø²åúïÝ÷ýíÛ{ŤI aéRÑä•d\V3úˆk¹Üg,Kâ•Ä+‰W†p â•ö¯tݼÄ+IQUèÑ®®¿þºŸýìç»wïö·ûàààúÇÌÔ’Úé¨×·Ù›X~|Aº»@/r(,y¥q³R畈xVù馄 ËF>räˆëá©Ë[´ž R)‹%½;ƒðpè.ĵº¼aÉžǼÒuÑEË +¹Zxà•¼èƒW¾ôÒs‹]¨¿–øÑ¼Ò ¯Dzá?J\n~|síª«wíÞ½ió‹»w÷©k–,>ollŒ1öaržyóšæ66š"fÀ÷ð£Þ^¯lmm¹õÖ›Õßíj•´¢È+ɸ2¸‚øƒw´¯àv£î§·µ/[`56$^I¼’x¥‡mˆWÚoL¼Òuóæ‡WŠýiJ¯±ŽãF/(fe鄼DÍ¿Øv² µßÔò‘?>²|ùrínD@)~'}Ä mŽ FÒWÓïâøx23E"2†sfÏ—ÈLgp’¤QBEïR,|ÀyvYÒ¼RÝæ¬²‘mãÖ©Ã> D?7!Œ>A¼’”%²,è×%f{_‡°ÛÅ2$M"ÑÓÓÓ³á‰'³ÚPÍÍ)SÊææ³[wæÓÁ Æ¿þx宽»víØ½{ÇîÝ;|¶£k^i¢®îyåÈð°áÅ-Ë’žÌN– Ñ$™$=ÿÂFA±sç6^pþ’q.~ôîØñ“ŸüT|ê7õ¦––S­í!âÚu÷FŠW’q¥?…eVÉË1×ü¼yóÃR!â•þ° ¯´¾aˆW¯$^™^ùôUv¯u"ILJû`òQ™$1›èÛ†¡–¹°˜Ì 5(…¸–¦‰÷Iá›””žäMŽŽiË| GSHGþòh&“ãI%#»ªÑÑ繑iËÝ„| µ°˜˜L&µŸW“ɤV“ññä§>ýéõë× ú¦–]]K/Z*I²,ËÚC¡9cºqEATúûúµŠIÓolÆjªgŽ ¤0…i0•2™i¼R¦eÚaÚƒœË’x%(ˆ€ ä_ONró0x|v<3Kâ•$B–$’®D¢'‘Hä ‹ÎÅ_¢þ73qvVy寀M›ž Ry·¼’ÿ¿˜W†ý"yñÅ—úúú|áºÏg®|öÙçÄŶ´´´¶¶¸¯F¤x%WúSÔ:bu´¯pB–Mv_u?ózû²³ˆW¯$^I¼’xe©ñÊí{¦Ø½êé­]Ê Ë  ¶´råÊ ÔrÿþýÏ=ûÜgžQSSÆìF¨Ñ4Ð?Ù©nvUŠ?°4vT@a©ŽEAH%ûÑ˴ʽC¼’ŸàÙtÇŠÂ'k²K-¥(¨Ç²T˜¤H©ƒKhðGɲ3Leg"pR´EÈ’” %‰x¢Gå”Y=P S.»TVر<ÓÈ#¯Üµ«wç®°iãŸCq¼2Ãï;Ÿ¼rw_ß‹/½,Øàúë®Í\yÿOرc§`¯–––[n¾É}5"Å+ɸҫ‚§Ö€ŽŽö.›ÚÛWC:–3â•Ä+‰Wz`aÄ+‰WæÈqûÐdº“r tm œZþù™?_¼ìâÙ³g; yPX_5~%K9ƒ§âWÊ*¯DDHgGD¦-+JÊèRu$W””f©óJ‘1äáÇgUé¶ÞÆø•ȯWÒ]™”ªsj”ÜŒ²(~%©DÈ’”-%‰D¢'‘èIdS.[v) ª˜ÒèCbîICä•;wöîÚÙ»kWï®];‚T¾¼¼’É$Ÿý-ý¶3'áñÍ+ÑWLþíïßÎÛØ4w®i¥3¯lnþêM_q_7¼2‹Ýuçí9@od\éµÅ‚û€«ÍøÎ;ñ.Û«ï*êÒ¥>÷Üó–_u?ýzû²ŽåÌ„x%ñJâ•XñJâ•9ã• ´²¤@–¤°„Çæ+W®|å•WþúƾøÌÓÏ\¼ìâêêjÁ6ûöí¡©CT«¯Æ¯DW8º‹ÚRi|fž畈s`ìu›Æ>zô(,]ÞRžoB╤B!Ëȿςun"Kú(J°{w÷†8}/[v)\¢bJ=Sdàº0yå /<ƒ_dJ©†’Q¥­Í@–¡òJáåá×h±™@â*)§ÃÙüñ‘ÇÄç¸úš«ÇÆÆø5/lÜ$æ•ð•¾Ìÿ„Èムñ§Ç»»ŸpDá¹á•d\éIÁSë˜ÚЮ45ñŽ›ç55ÙE+Hô, ^iÜœx¥cû¯$^éÚˆWF›Wöîj÷ʘ>mZj”Ì2%ÆÀ:~%ØÄ ‡LéqÆ (Ö7X2É=³%*ÜÕ”FGùa¤eáL2 /¹ ZøõÑÑÑñä¸õ˜ùÃa¯,‘wãÕÏ(É"™T^zùåô“ªÛÍ)Ђܨ89>^]]-¢Š.¨å’%KfUÍRcY*¨ ¢‚JúX84´‡«¹¤µnÝ쪑áau†Q&IŠ”>D†RÊÒ‘IRš ªV–)†ÈR-¡p±,S“Vª¼k`4Db€Þ´€}ñJ!KRÁHMó½!Ëɾ[ZZZš[[ZZš›[3;èpy¥ºãν;wö /<¤æª)¥ SŠõò+Ïââ•zìË0_'ýB·”Õ«®6­Ù¹k× 79òJ—H$z¾óÝï9n–^IÆ•žÚ*,³ÊŒûÁºÌŽöv—Å ÂY&z÷$¶ÅZjˆW¯$^I¼’xe)ðJšXÎml°Æ”6Ë`N¿c»ÌˆZfM}ýýCCC`lný‹¤áàÞ}ûŽ9bYÚÆM›`Ó¦M…Û ›6mZ²dI]]èež¡Úê™›CþiR@)š§ãHÛeÀLKÌÒä•\Ñ òeŠ”‡ƒ’HþDÈ’äS¹Ã”-­ê_æïGÚ¡ðJõÃóÏ= ;vöîÚÙ¤æ*¦¬¨¨þ*pä•fv Yä•<º^°ÁâóÎÛØÈ¯Ù¹k×/~ù+q±_¹áË-ÍÍn*àšW¶ÞyÇmY½3ɸҥ‚§Öi‹ÅÚÚb–´·³³Ëv¯¶˜û›¿©©içNk+àDoY¯$^éÜþÄ+‰W:6â•Ñæ•ðô_mÝfÉ+<7úáï2ï+‚Þ´iÓÒ‹–jâé郃¡^šâ)€ @Rabšã™P¦.êë•t,KžÖeîUb¼òÿgïÍ£ã8î{ß_õ †$6À ‰…’H‘ÚhÑ"%‘) -É[âxKœ8vì\År|ï»÷¼óÎ{÷¼sïÉq,DZÉkœ<[vâE6 ."E­ÔnQ’åØÂ`áNʉ…Ät½?z¦§º§»§z›õû‘Ž4˜é®^§ªë3U¿Ÿý çœ'„q¸Ui³ ¦³Gu3ÆôÄŒ1•YV“bì1F,¯ùʸ`tt?¥eexèš²¯·„ÜÙúÊŒ äDDGŒˆŽ>ák(¥åŒo;ª««—T/©®®>c—t[h—…çy¯¾2 `–/¾ø²Ã§+WnÞ|›øŽŒ¯üä'>‚¯ q|%WJž%ÿ©uöŒ  ô;¨^»òGF†]mè®íÛ¾þoZWz‡ß}|%|¥Äù‡¯„¯Ì%Úà+‹ÞW:î>žŸÐdexôöööõõõööõõõ‰O×úJñÓ©©‰©©‰©©ÉA ¥”Ô”ñxœˆâÍqùKœ‰OM™üp^|¥cƒ¶\d3IJöó¥W;O ß´éV5iˆÈ™s>øªžžU==ÜòŒgIÒWŽ ¹ÕU®ÀàÊœ„7<{C«K~Å´{}µýÜpÒ’ðì¼ÑC±|(„¯„¯„¯táÂà+á+óï+‰¸ÃË•ív³¹Å׊˜ânb¸¡U q/Ž>~ôέw656i½-–¥eäMíÓä‘Jœ3ΓJ&*¥ª(é<<\¬"WXJ;’Ùë¥|¥˜Š§2}%'Êï7ùv@)eYæx³ŸZšïDb|<ädß»wñÝ»-Ÿ®ƒõ•?~ˆˆ4SégŸåg|W×T×T×Q¼9niâœDs°¾Òñ.H.,ˆá—#\Éîœ??ýÒ˯8ò¾÷¾§££=©ªzQ<òýÓgÎ8¬²ª§çãô1³¤Lÿ%>µOüõ—¿’ó„öÏ ô‡t¯bpeNO­“ksv‰wú;–¯ïÚ¾íq»¼á‡§•%|%|%|%|%|eÙúJç!–Kë—\¤bP–zF¦(†ì.Âkˬ/dþú„ÍSO>uÛm·u¬ìH)KnêteþT“I5yU{/©ª,ÑU ª(™!“ŒWÓ¦T&眥;$ZÕÁà+3KÂWe äH$‰Ä¸&+Ã×”488”jlºÆøÊ#GÑã?æg‡ÝÍø®©®I“΂ã½r÷ï+ƒjY~öêq‡O;::V®4„ñ–ô•2›þÒ_eb"wúø³þþ¾nZ ®t oÃ*ÍÛµÙâOÃlwܵÝNYуíà“ƒÅJ?ÁWÂWÂWºqað•ð•ñ•Dü±×ZíZúúú¥KëÑYåÇsÏ=·uëÖ––mª·C§$õ­QUŠ(‚ 4Ì'ÎÒQ1¹î+õ ·šÎ8U®¯´?ߪªŠiëU1q¼ð¾¢0¡ß§ê#c¹Â¸8Åf›«1žð• P@YV4c)M™H$ÂÕ”ƒiMi¬/=úJn|fåÝääÄÔÔÄäd0C)%5eËòâÔÜÒœ]­g[K™!–†gé |¥Ïœt{÷ퟞžvXàýï{øç±cÏ9ûJ" ÜWô‡2¾ƒ+nŒ0SëäÜ´m™^GÚvuuž:uÚò£Äätbrºõ Gq`¥Ÿà+á+á+ݸ0øJøÊBùʯìw «Ý™^€ráÉ'ŸÜºukS<žSYé³¼3¾Òäu_ÉUb cVRÆfÙÀJõ•¥#á+A²,áŠÀö“\Ëî?M98Dćˆ[>Wºó•ú_Ù¾òðáC”Péùß5µ©jjjRûf<ÿ’¾ÒéªÉùJ“þ´õ•α,ÄÉG\˜¸ᜈœ}å}÷Þ£&3×ïìٳǞ{ÞùþáÇþ@öÙœ¢|¥xÚg]ap¥%ùI­“ãlôŒõg_?‘Lï¼ãö¿ÿ‡ïØ}úàCþËÁW¡ ¾Òú†¯„¯„¯,¯œ˜®™¼PkWÿ/­¯_¶¬ž²fsë½Ea,ýš1EXLaRq-™Üc !ñæ›on¹ývUUÅÑ|ŠBœ§nH5™\X¸ª§`<=œ¸šÔ]r®2®Û@Æ8W‰©jj4Ÿª’¶3»<í¿ŒW˜¯ 4}…}Ÿ²óÁ¡8”%p;ÑýÄyØÉ¾ûúúúúúûúúúûûÓ©âÜûJãØÉl_991>99A”’•žq5ã{yër"ª­­­­­•rŽYõu¶¯Ì±z^|%%“Iñ9{VÈïäC¾pñböºm+V´­X¡?ßœ9{ö‡?ú±ó9ùèG>ÔÓÓ-î¾eüÊ/åo&'§÷•òVƒ+-OKAæ€[)ËÏ}n [žÊÌ 7t…×ÝÝÝ]]]§N²·–ÂôpøJøJøJøJøÊ²ð•Dô7zZÎέáP˜!0¥¨,-_“cüJƒ²ÔãWcYÂ`‚<Ç7oÙ’L&¹ªfõ\Ò±,¹šL&5ѦhMz¥Qu1§ªŠ>ú’k*SZXf>¸öZq©O¯<_éÒëÁW(KPŽš’(ìdß}}}ý}ý}}}}‚9 ÜWNLŒONNhÿúÙ[W3¾[W´Qkk«ó J‡O}5”e=ûJß-Çî];gfg÷8dò•ïÜÓÙ³ç~ôè£9}ewwwÎÍýÍß>”ÓWöõõ=ð™ûÅÎ@P`pe6yN­#/.öìѦ¨÷ûN¾ôÑ|èÿùŸÿ¯Ý§‰É ©éáð•ð•ð•ð•ð•åâ+§„/­¯_¶t)ú \illܼe‹öURmëq"³Ê93gý3©­ IDATΈ9ãlqUˆt)¾ ‚¯$ÇÁ+¦!'ɤ˜É]xTU%õ§ª¨jºTHŸ4¸˜ý=â¢ë_ Š(Ër#š²?5š’xÖƒ§œ¯4õn²}å¡ÇÑcô¹·ò3¾këÒÿÔÕÉI?SÂ+ô¢ò•º ü£ýþèþƒÓ.hïÜ|óMâ/¾ü²s Ý]]9}åÄÄäc‡KúÊÀol ®Ì>!E2¬2Ç&öŒõtÇ·?ýô3vŸ>øÐÁ>±»ukÖ¾¾2ç2ð•ð•ð•Åè+¦„Qg'¢X‚²¥±±qãÆò.ŠH5øGUåf_©ddœš mÉ„0—,“±Çà+3â*ÆW†)÷0¾” P–…ÄUJ‡‰ñ±D"?šR•¦çF;_iŽ;™ËWNLŒOLoMéjÆw[['^WWWWW—ó¢äÇWŸð=úʬÅl7eŠe©(™“fœAˈh÷®¯퟿yÓ7´._žL&µOýéÞsçÎ9PWgç‡?üAí‡Añ·>Î2©î&&&þê×enE;_)Î1—Y©Á•"L­SXî¼ãö“'O9MøàŸØ•NÅ“ýP_ _ _éÂ…ÁWÂWÐW:‡°$¢ö¶b ʘ›o¾9©&µ‡y5™LªISÿEÅUUU“škSSUFjJª©–âkN\åŒq®½£¥ÏŒ²´ð•\œY9¾RNð…«6á+”% Í&ä!‹ÎÐÐ Ù=7š}¥ÕàJ_9>1>‘ú7O3¾ëêêêëë9ñ¶¶¶Üb®*ÛO™yð•”ËR\8[Yцõ×ßxà ‘HD󌱟îÛwþ¼SŠž•+;>ü¡ßÓM¥)³’¶ÉÉ©©¯}ý›9ÏI_o¯ÌøJι|( ®OEÁSëŽéNƒ ˆÕE4ýzûÖ;¿õí¿w(ðÁ‡=üùß·zŽ„¯„¯„¯táÂà+á+ ë+CXÖ××uwvF#™¾R´JxF£QýuU•þº*­«²lqÌq-õ?K$~¥ñ¹‘;,†`œEËM7ݤª*'®&“D”T“jV,KýOUåjZ>r•ñdfn8OOñÖgFÉ)ªžŠ‡ô‰_\è }%'b•å+eF/‘uX€Täð•ʤ4e"¡™Êp5¥&(‡‡†Íq'¹S *ï+<@D‡åoÆw]}úŸúz± våa)7¢ÓZDв2—Ö ÷¶|åg?sö•Dôßy¿óò¾òþûÿ4ØýÇàJ]V–Äð<ÐÓÓ½õÎ;ž|êi‡e|èàðÎ }†âð•ð•ð•ð•ð•eâ+‰¨³SÂAÙrÓM7566ªé|ÝN­_¦KºŒ­3ú;‹$àªÁŸeëKƒ¯¤Šó•9¢„¥ÿ±é±š»Øzˆ5N¦@™>û§9z€¯¡eYì$‰ô€Ê|hÊ‘áaý/@_9>>>11>>îw(¥«ßDÔ±²CF&è+bÉ…(”¦¨”^}e(mÉôôôñ×^w^æwßÿ>ç$}åàî]ƒƒ»•t‰¿üWij²ù¶wNLNž9sÖ¶fžºðàÇ>ó‰]鸖ð•ð•ð•.\|%|e‘ûʵ×^³´¾ÝP–¬ß°¾±±ÑÝ:ú””d‚”¤,SiŒP™î”èFÙøJ^Y¾R®sj®Êì[2AÊŠÀ&y1P–EŠ673áoÄSNúûûúû‡‡‡)ËNšçqgë4SU•å+ØODš¬ô³“ò3¾ëëë—.]J‚¦$èvDd¾R|‹Å®^½b>ÉÁùÊÚ’éééý9/ó»ï_gçJ‡&§¦¾ù­oçÜÖýÿåÓ}}}îüçÿòÁDe®Ä°Jg>ð;ïÿ‹ÏÁy™/¦¬år—þ ¾¾2€S _énÏá+Ý\£JηCDõõuð• \Y¿aý²eËܯ—žúv|V¾2L<õ¥¤§2“=Üà+µÀ™ð•žñ]|%(N ,ódʱ±±|¥¦)µÿ’ušï¾òÀýœRó¾ý ?ã»~iúŸt|tÑ JúJÿ!,ýøÊÜÅ’__éÜ,^¹rÅ.ýçÖU„¢¨ÓÓÓ‡qÞÿ÷¿ï=ííIU%¢¤rGMïÍß}ûïOž<•ó<|úSŸìëí%ó!ZœI™ I\Y!©uìnÃÝ.¾~‰D£QÎ?ø{øÎwqÞÊ>4´cýðÎ Òþ ¾¾2€S _énÏá+Ý\£J÷•uu®_'<‘VY¾ŽÅbúcj,«J¿®ŠUUÅ„ø•Q¡e‰X¿VE²lŠqYr¡ -ö¿L£YnذÁê³¼+ ÷vsssËò"ÒJjªÉ¤*™T”½T“Úv³cYê_(ιšÔcY’Êô`”¤§å¡T>pNª& Yjò¸–‰‡¥fªjê}¢”ÊL'gýW¾28ßç?¹«4ð•Ê„¦)ú³ÚO_¾2‘HŒ'Æããþ†RºšñÝÙÕIœ:»:-U`à¾Rfˆ¥·ú]*–¥{_Ém²ËëoüÜyöö¶•+ÆWÊûÊÞÞÞ v»’W–ejPéêê”±–û¼‘˜¼0¼sƒ0ܾ¾¾¾¾²ˆ|¥ÌdpÍW^wít ÈÆ·h/®^µ{¼·«-“FÑ7Þ`XŒ˜e·&¹° ½xk,1fëÞ{ïu{Dªð…Õp›niU5¨I»ôë+¯¿~éÒe5“nëìCX¦þMgéÑ}¥žWÇì+õ ã)UšQ•â+yiÝÙuEíêWÙ lJÁŠø•ÊäAS¤Lev¥äÇWîß¯ÍøNŒçiÆ÷Ò¥K—-[¶tÙÒeË–e=µøÁ^Ø–‘HäÊ•+¢"§¯´Òšùð•y|ffÖÙWÞwï=Eå++yp%æ€û±–¿û;ïÿÞ÷ÿÑy±ñ©‹_|ø±¡ë‡w®‡¯„¯„¯„¯„¯,*_)3¸R¾2pvïº{÷®»ÒBP|MDÿöÛßf^ÿû¿ë¯ÿCxjc-·¥(L^¸”ÇŽóã+×]ýR/óÁM_1Îå|e*®¥*ÌgÌÂWê‚’ô2+ÈWú·~yÃìš¾P–åÆÈÈð€0šÒ¢>qï+‰ÄxbœˆöØïgß´¡”$7ã»»»›_¶tÙÒeKÅúÑÁWJ±tÛ â+u3«(ŠöB{ßÂÒÔzXùJ³””ð•Á¶+GŽ>Qr¾²bW"µŽ:ÚÛß}ï½?zôÑœK¦‡[^ßoÈ$_ _ _ _ _Y_yèxËc¯µÊTõõuumèG€â䨱csssžW_»nAËŸ3‰9cŽ-~¥¥¯$bº—$;_i‘½§Ì}eQˆ?įÅ”e9022LD{F†-:Y•’¤¯L$ãããÚý웫ßÝ=ÝDÔÓÓcÿÑ¿¯ÌOKíxESÉÓØí¿Cƒ€¯tl¯\¹*þ.‰X÷ˆ´ß«ñÏ¿tö•Dô®‘‘äB’ˆÔh2ÓÉHÿ<þÈ#ß?}æŒs «V­úÔŸ|BQCæ;OŒ%¾ðàå—/±„Viy÷f¿ãWF£Q˧1ÎùªU=ïß{ÿñŸ~sCãS¿øð‘¾UËâ¾¾2€S _énÏá+Ý\£ò󕇎·<öÚrÉ6béÒúõëÖꊿ¬‹¯-Z$¾Ëį4Ƶ´ )¶2zðJíµÞ09Œ ,ì°AÓÖíbíbq2ÆôCcwQD8-UÂëdUL8EIUMêÏ“v–˜qfxfu¦Ø5¶b]-s ÂÜÜœ_¹nݺúÐ|¥ ÿRòQèw=#CD×úbª(+ÃWÞýÁW(K¶¦Ü3bWÙyð•££û)=ïÛòÉs–5¤ÿihð#‹!„¥ö »hÑ"q•ì×$™²ýŒó2#ÃCímÖóªNŸ>ýÈ÷þQæöÄW&‰/<øWòË—úˆB¤ÖÉ+W¾çÝ÷=ÿü ç§§%WÙäçûü¼oÕòþÕˇw¬“xN„¯„¯´\¾ÒÍžÃWº¹Fåá+'¦k&ÎW»2•õuuk¯½ (06 Ž_Ä|pãW5¥ð,|%q•¸’í+9×"#r.”`Œ™ŠO _éáÆÉ$ñq—1¾”P–¥Ü¨yò•c üÍø^½z5­Z½*{8$ùö•ù a©9Êh4FMã(³WÉ9ÄR&c¸Þö¬}¥9x¥ÿiÕifçæŽ=÷¼ó2ƒ»v­hµŽuæìÙüðÇ9·”¯üƒ_¬œÁ•˜žOÚÛÚÞµgä§{÷É[K"Ÿº8>uqÿ‘ŸÑPZ\ ¦³Ýƒ¯ô¶cð•ð•ð•åì+o&¢‰éšÉéoUúu׬YZ_¦ ºkgÝâÌÎÌ<óÌ3žK ÚW¦w•sRU®(V¾’[úÊtŸÅÊWꂯ2}e¡$`>|%ü& (Ë<4CÜíbRŽÍ¯Ü7:ʉFGG}‹üŒï†Æ†ÆÆÆ†††ÆÆF·5]¾ÒgË%K–ÑâÅ‹õ€ŒaûJñòYúJn¼ÜξÒyK WÅ(?–;ö«_½å¼›­Ë—·´Ä“j’©™@ɤJDgÏž“I]ÒÓÝý‡û}ý “},!‡S7–HbÌÇ2km•ôqE4Ã¥=ÅÇ:ˆá>ÕdÒòaOUU!c¸ªªî†•1²}žKH²L»Q¬W‘¨aB¡Ì*\xÊMªIýIž1¦p%½IÆÔ™™™§Ÿ~ÚóÉ¿~ýúe†ü¥âmÆõÑÒ¯k7¼ÊTUQ³îR!iz‚wê5ÓŠb,UñÔ,ïô6qí¿© Å2n‘¥Ö5úJUÍ(Î ñ•Q–ð• Ô€²,Mrùʱ±„–yŸ?MéjÆwo_/õööÚYBÉ!–®*ÐPCX.^¼8VUU™Ì£d͸¯42W¾ÒO›øü /]šŸwX uùò]wï°üHÒWnÛzç¶m[}~-|ð¯ÒÙ¢úûû÷Œ ¯YSz¶Ã*%{Jv[šRì“諨ªÊ£Âk®QWçÊÏÜÿ§ßÿ§LO_@‹%DçʎΕ Ëü4¥D”ˆM6¶h•¨, ¹tôÅ"âk£²Œ¸W–e©/›þÍÌÌ<ùÄ“ž‹íêêZ¶lYÀÚˆ§½aFª‚ÚK ÇLÀJ®rRŒÚŽ„|;¤Ç¯Ì¬®Ç·ª_YS­¹Ë_Ýà+AÀ@Y–IûÆ9íÛ7JZxJ.C~ÆwcScSc¥e%9ZÂ0¦„çl[É“¯\²d‰6û[Ünöž8 ±tµ‡¶µ}¾ÒWãÑßßû‹/Û}êßW~ì>ÚÓÓãg‰Äƒ_ü’üò##Ã{F†Kî«Ô:ÅÆÈÐàùééW¿q ÅÏÒúúuk‘imŽÈ·¯\ºtiWw75÷³KéNEJ8ÚúJ}Ô¤i’xjuc6ž,_™Q™•ã+Õâ·{_ ”ei£M Ý·/3¾›ššššš››ššHzhdABXº= K–,ƒTŠ[qå+ƒb ¯ôÙzÄ›š6ݺÑÎZÞµ}«Í͹Z"Þßïô#>}¥‡Á•ý%ô5Gjb¦mÅŠ¶¡¯íÕã¯ál@qR_W·²£}Y€¡ýðÝ”ö‹/}ü¨çb—-[¶~ÃoþÎë¼qò Ý+2‘G¬|%e|%e&>‹™ÁÓiÁ)ÛWrÎYFùQEøJ^ô÷i¸Ë ”eA«Šmï¾Q?¦ÒÕŒïN¼¿¿ß´«&Qè<;ç©(T˪ªª%K–h³¿ív8 _éì.¹x,ÆÈí+õ陨—N'_UUñ‡X1¥¾¦ÆÆûîÙó̳ÏÍ]º$®»õÎÛ-jßè_ùáþ^WW§\ìV‹eãã_úÒ—åoû’\‰9àaà:–¥áz,ËhDåqÍÞ±ñ–µ×]÷æ/~qüµ×q† xX¶tiWçÊúúz­ÎWÒ1 &†ÛO×_WUe&ƒG£Ñh&®eÔ.–¥]\˲mm‰¸~˜Âäw…1n•ÅòlcYrïóR &ÓûЊ( ‚¼zÎѨkU#åLqä¦{@UWùôôô‘#G<Ÿð†††n¼Ñ²Á9gŒi§Z‹oQ"DĘªª<õ>q"¥¯¯obbB¸hz,ËTÖo"ÎRûγ˜¥cY22j;Ut—z§Åä+ *°2|åñºf·Æ Ÿ¢"äåʲ‚ŸñÝoŠÇãD¤Ér#)ã+e†Xº«ò•ÕÕÕÕÕÕ9Uc!,%Kvá+v2ÛW:üÇU®*™pàŠjЇs¾eó;Ÿ=ö¼n-·Þq{Ks³! çÄù¾ýdæÉ~èƒèêêôÆå¯¾ô×ãã’ —ÜàJ¤ÖñÒ;bÌîñήsè ,õz’«*ç©×QUKŽ Iªªªn¼á†o¸azzú•W_™™E£¤»³³»«“EaFY ?M‰u>Â_~²Òÿ¬2ĵ4¦ß©€T|Øó±444ÜtóÍd3$‚s®ßðÚfµÓÎTƘšÊ=ň1•)Š )3߯K™VRS2ñ)EÌJ§ÝIyKN\eœ3!N%×ÒïdûJUï¯TÆøJÇ»·¥¥ÅsO9ëô¶âW‚bʲ̑Ÿñ½æš5Ä)7Å›ìºýÙn.ÛÊYú;Si… a)«”ñ•a„°ôã+õFßô¸æÍWºE·–[︽¥Åâ·Ay_ÙÝÕåm\®*•a†VY¬X±bÇömDôó7qqfîòIWçʆ¥ËÄtÉ väÓ¦<è¹Ø†††›o¹%³¡× b!Ã'…é£,4Õ…_ÿ¦uiµf 9WŒ¦©‚mÔÇTªMI”‰eÉ ¾’¸“èc¦ž¸M·‘SÒ#‚™áê¥~üàœ“ËA8'—¿›ÀW‚p²,7\Íø¾æškˆèšk¯që%*»°|¥Ÿ–ÕÕÕUUU¦ àAùÊÀ²„[&Óp·¾RøA:¶l~§ÝXÝý]¼8ã¼úÊŽŽ~äCž·þ×_þÊÄĤäÂýý}#Ã¥1¸©uÊë×­%¢«W¦/\˜››››››»„ÓÓ¹²ƒ1ÖÕ¹2õ0l3«€âÐØ?ºßs±ÚøJOûäbÙ‹¿þMký³éã_ɹ0îÕ”„GL­C™7¾RT ¾’_Úâí†ñËÙ¯æ=€' ,óÝ&É®ÎsEÉú|ñâ%9g|777777Ç›ãÍÍÍ>ÇH:¯.¤ù a©Í'wÙoxx!,s±L7ÁøJGT®Š“ÁÅIâ†I+Âk= Ž~D/^|ãÍ7e|åûßÿ^qL‡d¸ó{>>>ñå¯üüÝ52<4Rô‘+‘Z§°8ĵԿ8‘HÄðýV¯2Ä5ËL\X¨‹m[±¢mÅ "J&“Éä%Æ'8çvß/»ŸadëX<=ʹâμì\¹Ò¦·ú!>½'†Gä&†^ “Á3ñ+MsŠe&ƒWB «0¦ÜâÆ9×vñ+Å&OUT®Š ±U˜$÷~…›'†3ý‚ÚN ·‰eIܾz *·n÷ÛÛÛÏ;çá$755mÚ´éꂸ5½¿ÀXê Ã9W–6”1Æ8KM '"Fk®Y#ƲԿoŒ1…)ƈ(˜ÂXú@µ ãŒgZþJÄy¶²L§ß1ƯÔ?%ª_éð´VSSS_ Š(ËÒC«Môd8–M¾Èµ×]KiYiY…î+‹*„euuuMMM¶a”Q’g_)#.ƒò•N1vTΕ܊Äòlk[¹pñ‘ǟÈyº4_)ÛØONù ®Äð‚ôšLw²¹C¥/#öULßSa±±ûËt’ɘ°x&Š2¹L*D´öºk“Éd2™Ô»zuAèÆ$-»4²5Ç$ œëôÜ‚LVYf<£­²ŒY+ËhU•Î8F£ÖEI*K™€Ë¥ØàfÞN·ï¿p›ß­UÓóă´Ý'¢(2§ßÑ•e$’±xFª„tLöOï<»“nÓ3%…¶þž{ïyôÇž?ÞÕÙnjjºmómD¢ƒª*Ó7ÇÒ‰rT5£,µ1È)e©0…§¹2bŒ±¨ kãZ*oœ™»©³™RÊ2õ6OéKž*BXÉÒ½Έk 12¥â!¦çÛ¡Êò•Ó‹ªí®lʾ”P–¥ŠƒŽiiiini&¢ë®»NÆ9º’†T”!,-÷DYI.}¥äKù“FîŨ_)&P4n:Ä[ñâÅ‹O>õLÎŬ}¥““ûÐWå—*æ//RëT’ý…w½ë]?ýéOå­¥î+ýÀ´4/™úkjjºté’ÝÁ¤ÍYMô¦t¤KžñwzÌJ¢ôTñtª÷ _™úïô"G/iÌÄ%õë…>(–{¸K3‚[r |µAÞ€²,Cš[š×®]ÆIÉV9ÿ!,MÔÔÔh#+Ž+o!,}<â˜ö\øSð•\ßbº±ð•<¬–efvöégŽå\¬½­Í›¯ü›‡žœœ’\¸¿¿oxxh ¿HWbX%@á¦# o-›››7½sSH»Çí”åkgf7¬l"®œ†’gâTrì¤æƒë®°²}%q~|Ùr»Kв|¹[.Ô›Ôë6ð”eþ[.¿_ò±±DίáÍé.ò–µµµº¬ô6"2?!,s±´ò•Üð?7¾Ò±u±t»¹^ç|vnNÆWn¼åæ·ÜlXÝJÈRŽONN=üµ¯ËßTCCƒ#ÃCÅ9… ©uPiÓŠ3±,Æ(+¨¢E/É.‘dŒgû‰á–sÌÅULI =ç¨LßÙ¶Ï"N gªa ÊT"z÷»ßý裞={Ö¡ôåË—oݶõ?ÿó?-OƒÂ¸'J7§(¤O W“ª~ š‹D¢DĈ©LD"Ë—/O$”ç—¹‚ŒÑ…ù÷c S"ž ”©ýŸQ:'8#1N¥!ÙŽ ûˆ2ùz¨}eŽ!–ÞEƒ5þ6ð”e‘2<<4ºÿ€åG‰Db` ßgu†¯,l˪ªªÆÆFÓ0I·SÂ=œ:?¾2w,Ká¹Ì¯Ì5#ûPÎ×D|vvîégsûÊ{ßµ§½½Ím»÷Õ¯}cêÄ É;¤¯¯oxh°¿¿¯Ø¾ÅH­S ݤìûß”fGìˆ U"iŠ}Pš6™é€ Å.,,,,¤VYH.èñ+M±,|IJä^ ¨øª^üÉÒ˜'".¯‡¹TE—e¦ô;‹eâ/Z¼X½XxFõ l‘H$"¾в‹ki:Š’ÎÆ#³óLДŒHš-»XüævÜJYЭ°*!µU–UUQù eï¤!•eÒBYÑ}÷Ý÷Ãýè¼M6žåË—ïØ±ƒˆ’ѤåÃ4WteÉ3·'EQ´”Ú†¢UQýlhúReLUÕH4²¼u¹ƒ[LQ¥uñ_ÿýâ¿üGDzj¦M)'NÄ-~eZ’¦ÂZê±,9çŒsÆÒóÉ_©' ¯(_IœO/¶U–---^»÷„@øÛÀ7P–¥ §\ *‡stÛºS‡°¬ªªª­­Åb>}¥äKÏC9¦}!Æç¯¾2ðyáÏ{þÒü|ÎÅrúÊl&§¦¾öõoÊ/?448<4Xl_P̨Ўš¿Ÿï½÷ÞsgÏþä'?1½¯ûJo}GF²füšk¯ùÕ/eùÑô¯Û±l 3j;n¿25U<3O<¿2#Uœ¯$Î7´Ú|ëÜ;6q- ?szQ®ß¸ëÞ!|%( P–¥ÙZÕÉdRû¡’S…°Ôf‚;ìXABXû´ãÙW Äy€­Ì /¾$ã+wírë+¿þÍoÚ^3K!ňš››ÇÆÞ²[íåS—6®lM_Já±t,K-x¦è ©ÒãWj+¾ÖèÅRŸNöéÅl¯¥0‘d2)<ˆgT ÒªªÜ"v'甾9çŠpÇQUU”ˆTÎ9OèL&“j2VQRQ’jDQ”HD!¢;·ÞùÌÓÏ0f]ôÜo®<:>_o'ÎRùÇ-|ej?¾ÒLwOO(j½¯tû•Åã'ÈP–%IŽ ‚»*ª¸BXÆb±¦”ã a)ÒüûÊÜá,É—¯4$®UÙôŽo¿ýëœã+§/\8xè°|±[ï¼ã®íÛ"ù\‰a•Þݵë\qÎ-ç㘾¶b—¢Öí©ASª™> 7Fý•¥¨2íÒïÈtÌ ¼ëpÃûrÊ23q’”¥ÝOPQ eiz-Nò @1öÂèÇÝj‘ Ü¡–––æææ¹¹Kv œûí•OÌßÛÓ@Œ#~¥+_ÙÙÕU ÷)'î¿ {÷Ž%H­džíƒè Ýãó´Zj$&és­´PŠ‘ÔàMNÄ%å⣑è]wÝõýïÿ£Cyç~{õÑ—ßݳ ¾RÞWÆãñºÚZ=V$bøíDñªí’Ýp¤SÏG”ˆvù8qEQ !/Á,-º›ù¹ëð”e™6¨%²©©)‹¹ÕŽ®NHÑ…°Ì5Z6‡¯L»H»à•!ùJgÜ®¼ãö-Û厂–|Rë€üvЂ.0;¿}û¶£GŸpXàÜo¯þhêíûzê¿RÆWVWW777~¹ûñ•Å|×@P–ùoœdÚ*)çŪž’ aéÁW–tK!LeŽ&Ä0v’[¸H_™ãéÊ9%¾­¾Â)(IˆXRD!,›šš-Zž¯ $„eð¾RDi¿u³‹ôé+Cn`.ÎÌ<ñäÓòËoÙ|Û–-›óóuÙ·otßè~?% µN¥õ”âZzÅŠ"³ }11~¥©f4%f€²‰:<ÛØ¤^Ë)KS¦5ýµ³X±ù9ŠìcVÚîa™*ÎSñäÿþÉÜdÔQ= 1b‚›TLí¸aæ.o´œM¿YYfÝ¥Z&1ÆkU4ªYIιæåSyú‰E{DÑ3öhÙˆ4kÙÜÒ<7;g:sâçþmáÑS¿¹weõÊêhæÛÙßÑ{¡º.ç]ÑÙÙYxAÀ]Ûy<{‚‚eY‚äüA„›[5·!,Ãð•–ŸjÉÁE_)¿{yaé¦6—õ•rCnÓÝŸ¯ µ‰9úÄS3³²ƒ+;:Úï¸ýö®®Î°¿(cc‰}££‰Ä¸ŸB0¬øèºå»/¨“6žœ)ÄFD<ÖOTö”ö¤Ú¦#–‰e!–ZlÛ¶mO=ùäl®ÇþGÏü[û’È}+—TNüÊéŵ:úd.JWWWmm­öZ‰D"™X–‘h4ó»HUU•åë¨ï1F¢mO%¢¤U2aIµ­¯Y*Þ%|%( ,˳Ùó9%œÜl¯ljj"‡è'„e)w‚ a¶¯Ì$äáá¶1³ssÇž{^~ùM·¾cÓ¦[/^êwdßèþD"áGV"µÈCÇÍeVG[·n}RÂZžû÷ä—¿ÙØXõކhyûÊó‹k7,—\ID+;;«««¾%xè·ëM@p‚à€²,ÚöÍû÷\¬†Â aéªÎ ÐWÊ ±”¯I ÂRæ9Æ­¯´H ²¯|öØss—.I.ÜÑѾéÖ[W®ìïK3–HŒOŒú›ŽÔ:À‡¸–“Ä3ßNÆHˆcë0=S,&†€}lý¾øÚX9[O 'CB»¸–~f‚ƒ"lÇMmºõ $wËIŠqmchl¦¨é;M1v„U"<¢wôÄÙëÖ±,)5PR[ES£2U¿±õ˜•ÚJ*ãÚäqNL[75Ó0 “ˆÑ¶m[Ÿ|ò©™™™œgàåù«/Ï_ݸ,²qY¤,}å« ­¯5¶JÞð•xʲ"/厷–±X,{˜Öà”pùSFËÌnçL”¯ ´ñ˜»ô‹/É/ËÍ7…¹2‘Ý`|sÀA0]_q-Þ·üÉÇT‰ý ‡ÅÕ}¥·‘T|!,ƒñ•ΛËé+M yì}¥ÿvdUO÷Ô‰“9ÓW®hm ðVŸ˜œ<|øÈä䔟BFF†1¬„Õ; ZÜpò¿ÒËZ¦D Sˆ¥Ôdvr*íýH$rÍ5k®¹fÍóϽ077[áwBKKK<g6I½"EÿSQ"鯤(‘ˆa±ŒÃUô×i­¬«‡­T”ÌUcLa©8•<ìÛ¾„ ”ei¶ˆu‡ÌK•‘_lË0|¥ŒŽtqÒ|%wº––¾Ò6¸£¯ôÿijª§ûí_ÿÚybøÚ뮽aÃúªªª îóÇ9|øˆŸúûûF†‡ú1ä„Õ; ÜWr¿Ïï<׈C-x*7K&x&W¸vLbÞp%"85͵1"¢HzÚú–Û7_¼xñ­_½5??_·AMMMOO*.3jJýµ–rG»a"Súˆ°JÖk.NêO¥Lq2¿v9çÚu“ cªå·ÇøJPl@Y–y«Y !,ëêê<øJÉ!–®v/Ï!,ƒ»ˆÆ£ |ûJþÞ^»Ü;-ÍÍk×^»¼¥% MMxüèS'Nø)dxxÃ*@Þz^ÅS ×%¤÷}ð¸Ûß±qþÒüÄÄDåˆËêêêæææ:¹$ÜÓÉ ûBüJPœ@Y–fó¦G–+¿€!,›šš-Z„–d9ÄRê"/yò•A4(MM}½«Ç'&Mï_{Íšõׯ äqô‰'Ož<åGVö÷÷õ÷÷ ¡zAáðk³]˜K™P˜x΀â¬Þ=ÌÌÀdtè¤î 1ÔµªJ/Å™ÂUëb\Ë(ñìÎBº°Ì;Z±ÚŸz*a¯´× ±TúôîhÁ íâW2 jÚÛÆ¦ÆMçææ&Æ'Þ~ûí2¾š››ÃȱcÝ! ó6ÆøJPœ@Y–lë˜e¡’ɤdý’·–º¯ô#’Ê8„ez–w®JÆ D²¾’SêE_ï¥ùy}zx¼©iÍš­Ë}{âäÉ“'O=ùÔÓ~ Dj¿Yàâ†ûß%‹7M2*­/³·§¹HžžNlV–â$ñˆ0C™s®Odæj¦Ç—-k ¢“'NÌÏÏ—Ó Ëšššæ––ššãù±Ž_Dõ.¼¢(úänq2¸ÝÄpNÜ×ÒËR‹©ÃM)é¾á+A>²,ÍÖѪ5SÕ¤eUâvµs!ž}¥äKɽ*Ú–.}%—yàô•<ó©¾ýàÛ}zøškÖ ø,íÔ©ÓÏ;vúô?…  Æ8€¼õÈ‚ŸNa¨ ôXI‹é ¦Ø…Œ1Î9#F 1=~evÊƈ(*85¢ËîÃôöõÑþç•'N¼}ùr鎻lii©©­­­­ÕϘ”²ŒFõ.¼AY*ÆX–Q³²ÔV1ªLár2ù0}립rî"ÁW‚"ʲÌ[ÐPCX:Ǻº:¾Rrˆ¥üÈKùÝ“|&±ö•Üð<7_IDMM·n¼%‰´ø‹\ùì³Çž=öœŸúz{w­Y³ßwPÞVpú.A›èí+~%÷¶Qyzzz¨§‡ˆ®^½zúÔ)­ÿræôéâ¼Ê555555©iMéçꄽJØW¾ä(Ë¢lÉ9•´»:%¼–vÄb11öpABXºªs ÂRêñDô•™âî}e m‹~˜ ‘ˆÇªãÌ™³/¼øâÙ³çüìÉàî]ƒƒ»QQ€‚c÷«µ]üJ™uñ˜VÈÅY,(ùΚïYL'MBI¢Ìœ^渊UÒb;9ïðLMNéDájvñz˜Kã&}‡Î®.íL¶·µéo&Õ¤šô)žgUU³û°©’ªóYðxÄmù« à+ð ”eé¶”î~‹Ë[ËX,ÇÉëDï|†°ôvö a™{ÐhöUñì+‹¡yá…Ïœ=ëGVöö®îííݽënqryì…¡¸ `—ÉĵéÆÚàMã$q–ÑzÜÐK5–›y™Ý¢AŒ ¿õª,£,ÕLH›´nyÄ¥ÜËYbÎïrJõÌ>WŒ1…YOW2C0ʈÑ?²Î3á/E7‘Þ:7Nwø­E?WœsWãqá+A¡€²,Í–R•­VÜŠ9Ë®BXš|¥ÌKùÝ+§–9§†é+ ×Äœ;þüùó¯üìU?…ìܹ£¿¿¯¯·ß}P°^Xðùv¸ï9áÜÿ.hšKÈ*ž•r'KYǘ‚'f^ŠˆÃ!U1g:FSꩪªçOgÜ ,ŃN2UîÂYŸ(-gzNŒ0õ‡ÂXv¬ÏÔùÑâWrNDѨAYZ¦ÜÑ_§W‰Zžêìô;œ8#bLÑD¿”VG‘ºÄ¦x—ù¾íʲœ[S·SÂå ÐWÂ2$_FKíÿ\"e¸g_iZ1ÿL_¸ðÆÏß<~ÚO!;wî¸{çS³ @AzXÁ–迈Âïî†qÝ»¤ð7Á‹qV;¨`Ê¿ŠÌ[Ëx<‹ÅÈJæ'„¥Ÿ3“ï–nv4§¯Ô·”ùÔ´b~ï½×^ãµ×ßðSªžž;wôõaX%(Ì Ó™8¦‘à¶«3½k`êɤ‡ÖæzsÎõ1Œ;Ìx%FtÕÆ!Òu•°7_ ”eÉV£ÒµLÞBXоRrÇ*7„%ÏIçKlò•™ýI}*å+ó3˜ÿâÌÌ/~ñË™ÙY?…ܵ}Û]Û·‘1h %Ý' êÉY Ï•0auåxŒŽ„Y ïêP‘úJWÕ#|%( ,K³ÍÕÌä9„evÊœC,]í^ù…°L‘ä¹/ 7 ®¤¢ô•oþâŸgffýÈÊîîî»¶oCjPÎ]¹ÂùÊÔÀI-•Í !53ѳ†…F"™@мªJ­(™4ߊ’™øŒÉ¨³RôeŠPÔÂBFJ$“ª~G5tÓ„®“1“ðÜZžeƘ!c»~V±,µýóçDÄX–BüJEQ"J*~¥"¤â!¢ªXL‹ E)}ßEQ”ˆ¾‡™D@ÆX– ÓÁÃW‚’ʲT:’øa,o!,ëëë !,mjn²÷•Îè+C²–³ssss—ÞóSÈ·oYµª§§»_kPîݸ¯,ÌœåÊÐYù_É¿TP–¥ÛÚå®hòÂ2$_TË0|¥ÔKŸ-žo_©ïa°ÌÍÍOLÎ]ºä§-›oÛ²es4AjP=8wåÌ¢@YC匩¬YÎ}}·-û5q}á+˜‚mírW4ya)_$„¥«Ú9!,3B“ç¾Ê’¾RØŸì= Œñ‰Éñ‰I?%tt´ßqûí]]ø"»¯‚ˆl@™ôà¸]coÙÍË~àVÓÕ“çÜ´¼þ''æn·]lßþ–pwùˆ˜ëM`>8(i ,Ñj]Ë0RîÈèHW{œ¯ä²g#ÝP‰¾RÈ®¯¼|ùí§NÍÏ_öSȦ[ß±iÓ­D´xñb|@¥õ¼¼—æ>a-±«Œñ+íD‚ª05|\‰D"é`”ªªªBÐI±¨«Â,«« ™¢ÔLXLUåzMS,KñXT»¸–nN~lÏÂÄ?-_G„ø•Q!0¥˜VTQ2á/EÑÏ cгª*STU4ªï£bÜ ýO&^Bû+.“PÞåm _ Âʲ$™˜ïííËYÝ”hKù½*Ζò¾2g¢¯©ñS _H,KϾ²­mE{[Û–-›ñÍ•F‘ûJ‡u¨ö;—§n¡Ð¯8|%(N ,‹—ÁÝ»‹ð[€ýG¢Ä4îª!n#W­ƒQšŠR”T ªªèÁ(yÄÐdÂêŠðº*™‘ªªªjjUU9W-O¦¿Òð>Wƒº: SrŸ:cI1~eDx­¿Ï‰3ƘË’eâZ2áµ¾ŠÖ“SC(Lq7¬M41ù”;ð• È²,Ù&ÐMlK·¾²ÔCXº¨›éq IDATî ä+³ä)…Ë’ˆúzWÛ¥ _{ݵëÖ^F«ªªðõÝY+n_Ú6 ³Û¸:^Ž‹‡ºëçÖÊWÚ}Jð•¼_&]„ß ²ÛøÊð|¥×Ë _ ”ey¶‹z刯”œ.¹ÛÅÂ2_é4Ä2×Îè—L°“ÀsùJŽÆ€¼÷Ë$‹ð[@~vÛ×’ o[ÌJÂZjk(VVãŠÂ W„‰áz‡ˆ‹¶Áýž[fñ’sÃ߆‰áƘž™Ùñd7Ë› ¸YúΉ1SÌJë2]¾âŠ_‰.'(ËRmƒmY#98%ÜíNæ9„¥|õŒ¯ÌÊ2ÛNÂWP,½²²ó•¦ˆˆâ‚Ƹ–ªø¾ SXEaÌÚ'2Æt#&z7Q“‰=HSwR5ªÐ ®Ž]¤HóGŽÊ2THË£]­ ,EÑ÷Ѹį€ʲ”[GMQöKù ÕÕÕgË0RîÂRÒW:k¾M÷È*u>x»Í‹ÿêxsö‰Ä|pPQ(8¥Ù:º¨›|N ¯««“ßP>CXÊ×ÑùaXãáÃW «   Ø|ežöÛÛŒ½ü_ß<ì$|%¨40ʲ¬¸råJ,›?¾’ä†XºjfŠ0„eX)wd§„Ûîçüܹ†¦vrã+ÓŸ’8‰‚ˆÐŒð•Æ‹†bÅ9Íâû KÏ›f¤ˆc§"†’Ĭ vq®Œ12Õ O­†ƒ&†‹;®`zyaÅp´‰ðœ8cÌn2xöòÚy^Ið• ,€²¬¬–Õ­¯ŒÅby a†¯ *-Oà¾R§¶¶î7¿ùå6ç/ohj·ó•<û¤¥[l_‰¦€ zUþ‹ð[@¾vÛqGåÂûÌx41³ÒòpÎy:áŒP”ËRˆÄÅ9WmöYSîpè©ÍyŒ‘;Iï¨1ƒ²4¤Ü1EŒSJ>:Ǭß×Oý"~%(C ,Kº±”m–¼ k¯­­ÍgKWmm¾CXÊUñn}¥ÌÁZ ¨„¯ 0]° _Äqóð³·ñP—'Ì bY†3"³1濊ð9%<;Ž–dbéªý±ÉÜr¸'_`\•Kóóã“ø^€J¾2o%pânµ$|eî:øJPN`”ey6™brï+c±˜ó”pù=DK²a)×øö•Ÿ˜œŸ¿<>1¹f` ojvTÛ”}çËÅã½Ï ±Ûò•ùÞIO—ËTeÌÝåc ñ+AåeYþM¦[_É9ײîPø!,Cò•ÅÂÒîä[î‚[_™ýf€ÍÊüüeíÅ[cc4Fñ¦¦––æõׯÃW‡žßÂì3Jíµm~"«´0œ‹¹BIQ”œ;©­’½ig­ÆóžÄ<½KŠ!ŽM.=ߎCiŒ,fdfûJg} _ Ê(Ë’nïrÙ.O!,c±XUUå%„¥|C[~!,ñ•æyß!ûÊì)ás—.Í]ºôË_½µöºk×­½ßMìúoÞ ð²ÍÒ_I…8L¯¾2Äå óÁ "(ËÒn÷xîݱ$¢x19wé’‡Ξ=÷ÝG¾GD[6߆Ùâ Ì:i~ ­cæ^ëE0·»¬(Šüa:yTU5¨³Ë2(¹«ô9œs< Ù¥qN®RôÀW‚Êʲt›Bî-]¢Åãñba’¯ $„eྒKÌ1çVó¾=øÊÀÛx<ÞÚÚ:;777wé­±1o…<{ì¹g=wÇí[V­êééîÆ—”z'Íoùêº×e;¾2/óÜC?ð• t²,óQÆjYw,WÌsKÉ:º !,]4n|¥ÃëC1ýûÊ"4ÇãÍñøºµ×þê­±™™Ù™ÙY…<ý̳O?ólwwWOw÷]Û·á{ ʶ{–£7áa›¥ç+‰J#ß|exgI ,˹A”™NDuuu%ÂÒUUžç–ò×ÎEÒp¾’»?uÞX·ö:"º83ó‹_üÒ›¸ð•øʲ¬¸zõJ,3U|¦!–¦œkL„°t²P¾’gŽÈp*ƒò•S'NŠSÂ-yâɧ[[—îº;¨ûùî;îÞ¹cbròðá#““SJH$Æ¿ðà‰hddƒ.@A€¯ o'óï+ÃX¾K ,K¹åË5';Ûʱt¹ÅÂ’&„¥›6)wjK_É-.®~D†/ÀqòäÄÄä¡Cy+dß¾Ñ}ûFGF†‘¢äµ×橣绫@o3ì…¯ô|ð• B€²,Ù–/ׄæl_© ±¬”–nNcQ…°45ÁøJßÍÉ]Ûî<þú9Zj1á¡}ûF÷õ÷÷ ôcÐ%½×正緫@o3ì…¯ô|ð• rPp Š™¾¾^»ž~êq·UaMMMq†° #åNP!,Ãð•ÙKÚ¶EÅá+5nÝxË;6Þ,¹°6ÜòÜùó)vïºûþûÿôþÿòé¾Þ^o%$‰}ûFÿèãŸØ»o•  øÊt—'û_/ø>QùJ›ƒ"UõuPrÇ_ *Œ²,júúú>U««ì!–‹-’ÌRç–òwþCXºÙ½@}¥±5ðã+9Ö¤456îÚùâ˯H·üéÞ}mm+îÙ³'Œ¯Æý÷÷ÑÁCíßÀ[!Úlñþþ={†Pá€bïöàñ$`2y1ÈCÃ]ˆ¶¾DÆWú?Ìâ_érĉˆs»J¨‡_ Š Œ²,á&Pf®Éjkk aFÊé¦/–Q]ûJíǽÔÁ ¾2XÓqëÆ[6½c£äÂçÏOÿíÃ_ c¸¥ÆðÐàCû•ÏüÙýÎ~ß±DâóùàþÑïÝ» ¤³æzøÊK(R_î*ð• ô²,çfP—bz¢pÿSÂåëëâ a†¯ 6„ev«ÃW¦·š5ØÒp°ÁÇï}מxS“äò?Ý»ïŸ~ðÃð¾ ýý}|æþÏ|æþáá!Ï…ìÝ7ª‰Ë±±1T0 ´ŽšuÊ×Fá+ÚOøÊï1\‚‰áåÞ^…ç'„¥ü¾å?„¥«óvK‰yúÖ¾’sçÙWêlÙ|ÛìÜܱ瞗YøìÙs~ñKï}Ï»W®ìiúû× Œ íÝŸH$‰q…ìÝ7JûHK,¾gÏêà¢ãµËæ¯Ó@¿Ñçrîý0ÝvÓB½ æÏ±ŒSaêž9ôÖô9ãð• b²,åæÐQGéU§>ÄÒa±â aRÊ" a™‘‹ö­§lUêÅW†×¾4Çãï}÷½O?slfvVfùüðGíþÐCýjŒ  %ãûFG½‰Ë±Db,‘Ø»otÏÈ0Ä%ðYQ—P±P¢ˆ^ Àa1ëºmzyŒ¯”8œˆ…ºŸî¢‘åÚmË •_ €+01¼t›ÃÜ‘,e†XJ60 aé¢ñ+Ö–9}¥s9AùʰíÆömwnÛz‡äÂgÏžû_ÿû/N:öwd` ÿÏ?ûÀ×¾úЈïÙâŸÿü0[8vÐܯ_æÙ ûLb>xh—€P–åÛd¦…Çb1ŸSÂ]m1Ï!,]Õõùaé¿Yöã+yÚWæ¡ÑXÞÒò»ïoKs³äòß}ä{ßùî#ùù.ŒŒ ýkÿùgèïï÷VRô€œîúkþz|tÃ>PøJ±Cî*ð• ìÀÄð2oµDáa)Û&ä5„%—Ÿ‘ÛWŠG-´o<¿ÆÝ;ïš»téà¡Ã2 Ÿ>}æýï¿øÈ‡?¸zÕª<ìÛÀ@ÿçKŒ%ûöz+dï¾Qm¶øÀ@ÿÀÀj!P9¾ÒƒÀr\GL.Êsî#'5œë‚꓎Yi€Âa§,W¯å”e‰·Žu‚>ÄÒ®å@Kâҵp€¾Ò±Ïé+Óoäô•yl™aÒû(uÞÜZ6O‚S‘«ô¾§¹Þ·É‚¸:ð• LÁÄðÒnk»!–’5BXf±tß<{õ•BSÁE!êÉWÞzÌÏÏ;/0¸ëîÝ»vJ–vòä©ÿëÿþŸ'NžÌçhÏÈðçþüÏýù>f‹k‘.1[øî2¸' üT¤8c Ð>Y¾ÍN…Ìç¡O÷è+ÃÞ|%:P–%ýpæT—Äb±ªª*ÏSÂå+¦¢ aRÊ BXò\“Ö¹à+ÅE î+_|ù•ç^xqvnÎy±­­ò‰?nk[!Yìß}û¾ù­oçùK400ð¹Ï}ö_ÿêž‘aÏ… E@%vǼôàà+C;LøÊ€Î|%( ,K¸t®jjjÂaé¶"ËsK-I‘…°4œ4c;àÁWrä¼ð_~e~þ2{îùœÖ’ˆîÙ³ç]ÒS§§Nœø?þÇÿ95u"ÿ_¨={F¾ñõ¯út‰=•Ós½ |ex‡ _Ðy‚¯EbY–n#éT5$“ISËRîTBË0|%—É ÄÉ»¯4¦è ªÑ}¥Æ±çž_30°níµÎkµ·µýÉ'þxïèèÙ³çd¶òµo|sUOÏ'>ññü«>÷¹±±±±±Ä^¤èÒÏdòC¿s¾Ç> Ÿ(eb\Â\VÎ#ApOõÿB*‚˯¤ªªÍažsTnsrx¦¤ªÜÕþËwëôbÜ~UM[¶[›1–*9{W…uE¨OTq¯„ÅseæÉ>fæû¾…¯Å”ey6–W®\ñ0“šŠ;„e(¾Rbˆ¥|æÉWò7§¯LKI»”â.41ùJ·ÆÆæçç·o»3çêï{ï{Μ9ûƒþHf[S'Nü×ÿöß?ùÉ?î]½:ÿ_«={Föîݧe÷PRôTtÍ,ñ³Ó”NÁ"ðPx†v:þ¸ê‚ù|6énä·ˆBŒ¯ô°QâaŸI™ÕÃÞDãPá+A‚‰áeØX&“IUM^¼xQ¯JÊ#„¥üîå?„¥›ó&Öï¹ŽÎÆWr=Êe¾|åÔ‰“Ù¾RcfvöèOɲreÇŸ¹¿££]r£=ôÕ¿}èá~ÅöìùÜç>‹=À¹ &ñDïc£ð•­_Ø>ÀW‚¼eYÊ%ç·Ý¶-û£+W®ÑìÌ .„e)w‚ aRÊœC,½=Y¤#QòÔ N¢%9_é³A¹|ùí©'·–Dô¾÷¾ç÷>ð»’ ONN}öÏÿëÄäd¿kHѾ2Ôį ï& |à+A¾ÀÄðrk2¯\¹¢ªI±6)HKWõ]žCXzرpBXæ8Q¿ù—‹-mÆFÁ«¯ä¾£Y6,[vùí·ЬåÝ;ï’)­««óü÷ÿöï>rúô™åz諽½«ÿôÓŸ*ì7nÏž‘={FÆÆÆöîõ6[\KÑCD{F†1[ Ô;_9;~7ZÄñ+³…ê©bÐÕ˜_ˆˆæææfgçˆøØX¢˜/zCcCcc#õööŠï+BÔý¥vl†O20cŠÌ ±(R›Kbˆwi³B¼hLú¾…¯Å ”e©·œæúâêÕ+j„°ÌK}ƘåFû¯3¦g·¾R8mþšê†e75l8þúvsÃ5ffg}bp×Ý’Å~ð÷>pêÔéï>ò=™…'&&ïÿ³>ýé?é3>.ä¤è©ŒšÕWžñ#-}öëÿŸ½÷sã¸}O5&rrÄÌp†ä$@b¦(Ù")+ZÉâÌ^ïÚrÐÚk[V²%Q’ï}÷ݽkíÛûÞ»ß2H”de9®íõÚÞµ™m‰I1H¤$K0œœ0Ã8èzШnt7º˜9GøÄB£ºººººÂoNª²|/ÖÂÈ<¸¾DAAI±0N‘Å»Â7óA’@¸$Q­†–KƒÙPp9aÅV/EÄQ¿rhhxhxhxxxxØ›*qtdttdÚZÛ H0Ôö#4Ã#¯DIyAd™ÚÓ Î DͨX›’×…exeb\X²ÿ ‚à÷û#L †y%•#+4,ríç®QÀÃKÿÀO~ö‹;︭²¢ÂH™ .ø§ü¿~ño¿:s欑üÏ>û\CCýcñ—Cô         @„E|L[=Htqá•CCÃÃÃß>= +#˜m­mÅÅÅÅÅÅ Q?,ä•((F‘e Oƒ!KM a3ãÂÒ`Ý’Õ…¥ÎèóÊðâZ¼ÒªÙŵ€}û_7N-à{ßù‡Ž3g~ú³_ÉÜÚÚöð#6<úpccc2¼ˆ 5Æh-΢ô µ8 Ê¬Ø¨Í ^Q>ýô3Æ+gß#immeà’'ÕÃA^‰2;‘eŠO‡º?%Ø…¥©‘.Á.,MʼnqaI Æ‘óJ¥Lc¼ÒÂéŵ¬]Tk°ØÚE‹þï'þé'?û¹AuËO?ÛØÐ°aÃ#Iò&2kqعsWŒÖâN‡£¹y=Z‹£       ¤äm¶û¯Tì€$ñsp>ýô³Ó§çDÌIïˆ×û¾·¨¨¨®®®¨¨ˆÿI $ä§’"éá¤q~*(ï¿BÞ¤H`[ǹÂdî,C§ÄÈóW¸©@^‰’*‚È2U¥««#–PÐñ¹cGšªarº°Œ¯Ô3ï—Ý_̯dbœZþMsÓüª*ã%ßwï÷Ξí|é•Wdö´¶>üȆÇÛàp4&Ï‹‰!zfßCË›¤"-ó_ɹzðkø²d§¼öúzåÊyÇ J È-·Ü,ß«lÔ‰â8—¶Ùlpàà\yÜÁ¦=qâ„*¸ŒqÕM ukk{5 J,‚È2ÙåKwÞ±wßþðã]]g" º*–Ç&ta®bi¼ñ F^ –ñJËg™ukÖŒŽ|ï~¶?íÜe–ZÖÕÕþïÿ÷½ôò«íFò?ùÔŽÆÆÆ–Ç7 ,b‰ IDAT$Õk‹!zP²oÿkàñ´¶µµak    D'o½ýgþ+#˜uµµuuµØ8(É DóJ°W öÙéYi®µSSaಮ®®®®Î’Ç›ª¼'Šu‚È2¥GI=‚O?U9nit8ôU, aèÂTT,­Y"DÍ+©<€x<¦²ÒÒëÖ­5B-¯¹zõ5W_mªðûï»·ãÌ™_zÅHfÇóÐ~ü± N§#©ÞP Ñ3Ç…ýµIõoN(((((±Ë[o½ o¿ÞrËÍuµµõˆ/Qfjk– öà& —ÂÄ8–Ûüë§Ÿ}úégsi'®ÙÔíííííí«W¯.‘;¸LÇC^‰2û‘eªOš? Gpuì6 PÕÖJããS‡†1J ¿  ??_u¤C–¦]X†Í‡y¥År¼2Nó†Ajyìø‰žÞÞ¯ínS…×××oÛú¯Ï=ÿB[[»‘üO>µÃáhÜØòx¾§¢gN‰ÛíÙ³g¯§µ›%‘òÖ[o3|ùÅ[n®««­×ÐrBA‰Ï¦,%y%ö_)ß1ñ_axxøÓÏ>Š^¹²¼¼Ün/7Þ¤a¿È¶~2˜hð U–Fj¯×ëÕÞŸéɉ'ЋЮºê*öUáhÂÏ{àMÈ 2‘¾ËÝ]ð- ¥)Õs@!íXÁ°Ÿ ä•(É ˆ,SzjLÐ…Î3Ñ…®.ƒææææææ*}·ñ–ÒÁtö¼yóæÍ›Å.,#T@ƒW†l°ÀÄóJ&e¥¥×áºw#9¬éííûßýþî¯~Ålù?xèÁÖ¶¶çŸÑHf·ÛóÀƒ?hiyÌép$á ‹!zRhwaÄg¥(ŠŠô¾ý¯íßÿ6, ÊÌÊ›o½ oÁÂ… -\xã7HÇm°¥…6Aç¿R!èò’÷k iœ+LʻŔûµ$r/™ª5D¿–sgEatO§V‚A·TòM‡¡Þ%„ã\š—P8f•ö5ü¥‡ŒÆsåòåË`Ù²¥ª-IyÚ©l©œ¼ª.áôÊ[€BX£S .—K—ƵGFGŸ8±jÕJJY(u¸¼k]A¤Ò%ˆ‚X©>*Ù€~%{¯F^‰Ad‰2âóù|>Ÿµefeeeee«ƒOŽ@FFfFF$ ˈ3åë-g‘¦x¥%ÜR¾·——Ý|Ó oÿùýÓ»»{¢£– õõÛ¶þëó/¼ØÚjÈ àöíO%­º% Ñ3ûdß¾ýûV¢   $“œ=Ûyölç;ï¸á†ëo¼ázl”8I2ø¯4O£¬¹Ä»˜U®\¾|™Ýng:•a“”W€Ãá`ÃÃÃ.·[UõRUÆÆÆþü翬\¹²X7&|ÿ•È+QRUY¦ô‰m’ .\¸pÁXÞI£¯GZZzz:(½€g H ‚Cé‘©Ä2Ò™-bç•ñî)öòò›oºá“O>ÒÉ5µ€Gþ¡§µõÙgŸ3’™©[nly<Ù¼[ò‚!zf‡ìÝ»½U¢   $³¼óλï¼óî?|û[èæ%Û±™·‚F™ÕóU½Ä_Þy×8¯´ÛË—/_^QaP·uKf^Éç/))Y»f ¸\.·ÇcðöOž¿plœÙÇaÓx*A™P BnNN]í¢ÁÁ¡©sçtštbb²«»{ÅŠå:E±4áÒì§’ââ;︽µµuddÔÈã;|øˆËí^»v $±eVii) Ñþÿ›-/.·ûСÃ.—Ûëõ"¸L˜¸ÝžúÑ?£ÏJ””S§>ê8s¦°°°¨°äÖܼ9BˆäWNá{NvŠüt­¥…‘4Jò,ûY¢§····W5Ï5×\+ÙQnÁòô{Ûzéúþ+ßyר~¥Ýn_·nÍŠËsss!Åy%_ZII‰Ãá ###FÚ¡¯¿¿   ##S¤Tä4W(¥$˜V§|ûþY€d%®ó4ÙqÕ^ªëòÒ^yêÔ)Õ¼yyyùyyÜÖ/´ÙLÏH¥ÓCé .-ØBzB²£Ü‡Áñ‡â”D–É"Ñ Ëko ï#²LÁgm08{IÅRÓ¼2”–M½"ËÃï=yêãÒÒ’œyó³…£±aphÈç›Ò¹©‰‰Éß[°`Aaa˜A–,ñùÏ}Îéh|ÿý£FšÚëÙµ{Óé(--Mò‰Êét®[·Öétx5ü|¸Y/ ÑÃJÃÍì:Lkf~*òäSOïÝ»Û%…dllüäÉS0¿ªŠ ïL¨(RJi€PšA„ BBˆKCX$IÜ3§¢˜E–QJÕ¿S Õº°KÈœHÝXQµX4ÒG«Lõ<áÎ+Ã}d1^©åU’Ûn»uÅŠe VÂ,â•R¢¤¤ÄÑØÆÀe^~^ff&¥€O€PJE*†-@ˆô‘*)ͱE«—j ;êW"²D±JÐ0<•÷ÞØs€¯€¯ …! æÕà•ªê~ÿ(Sr<øÞ¡ëÖ­- C_ºãö½û_ëïÐ/çW¿þÍ=ßüÆÂ… ¢¨Cccã³ÏìØ±ãƒ n[·=ép86ojIþŽ!z’ò-ä©¢èv{v<ól,67­çž¸…ýq¨YhœïˆÆž™R³R‹ï"l¯&2w!#¥70Ê¡6Q4úŠi‡¦ÓåhÜÙ‹FÞ+š=Ñì]ÓèÚ_û9éЙÇŠ»)å=Ç·7&y%Ĉ,GGÇN|x2üôÒ’’›n¼>Y‚ajùßþûúº:•‰Ç²déíOîðö~­P·L‰‰*Æ=L0DOÔƒ³„,Ÿ|j‡ÇcÂs%#• áÅÈ+‘Wš©-òʈà äA^i¦8ñJ>d—˜z1¾ð…ë®ÿÂuˆ,çÎt;²ŒeïL©zW7‹,uT,Ã7ûÜûB4ÞYõÞÈoNÙ5?ýì³Ï>;­=¦\©º¬šõ¼’%¼Þ‘£Ç";°Z¾ly~~~p<±©Ž'ò¡‰¢îf×6æ°ÉEʯ?ÔèØ^´¨"K«‘e²ìŠ£C–Á äP+¯ÔŠC aôS>ƒ2už±Ÿ:¤bÖ@åër ðé_`'1"Ëï¶„W¥½­R½„d©Å+™”—•ÝróÒÄÃ/è½#£Ú¹+âm~÷;ß®]´¢E–àv{ž|j‡ñ†•Ô-Sk¢Ú¹s—ËåŽEé2-5ö-Z_ù™!ËO?cœW&Ö*y%òJ3µE^iŠî!¯L^ÉÂÀ¥»­Ï`Ï^°`Á7¾~·´â—"6ùR„_áháKAd‘|QÝkm¡qÿœ€©?dùÐCÆV+uÜ&§vš/©(òàj½„D%Iÿ­^GøÆ‘~©848tàà{ú7ˆ¼’ý;2™Zæçç/Y¼$|ó%Ûæp.ôçJ‰@äHŽCu„H“ ùc¶px10Ý#²D±JY&tšcÈ’ŸA÷ìݧäá‘GIÃ+ƒÓ°JMƒ¸F¨j=Oö>w.U™T‚$´Ís|öu¤†åwó jœW†ž^ð`_ ÈòÄ'GÇÆtê)QK²ÌÊÊîéí5B-oºñ†›n¼!jdÉ$ uËTœ¨ÐZÜÂ}‹ÖWÞÜï÷ïxúÙVcŽSîBy%òJ3µE^iŠî!¯LA^)} ‚Ë~#ý»¦ºúk_ûjøØf³ñËŒŒ Õ4¿ò 3äT׸Dd9ƒSÿL"KQݲ[ᤒ«•ì.ü¢¨ÔQ»YýøÑìd™ òüü^ŒG–ÿùŸÔ¿;ä•üFF¼­­­#££:-&QKÈ’RÐA–2'di !²D±JY&tš«‘e¼2,Qå¬`–Wj²È¨x%æøPz4üDE»qG$åN©&m'äUR™üYæî³'g¶#5,¿Û8¯¤á3.G¥ûÛ¢G–`€Z.]²xÙÒ%áÈ RËE‹Þwï÷bA–„·Ç³}ûSÆ[xó¦–Ôµ•Ž%DÏ ñµ¤Û·h}å‘åö'wá•3јÈ+‘Wš©-òJStye*óJ)ånëÝþü>#½\¢–ˆ,gýÔ?SÈ’W±„è¥_TF,D–ŠšH§¼óλCº!wWª,'(=zì˜>µ\¼xqA~Qd)imk KJZ"#Ë`Pòh-z‚ÈÅ*Ad™Ði,E–?ò#m™8^)Y'ăW*hÄÌ+¹Ó•3%U™®¤;å§IY™ìÿ}]§¤Ùr+Œð%ô`ßÇ&岯ªòJÅcŽÈ+bE–ú¶áLn¾é†ùUUáÈÒ8µ¬«­½ÿ¾{cA–lâÙ¶ýI·Û¨º¥ÓáØ¼ycêŽ6¨tWdiÐåL4 òJä•È+‘W"¯Ô¿»@zûó{¨[2j‰È‘ewƒ%´Wrœ1¼2¼žÖñJ6¯XÈ+7h1¯¤²rÊ)wf,¼’u©ö¢ü¼¼‚ü|Ù_þ¹é---%//·¤¸¸»§W§·µ·WTØórsƒ§‡&¡’’âêêꉉ‰‰‰IFÇÆÚÛ;V¯¾Šp‹~‰G±n¤¤¤¤¹i½ËíözGŒ¼€‡v¹ÜëÖ­MѤ´´ÔétJŠ~^¯7îév³vðz½s!¶¸~ø#þ+™Šnii)òJä•È+‘W"¯LZ^ %ÅyŽú ïˆÏ;êÓ鬕y¹¹¢_ý"+G Š`³…Ò‚@ƒb³Ù¤´‚TDøEd™„"©ôôöööª/w¯¹æê(VFaUzð½ÃlE×ÕÕ]\\”••I# âNfBa;Ju};vðàÁ÷Ο?¯Í+Ëo¿ý6ä•Z¼²²³Š‹Šz{5‚]ºt)???33SÂpÊ­ S˜dÜÚc¿K"êxNÑÐF†“¼’=uê#ÕŸòòòòóò8&Ë…8ËPq–Ác\9¦4¢Õ®3®â›"`¤ö–ÛZ^Iy6f1¯¤2&·žWrޝ¤ñ䕿—N™‚[À+­Ò¥.))nl¨×ÏóáÉSZ?ÕÔTßýÕ¯TWÏ×/¡½£ã¥—_µ¤Â›6¶llyÜ8°ûþ}¸\®”š››6oÞ¸yS‹Óሮ—Û½s×îïß÷ÀN¶ü³UöîÛ‘W67­Ÿ —È+‘W"¯D^‰¼Ò¯d⨯lyèNG]…~—ý¯?þ©»»·(ÖL«1LÚ¯drøÈûÿ oa%‡‡‡õÿ ¾|ùr@^©ºçFþ¢¢âúº:fìîîŽaBãÞ)ÍóJ|÷Q,D–©< ‚Õ¼RF'-æ•ò:XÀ+CBT€[Á+í!ç•`¯ ýé3X!I3v^IãÆ++þƆââ" ýý{÷¿¦“áî¯~eÁ‚ý«´wtüÿçomk‹½ÂN§ã¥Ÿwæw[¶nß²e[ªN§s󿝼übsÓú¨ aàrË–m©ŽqÍŠÛíÙ·/‚ýàæM-3áýy%òJ3µE^‘g<È+Í’´¼R’–‡îlŒD-=Š»  ¦Uëx%“÷Ó§–Ôjuú´Þ ðÖ[¿XQaG^©²çF~v¬¾¾¾¨Hs599911Õy%Êì4 OžYÍ´a8ôöœéé9ÛÛsòò õyeX|îk̼’j â–ðJ-÷”ÖòJ*ƒ„VóJ•†UÜrô¼RaÑÎ+)¥çÆÔó膫z¬©žï9þ‚V·ôù¦úúûxÃpÞEæUW­:ÛÙ9>>¡ÿR?~¢¾¾®¸¸Ì†ƒ\ÕݺµN‡ãÐáÃF^F¯×»s×n§Ó‘pƒ_ë…Y‹;ï°7:kq¯×{èÐaæ%sv[‹KoÕ?þÏéçœ!ϧÈ+‘Wš©-òʈ<˹3UòÊdç•ìq¬¹¦ÁÝÖ?¢m!>99ÙÝÓ³øÊ+¹š4Î8‘w±Í§áwÐ0<…ÄZÃpÍh€ø¨òÊ@Ýzz‹‹‹çÍË6²—$ªA¢ùõs˜+^u¢YÚíå+V,磊#¯ ùù›ÎÊÎêëÓ4¿páBQQ‘(Š”‚ß/úý¢ÚÏ.ÇœQ0½. "¥ìC! ³"7gNJ7 îk5‡­=æy%†£X%~'¡ü ú]E#áwô¥²rAUÕB*s®Zý y¥V˜òäç•á-ä•¡&õzÆÂ+©Ü9¶A^ CêF¾ááwø‚Ÿ9xïò6[Ú¡#G†‡õت•+Ö\{­ô5+;ä´>++ þíW¿îììŠØ“zè†úz…/jU¨jd?°eëv·áàÚ©L\!s*DO~÷ý;ÏztM‘W"¯D^‰¼yeêòJ)¹ýùýžöN|×wÔÔÔ¤spÍΞ§³ª‘–%ÁäCCe¸ ›‘­õ\ØNÇi+ª³ PÝp~çØ±ãÇ4â¥<øàƒ/Ï%Õ#ƒó‘Wù;:¼R’Õ«¯*ÈÏçN§ªÁT„bL‘u'›Í&ÕFõÆP9 )uÊÂO|ë[÷è„7D^©à•LÚÚÛÚÛÛµkccc^^^@áƒRÅ"Ø)œŽ‚ôÉþ‚" A£>€ †elú•¿øÅ/U³aø³‚Z–É>yëkYêˆÏ7Þ××Éúû:?üàÀ‡üðƒƒ'? | ¿¯³¯¿³?ø ¹¹š¼’j³Èhy%y†Ê®VðJÅ´'^v«–òJ¹]|¼ŒkYjÅãVL¹¹¹úÀ±¿ ªªJú3¯eÉfÖåË—×µ,ãe:¡Æ´,¥ôºµk榺%`ˆžˆ¯(z<­{÷뙄;´G^‰¼y%òÊTç•ÔµœÒêÇžÖÖ«W¯æCîò= _Õp(AO+–(µp;° Ï{µµ,¯¾új#åšý¤Îi„W@___aaav›‹ÁTddJŽ,¾×Ézc 14<ìr¹åÇCéåË—ÙíåŠ{á5.‘WªÝ4é ËK—.•””G“ù1D ‚$íÏø:¨w -d³=8jY¢ ²Ddiô÷wö÷w pŸ¶¶O>:õžôùøÔ¡?:]ƒOçà`W¹½&^)Ímñà•TÃO¥%¼R‹YâÄ+æðÖòJ)í=«nµa8[  8øž~Çø›æ¦ùUUZ&Ti6[Ww÷Oö‹ˆìÎ;n¿óÎ; 6Ãp^\.÷ÖmÛ÷𲎻ìܹ+Fkq§ÃÑܼ>©' Ãð]»vïÕºóÊË/"¯D^‰¼y%òÊÙÁ+YÚÝÖÿä ¯ëôé/ÿ·f‰Z¢ax‚7MqZ¨n¸ÀÃpuãhC†áŠâ¯È«V®(,*äJR)÷¦Ê?¶4άؖ^òÿ´“"sŒD–Ë—/[¾|Y8¯äòJu^ÉJ;qâÄØØ˜Ænº¬ººš(”†Ï”ø'È=›ÿ ŠMjBˆ`„âbU¼4 G±JÒ° P¬’¡¡î¡¡îÈtãÊσ‘„ÿ㪔*+-½ò çgºñ?^ý7Íš+rBj-úîw¾‘ZîÛÿš§µõÑG¶jvq:¯¼üâ–-Û\Ƽ[nÙº}–y·dÒÜÜÔÜÜär¹vîÜí2ìè“—Û½eëvhnZ?vÓ&ö-Š4¿aÐç•›7µ$¾â3\8òJKîyedš€¼Ì Cä•–ñJpÔWÜuë ]ËÃGÞÿÒwèS0~Y’––Fƒ3‹˜–F9Oþ­-ôlÚ?GÁ"-Ä—F¥â/—ZÑ”® ƒ&§!~ÕáýRÚ”~%/míK—,5j% Å™S‘¦Ñ'ý¡Ó™Æ¥Ûíf­à§*«÷%‹¯dW¤aÑ{Ô›y%ÈJ+**RE–”ÒÁÁAYƼ0Äøà(³AÐ0<ÙçxJé>Ýtʉw¸‡ÿŒx{<®cü§Õ}lÄÛsîÜĈ·—}F½½##½EÅ•ÖòÊÐÔ¢„˜VòÊð=òJ^¹tyåùq Ãðü0Ãp -€4›ºÝSe…}hxxjêœÖŸœôõôö.[º”_Ð+Š**,\´háÉ“§ô;ÏÈȨ§µ•ê£ ¡fCpB˜‰ô¡CsÑ»%/¥¥¥¬)JKJ¢— Y‹³“¤åé¸àhnZŸpÏÈ+‘Wš©-òJSty%òJ.í¨·»Û´œZú|Syy¹ 0CIÏàÒ²6 Ãðˆþæ Ãgnñ†S³WäÓ]ÝçÏŸâ.^¼8>1QVV¦µÊUYOe!w•ïÜnw >a?-[º¤Ân'„HûUÿ•ÌzYñn"¯d‰¢¢¢ŽŽ­¾‘›››™™©2†pþ+ASk[:N©"z8‰¸3¢@A–"MüzÝ ÃQY"²œÍrþü䨷wÔÛ;:ú´·žho=ÑÁ>m':ÚNP€±Ñ¾±‘Þ±Ñ>ö¡™Yy0C¼2ܱ¦…¼24Ý*ë™WRJÏ{T›: déö´:ò>°8‚ 4Ô× êSË®®î¥KŠRóúTTXXW[ûáÉ“ú}cddÔãñ\{íçU'¤(%ƒkÍÍMsÙ»¥$s*D,wìxF'gÂUk‘W"¯D^‰¼ye"x%û§¤8çÈq͘y¹¹••€ÈrVo¸ &dI£¸"Ÿ®ž?ØëšZNhP˨‘åGl ûéÖ[n&ò-áþ+CÑ^W‚Z=)^Ñ’ÿ5333OFô„$&%+ϲdU5Ž,#êW"²D±JY&û ÊUcCCcCCC}}}}ŒŽŽb+ã•Üg|´o ÏÓÙñAgÇ²ÏøXß… ¾ñ±þñѾ‰±~öÉ+¬€Xy¥D<£vŸ½ÜÎÖ¨,NsºÊž JS²}(AØ‘R9²$Dã)+û‰TUƒÈ2"¯¤>ú‘%Š5‚áw1Fž«tÃïðiÑò{"y‰nkïhm ™4úý~xçÝø ŒKUÍ nº£›oÏͯPõD^©¾n-¯ £]{To¼z~Õ‚šjB¸ð;OägŸoê°ë”––\ÝuiiOóƒƒCo¾ýgýv¾û«_©©©V:ªN<’å—^~µ=ÌbB! mx4Šˆá'*ãÞ-aÆ¢H'Z’4DNNŽj:==]Ú]§¥¥¥qa"ø¿×òi­P<ÆwÝÉ¿2¸3JLMBKyøKÁÕñã'ŽŸ8¡zúƒܯú jíéÍÏ9œžöKEˆ¢(R GZ1%''§¾¾^Þ€0°%‡;”RYPM.‹M°y<­lQ¤¿„,[ºdé’%Dîß&†Âh¤nŽóJJé~866¦ÚQ—/[. ‚MClBh÷¤Ø¯ÉÆ.(wIb”üC6€,ðJøå/1ü "˹,ù™Õï÷óþ•%d©H_ºx‘›§ýAÊùÈíÕ©ØÛÛ××ׇÏ˸”W-SŒÙeUK£æ• ç›ÖòJ¶@d)‹ž¦ò®c'>PÕ|,--¹å¦¥Ùqhhøõ7ßÒoû¿ú•ÆÆ~¥Î#K©¨_z%"µ€ÇÛàp4rs±È\.‹'cPfk0ñðf‰:D$†èq¹\.—›g©/¿ôB„ý‰²|äÑÇ’ãÉ"¯D^i¦¶È+MÑ=ä•È+#Ýéî×Oíyã#­îþõ»¿ŠÈ2PŦ)N5± YÊITTÈ’_Ó¯½þFÔw­ –„ÃQÆ‘åûï5dù¯Ý¿BQ}(°$òʈ¼:::´v:ör»ÝnÁÂpF%¥žnY*Ç™HÈÒ ¯D–(Ö F ŸC²v͵?èLOOOû§auà§‹.°Dÿ3û ͦ§].l=&ƒ½+ô)ÌË-ËÎ-çWæÙ9eÙ9eÀùÐŒ¯ D$ŠE^ëm­Ÿ†‡½CCÃÌÇTVV|éŽ;öî×ó¾ú¿ûýßßóÍ… è_ô¡ïᥗÛÚÚõ³=ùÔžZZ%N§ƒ‰«6ËæÍÎpPhJvîÚ½s×îæ¦õN§#j¨OYɦŠÒ ît8WZ׬ϼy¥º‡¼y¥;]Ûrd988¸¨v®„QL “Q±zõU'N|]‘SSSmmm!jIÌÆ+µkM ȉ—ünÂy%Uuy9·y%Fšei¼û¤$Éð‚Æý½@A D–(2©¨°rèÅ‹àÊ+œ ×ñöÇ&‘R::6666Ž ç|Cç|C³–]©Ü J¯4Î+Ã&c=^ã_°‹ uœK¾ý—wîùÆ×¤¯••«V®Ô¢óîƒßZøÍˆ×ýáCþøùfŠZÀæÍ«[ºÜîïß÷À\P·t:,D3ÇŽNérç®Ý°‹1AGJ—Z×ÝeYêÄ7s: ÛûÌpáÈ+-¹ ä•û[sÐ<޼ÒT@^M'i¬³{ÚTs  !²D±ŒËP½U÷Ê•+Nž<]ÁJj©y ¦õa—.Y¢}–ÚËFU D^)(*,Òjjß”¯T,BDÞ©èìÄ Q¨w8¦:CéÑvcä•(3%h§™,i Ãý~ÿôt =í÷û¥ô´¤q /^àÒUÓ~¿ß(E1T¿_äk%]:ΜUÜàÙÎ.ìÆ%#»43»”óŸ SìÔç•çÇÝÁ/0}Ñ;}Qï¥Õó«ÖÔÎîCf4Ñ7÷.//»õ–› --Ù,ìÝ·_ò*­* ,øÖßÒ8•~ÞäARï7B-àñÇ68« ÂÞ-ud¦¬Å¿ßªÇ7mlq:FFW6¢nx¬E민übbfŒ.y¥%w¼2òryeĽòÊ™ä•àníòÅ7µºþ·¿u”æÁsyÃðŒŒa¸Ü€‘7ç#KðÇSÑ0Ü ü2’ÍȶÔè֕Ϧ€;ü&‹Û„ ÃO|pBÃ0üþûïS¯€Ziôv¶“b?û¹˜_¹•ß/úGGÇ>þø“¨JNN΂5,x‹´®–¼"RJykÜ ÎbGû™¶¶¶à)DrvO,]ºdÉâŬ("¡ð;’Õ-¥„³='$Œ3hw‰¹Æ+™|ðácJ!‹/¹±³–+-ÈÈLWßÇ¥ËÆ>tÿr1 ‚W¢a8ŠU‚Z–©$F]õÉ£ƒqœõ°qüd£ý†ós<€ 5BQú©váÂ) ",Z¸@QÔØøøøø_ÔøÄ‹.réüð¥óʃ¾Q¥~zVÉå Þ(;ûO«ÿÓ׬^}LcõƒƒCƒƒCv{¹ÔÇîúÒ{öîÓ¡–ÿöo¿þÖßß ‹fžó‡=ØÚÖöÜóàÑ“Oíhyü±8©Å¡º¥Ž0kqˆ-D³7¢Çép¨rÒ»vov¶ÙíPJuT,ÍjkÆ)Ƶpä•–ÜòÊÈdy%˜A‡È+g€W¥Žz»Nï—””i.6¦ÍïƒÛf‘ãP‚ ˜es³I Þ¯‘&ŠY†ö&r_–r}>À=kƒÃ¤òŠÁŸEùñ²E¿_ÌÏÏ_ºtÉ'Ÿü5ºŸšš:sæLuu5,mÔÆU‰Ÿ#¨¶‰(‚ „"ë,¾òÊ»ü`m)¥ÓÃZR•WJFâÈ+ °°pLÛŽÍÚ—KÑPhŽ’Bb{â‰'°Re^—CCàuåzŽ¡Ù”ê©ê)Zi­ŠZQ*ª¯<4»ÌÌÌ‚‚üÀ'?¿  ß^^¶ ¦zAMõ‚êù ª«Ù(äçó¸xéö§ÏG}.ks¹mÎ wpÙm³ ^mï6íöòòüü<étGcãÀÀàää¤Ö)ãããP»h‘DÆy×à„)]\\ìp8Ž=¦/‡a$K*[¬Æ¤e ¥¥¥ÍÍM.—Ûë5D‡:ìr¹×­[;wú!³w:Þa¯ÁVRˆ×ë=tè0ãžÁeiiÉ¡C‡U Ñ¡Š±kß¾×F4Bs67¯/--÷`?Ã…#¯´ä.WF^É ¯Œ¸·C^9ó¼RJzÚµÞ»½œ%øè%²H&¼Ö\ˆ×àµfSøÄÃC¢ÑtŠËI›©Þ>Íð¤W]µ*¦»Ö.‰ Lgeeååå Ew­éééóçÏ䇯««}>HËØØ¸²œWp!„,]²XÑY=^ãRb‘ *¦ 1åí0×x%¥tllL Y–••+ž?†HiVrš<€7¶ü# >5Ù;QË2j^ùÑGê®óòòòóò¸Q}Ϋ¢gpi­qUÑà ž¨e™‚È2•&ìY‰, J~~žY–—•ÕTÏÁV”ÌÌÌ´´´Ì̬Ì̬ʊŠÚºÚªÊ }ódƒÈìåe^ïÈùóšxtjjª±¡ž?½¸¨H?vÓÙÎÎÚÚ…ÅEE'ž²²²†††ˆÔÒíö¸ÝîµkÖ€ÕȒɺukN‡*)Seg;wív:ñ'_I$¥¥¥¬•JKJ¢¶w¹Cá}´Z¯´´TG©S"×:£(¥ôW¿þV ßûîwâ=ÒÏpáÈ+-¹ ä•‘—1È+#îíW&¯ GNh…ñ-Gd9#; X%9%ªã,Y…ŒôôœÜÜèþ ÓÓÓçΨ%Ï)¥²ÈõÌññ±ÑÑ1äY2‹o^MRo^C^~ ¥ ƒ,©H³³³!ØÔ”RA ‘í’$|Ìw¶Ê¢H,’Õ¨[a*ß ±èW"²D±JY¦Ò„mYŠ¢H‚ÂN ¤eÓ P)›ßï—Ò"ŸæŠâg/I«?xœpÙ(J(M¸L2TRºÂ5>á“n<---+(µµ‹_yEeEÅU«V^µjåUW­b‰Õ«V@eeEðSYYY >ŸoŽ"Ë‚üÂÂAµ½Í&Â&0HK³qé´y󲻺{t¥ ¬=Ȳ¤xþüù§OëQË“§>ª­]TTX¨?ñ6[II±jéõŽ0jd ¨ni¸•˜Òeð¡D³Úv¹Ý¬õ¼^¯–Ò¥*u»ÝMrEKÕ?±ˆ¢¸ÿkZÄ3Î y%òJä•È+‘W&#¯,)ÎÝóÆÇªç -¾ò fÛžžNƒÂ§ÙJ&fX!¸ xaJudI 8+”æâpaÚúǵò„Ÿc¤(þëôô4Kôööõõö2WñŠÏÊ•+Œ\Y¤¡«‰HEJ©ßÏ]ÎïecÞµ))õûý~¿ŸRJE:íŸNK³åæähY„¤–óæeó·ì÷û Ä °Ÿ(Š~Ñÿá‡'¥W Õ‹¯dÍG¸'(RQÚo²¢B—Ú]‚WÒœ³¼’-,,誕+¤?5þYµRö•@oo___¿B5þƒ>œ¥ÝÍTBHYiéº5×¾wøˆVY|x²²²¢*H- !ÕÕó¿üå¿ù¯ÿú“N ~òÓŸï»ß©­]±® <üÃgžý±~6·Û³mû“›6¶Ä¯Õл¥AaÔ2–=.·›)]†‡èinnÒR´t¹Ü›¾öÚëZ?Å9V8òJä•È+‘W"¯LF^XlÔ•kÙ†KæG¼’ÌÔIý’ÿJQ´q.ÿæ²ÿJ-BEëé×Úƒ¢ù²”®HåÇå@4uãŠ#~ «8>Â*C–¡ô´Þ¼yuµµíÑ=ˆóçÏ÷ööÚív™ž/lj¦@ rÓuAhЗåW\ªpPI3ò¸ÿs7(鸠ÿJU^™øÒ”ˈÙÑ%Jµ,S`Â72Çk†ËUŠBC§|2žÖp"nÐ0œ¯/oožGãcãê® ‹ ˜âžú*•ð¬×)äåæVVU0úVUYYUUYUY¹úª«V¯}®^}RUUÉ Õ6 òó ˆ³7\š7oް׫cîñ´®Zͤ¥§@~~>ôôôêTctlìªU+îïcáZ–,QRRìp:FFFFFô"¡{½#.·{íÚ5RiªÙbùÛª[šj«uëÖðelÖâ.—»´´D²×j¯×+=}ŶA‹<žVOk«êµâéÈy%òJ3µE^5ŠB^‰¼2Ú÷Ë;:¥…,ËJKsræÜ<33“_,ñ~UÓ0— ÃûûZÛÚ+ìvã€ÏúƒIÃð¾¾þ¾>u¿R+W­4¿“S¹t˜¬lï&RÖßrrrF£ Õâ÷û³²²dqêåzyÒ ÕÙÙÅw9©×•••–——±ê ’Urpã ¡ÉðÐÌúÏy¥––å¼ìyóæÍÓò_©Ø%ñÑÃÕ£fí,ß¬êØ¢°ì7:§Éµ,Q¬Ô²D™¥BƒS…LÅGóˆœ[òȧ0”Ѐ&;¦ðhCY½šûã… lrŒéóMù¦¦boм¼\éoY„êùU@ˆ Ä€M¿°ní÷iýÚ×ßßÛ×WÅ9µ€¯íîÿíèSËW^ýé÷ß`Ûð¨jyÿmÚøx\¡Óé|åå·lÙfÁmÙºÝéplÞ¼qξžN§sóf§Ëår¹Ü:!tôeç®ÝÌZ\+ƒËív8A[yA¿óÄeXšÙ‘WZrÈ+#o¢WFÜÛ!¯Lj^¹þÖeZî,™@жI²q¾Ì?`xt¦¹%dòªÿ̸‘ûÐððÐðpüʷ˱’ìÓp­%EEEÅÅEªí&·–£¼%œ(Ó² e›öû³³³uLô¥¿ ¬¬,##¤@.Š!ޝ¿©EQ²É£¼0JI:‘ˆ"s;&AÌðþÎ y²9çx¥ö{òŸÊuÿ´_j4혡2m|­dÕ"òáwL¬W¢$^Y&‹ 7+²<òl2³YÞ/ï€R¤4èhÒ&ØBëIAv‘´´º5ïf%Mägj:¿“OH7h ªƒ)?…Qú{¢a§Ý†¶Z”9áÐV±4rD¡†I†xj˜üï,Kn^åxVVViqAľÑ×? ø•©ª¬àú ™_Xi 6™†<iŽDzš²(á‚zÔ9>Ýn//+Òjþ={÷?pß½üzˆò¯í7¿ýmW—&µlïèxã­·oýâ-áS¨êÂý± îØñŒÛãÑï [·=¹icKœ½¢º¥iq:,DÏλ\.wtJ—:Äs÷î=MëïÒ9WË*\+ÚxÜ‘b\ G^iÉ] ¯Œ /WFÜÛ!¯LIýJ©• Tø¯äÓ¢šÿÊ9èËFGGG£œ***ÌÈÈ`«ý Eð;ÇPZñ2Ö±ÃãçØÔ,Ü™–%`¼î q^iÙBõ+Qf…Ø(³R¨<¡œBe_¿óèýöUЍ~LA¤ Òªª*KÆùÊ »ô©ª¬¨ª¬X½jÅêU+æWV†>U• há›oº±¼¬L%íV1_·6‚'Ç7ß|«­½Ýx5ZZs46F̶uÛv—Ëï6aê–Æ×–­Û·lÙ†okssÓæÍ7oj±œîÚ½'ªçè°~@ŠëhgAä•f×òÈ+AkoeÅWšëÈ+£í$1õX””—ÑѱK—.ATª²áPUÝÄ8LŒôë,ç•ÆŸÆžy%Ê,D–(³T˜Z£HA”Ôêõ ¦ž„çáŽË‘¾Š`~ öãc*q=wþBjµñÒ%‹u~íëë?òþQÅÁ55_ÿÚÝúžôÒ+©K-€Ñ7ƒ™™º¥ËåÂWÖétnÞ¼ñ•—_Ô±õ6+n·;9£-y¥%w¼2Òöy¥½òJä•((3/££c.˜ÞtDæ•òlF`¢âD^inêŽmC^‰’Ì‚ÈevŠLY2 ó$˜j3j˜ ²¢bÖ4ryy¹>µ|ÿèÑîîÅÁ55ßþÖ=ú%¿ôÒ+ííÆkÒÒòXËã"fKµDuËX¤¹¹é•—_´DéÒíö¸f˜Z"¯D^i¦¶È+õ3)Wškä• ´QP’E‡†.\¼hf4É+! ˜ˆ¼R^‘Š~ÑÏœMÈEÒaÑ/†>ÔÏNò‹¢ä(ÕÜâÁ+ÑŠ…b{â‰'°’S!„É51ûðBE‘0—"¥Òq>8[9. ¤ aN A€R>mö›M`B›Íf³Ù"RZ¥m6!DJ Ÿdiî›e¶PZ=ϰwDµõ òó òh¯H) :ùB-oàQé‘Êñ³¯ýý ¿ééiééiÒ£Wÿ=VˆM`JØÇ–f“NOOO—:FzzËpÚåzçÀABHe…½Ânœš:§Õ6“““K—,&„¤§§I5))-€³g;uuttìóŸ»&TyAæSJKÇKJJÇá#GôŸÕ¡Ã‡³ü«oûuëÖ:ŽC‡ÉìõzwîÚít:JK1)@ii)kÀÒ’’X°cIIIccƒÂûcÂ~ 'úN§Ã"£È+‘Wš©-òʨQòJä•Ö¼_!P±çÍOTsQJÓÒÒ$|žž!±¶~f³ [Ki› Hø ---|%£H+IG'”¥´````W5I(çÎc;MJÅé ôt÷„¸0E QÅâââââbQý¢Ÿ¾”RÁfc +QHFÌ:þ+ùN¢È6Çx%={ö¬êÓÉÎÎÎÎÎf[ ipÔÒ`³qq¸t—–'% šc !1ñÊ>úHõ×¼¼<)0¬" BzF:·/¥3¸4£áí ³[Ô<“9ÊJè¡c ÌJaú’¢È™eT-CŸXÔ0©I5ÌÜÜ\î÷T•¡¡áwÞ=øég§àãOþÊÖ wÜv«Î)Ý==¿ûý†¿á†ë.\ sb[[Û³?~ÞTõŽÆ––Ç"fÛµk·Á 9± ª[ÆÞ€Lé²¹i}tJ—»£rgiÑ 4£…#¯´ä.WF"È+uºl´'"¯T}‰ã•8ù¢Ìg~-ÎTê0QnÜ­6Y ÿJ)_MëW‡V_¨_‰’ ‚Èe–Š„ åøP1y‚nÌ­ã S4ï S¹JsgyîüÅdnÑwÞ=øÎƒCÃÃÒ‘> è ÜyÇm:'v÷ôtu«D ÿ‡oËrjét8ŒPK·Û0j èÝÒ ‰%DÏLPKä•È+ÍÔy¥~#…òJsí¼RãŽp³2·d||ü¢Ž…x8¯ XËF}^©yÐeœŠÈ+Qf« ²D™"'‰áTT ¦\ ¬Uà ¡S°Ûí©Õ˜.·gçî½<¬d288Ä-++***ônê÷ø/ÕãF¨åþ×^7UÛ䤖¨niU3²=¦Àåž½û>üÌháÈ+-¹ ä•‘w#È+#îíW"¯DAIvñzG.\¸(G‘Ê‚ª úñÁõFUš‰¼’‘R¿(Š2ÕQ”> ×–¼ }ü"•>¼gK>_˜šê4apÝ„C(J\‘e¢…hˆÁlrŸ…‚ôáÅ&—4Ù'Mú¤§…>¼¤kI†•’.}Ò3´.¢Y‘àG×–jÅÚz—*_cQÃT†–E÷àýWªz±$ÿ£6ÏÆ·q@Ñ-ø/|'aG\nV…ß|ûÏìü¦»¾¤ßóýï¿$ǨœÜ|ÓMú'¾öÚëmmm’ÛV"o%-j¹±å1‡£12µL,DuËØeçÎ][¶l3ëÝÒÍõá8»h@^‰¼y%òJä•©Î+uâTP˜p‘/ü,ò…?4ƒ‡ ”1ËÑÃÉïbH¾¢À\º$¿ŒŒŒ\ºxñÒÅ‹Rÿ–‡}ñOO_ºtùòåéË—§/OOOOO_¾<==í¿|yš½ÓÓÓ~¿Ÿõô@ç…ë,1°/S¾Ýƒtîñʤ};W¢ ²DA±F|ÚQ_ „!¬[úh*]Z«†©{EWŸT’+tC‘œ :W¾ëKwèdëììR·SWWûý{¿§_gü¼ÇÓjªÎ‡cÓÆ–ˆÔÒ•pj‰ê–QËλ¾ß;wíŽ"ÇãIÌo† G^iÉ] ¯Œ¼A^ ‘šyeJóJÜ{£Ì-‘¡h½šQÀDÕwPuì¦jE^ixA`®œpuZÝéÞ`™8f¢ÄQY¢ÌN©ªªÔsa)‡˜úÓ*5L)?«a¸/K¸ty: óÊ+ôåžìí뀪ÊÊ«V­ÔÉù«_ÿ&jj¹ã™gÍRK0H-¯Ìˆê–ÆÅårmÙ²ÁÊ(N_¿þ®çŸ{ö®HZÀÖ,g¶pä•–ÜòÊȻ䕩©‘W"¯DAIÉÏËã£3Gœ@LÃDݱyel«CÓ¼2Rä•(I'ˆ,QRU|SSƒC:£§†ú¤: Ô%˜Ö¨aJÓoyy™Vµ/'%²€ëÖ­ÕùõƒN²Ä箹z~U•NΪ¯««½åæâ{÷í¢æF¨%lÙº=ÁLÕ-# S«Ü²u{j•,|ü‹/<×´þ®„Ty%òJ3µE^©ŸÁH!€¼Ò\û#¯Ô¸#ä•(sZæeg›ä•Šx;æy¥Š‹L•r.ðJk7Í+©y«¦[clfpÝ„c&JÜ‘å,…_LU¯„D ì#š®0µÄ¦ñEî™PéIÓ¬hÅŽL;×?0ØÚqæÔ'Ÿ¶uœÕj¼¼<æå0L;¨ÁHâqQÃŒr!L€°'Nˆ”‡€¶ûËàÓ'Š/Dû¿ä%pÐ^^f//תa__?;óóŸÿœÎ½tvv8ðžjwýâ-7×ÕÖêœëimÝñô³Q4à¦-d0ñÔPÝRÿ~]î(ÎjjZÿòK/llyœÇÁf]†Å™îY]8òJKîyeäÝòʈ{;䕳œWJ™Mº IDATK:¹›J>†Èà™k­, Ф¬õˆ ú²Lf ”Òé \¾|Yî¦u:ô ü~izú²ß?Í:~Ð_+óv)R‘ŠÌŸ+{@òö¢:D^y¶³Ó "r›Lã/ªÎ@‡¼‘å܈ñb‘CôÈ¢ôD…I• j|‰™RêIÿÀ û|xêãO}ÜÚÖ1084¥ã òóÀM†ZB-¦w‚Õ0AÔZqTòJžKÊydè£q\ @}8-}8Y¾l©N›ïÞ³[S]ýw_þ²NΞ=sVµâ>x}})j©Æ*D7%/µDuK-in^o<³Ãá`°²¹i}b«‰¼y%òJä•È+ç¯DA™M¼23#Ã&ÆñSX¸pÕ·J%h¸:éSWÆm9Icû=r@A‰Ÿ¤a $‰tu÷J£_Oo_,EÕÔT¥D`s!$0sBØ%‚h‹*fV–û ò_Y2Gñ«\Hød”““œØRRìöòåË–~ôñ'ZŽ?ÁT,««çÏŸ?¿§§G+çO~öóùç©þôƒ‡|îùÚÚÚµÎõ´¶îÙ³7 ׄ›7µ¸\î­Û¶ëgÛ²uûæM-N݈CñÍ›7º\®-[·ÉÌÔ-g¤ž‰ƒw×Ô´Þép8Ž™¨#òJä•fj‹¼R?ƒ‘By¥¹öG^©qGÈ+“W.¨ÿô4ßy8E)ôbCÅ"™Ú~ä1SWBŒ¼’2ý’ð*!¯Œm99¼ÇTë‘%JBe||b|bB庺{,¿D~~>ÓG¤€P@€Ò€Éx0MçUBø±›t Õ!˜ †æää(Š-(,J¡ÇYžø ¦¦fAM |åo¿¼ã=#îWú³{¿ûÕŸn»íÖçŸQçÜ={÷566ñP©§Ó±ic‹jÙÜ´¾¹¹)ÁÍËÔ-·lÙfÐuã–­ÛÇæÍgñˆÑÜ´^+êÔM‰Ö©LØr yeÔE^iªä•&+†¼Ò\÷F^‰bZ-¨€‹/JG˜¡1—¦ör0{DæYI>Ðñ%ôõ°Äððððð°ê)çÆoäܹsçÏŸO¶æ%‚MÐðÍN›ªN-•%ÓàŒå$@Ây%Ì=ýJÝ–— ý¨¼þTm§I=-‹V¨È+Q+ˆ,SJ,Mà –?Î[Ë ‚|¹tà§Hi6É…!(%üÕÞ~&8¢‰p¶³;‘´|ÙÍIµJ¸¶Æ(C“  ¥†©ðOlf¤!þÒåéÅÒJî¿2ôUîÛRñÐ5Ò!‚ôd¥‡Ë§#v†Ûoýâko¼©Uÿ£ÇŽ/\¸€¥¿òwûû?ü§VÎ3gÎvœ9S[[ðÃÉÝkC}ýzà9]jùÔŽ§{ìQGc¼¨åÎ]»].÷ŒÐ@T·ä¥¹¹)Y67­w:ì–Uׯ ä•È+ÍÔyeÔ( y%òJkÞ/S¼2GÐ~e¨<2 å5Ðæš¥äɤøý~˜žöKGDѯ@–ªÏž(6Mü’Y£ûñ¥´8 F04884¤áóÚk׿‘â¢"ŘÃÕ¤;*Šb°&üÍúý~¿?”íòåËSSS¬}¢A¨HE*J­J‡-«B_)Èô\ƒöŸêQÅ RJ¥ …t¢T°¤Y)aMä•\‰ œBÍÍYŠ ZïX!$|Œ2'‘e2Š‹¤rH¦I¦x̤•ø ƒÏÆýµMù´M^/ÃÃÞÁáan£žÖö$iºË– „P œCÓ!´80¦† `¥¦”¯´´Tõï·—/OPúd }å<]*\7ðkã„Ç”6[ddi³ÙÇ+++*ìöþÕûìééééé­©©€ j,¨éììÒj”Ÿüôçÿë_þø ¦êëøƒüÜ :múÔSO?þ؆+®0ê RK—Û½e˶¡–¨nÉ ¯h9#Ú¯ _Ž!¯Œú¢È+M•ƒ¼ÒdÅWšëÞ³’WšXŒô½Y+4ZV†äx0ç÷ûEÑ/0UŒ¥Ü@qiZÜ'Š“‹–ׄó5 dÉç)¥ÒÕE…|(›¼2èÙŸ×j42ìË@£BûO÷t Ԩ̀ú•qxi¬¢èÿÈ+Qâ ˆ,QTä“¿~˜Aý" {“¿Ú+V,+,(  9ÊࣦGK…& )üÅA “J*ž©Ü=V®X¾ÿõ7´~ýÝïÿÐòø–¾ç›ßøÕ¯£C-_}õ§÷Ýw¯êO wÞqû¾ý¯éÔd÷ž½Q KHj ¨nÉ=,§ËÑܼ>in y%òJ3µE^5ŠB^‰¼Òš÷+¼%n4jbb"v^K=U9¬¾±Kø¯¼: ÌUÿ••·¸Æš!ŠŽÃ'J|‘å\”Þ¾¾žž>iÜéïÐRšK ),,(*,\´hapô%@’ ±:%AˆI$Ãp ¼å¯†ÉÙ#ÄC Sb–©,ö•+–Ÿ<õ‘V†Ã‡¬Ys-K_wݺ_ÿúßµrvœ9óöŸÿrëoQýõÎ;ïð´¶¶¶¶iîñx¶mrcËãQÜ£–»víÖ×dœYj‰ê–¬6oN‹¼y¥™Ú"¯ÔÏ`¤@^i®ý‘WjÜòJ”„¬âÆ+§¹xDQ‰±žj@ίäÑ$ŸAÝß%òJµ' ‡ÆTs 5Â+ ±¸3ãð‰7Ad9K$ÜH¼»»§»»GÒü?vüÄ,»åººE@¡¶v§ÕÔ}$Œ4JÞ, ðÔ"˜3 † @ىƧ)Þ%o λ€m/ ‹oUÿ•¦|Y2¹æêÕzÈòÈû555 Ô„Ô.\ø…ëÖ8øžVæ7ß|«¾®®¾¾NyS„À£<üô3?nmmÕ:ÝíV§–ÄÀLìt:®¸bcD 8³ÔPÝ2™v"3\8òJKîyeä òʈ{?䕳šWx¿¨±<³ÆeÑ-¿ó‹"È ÃEÑ/}¥”ò¾,õwCL122hÙ›[ÞJfÅr^sOPš“GÍ+µò98 xeô†\äX·D%3·@F™ó‚È2YD6³jŒ5Z.;;»Îvv@WWwwbp'‰Q õuµ‰)0@²¨s4¨ÛHP ©TR©&¤йŒV 3ø|¢QÃ$)œTjù¯TdÓæŒ2g”ªþ+Aà}VLÿ·æ¦?îÜ¥u‡©]´]âÆ®—º¨ª¼ñæ[ õZ7¸áчw<ý¬>µÜµ{OÓú»tV«:²y³!j9³Õ-“a¿6Ã…#¯´ä.WFÞf#¯Œ¸÷C^9×y%Ša¶0Ÿ’’óJø²”Âïh‚`A#ƒ‰µSÊðJjÁ=ê{¨TµßêJ»,ä•©½þÅ!%΂È2…¥³³ë½C‡»ººgåÝ56Ö³A°¸¸¨(Âbd–Ö’ñ7J8ê(S‘¤RØTà ‘F£j˜Cñ UÄó1®†Йœ«áù󫪪*{{ûTíêêîììb ¾ý­{þåÿùÿ´Šjooþ…—zð~­ _ºóާŸyV§2»wïq8Gt÷b„ZÀ–­ÛgV{Õ-“x½çÂg/¯œœôMú¦¤£½}qlvkÆ^¿&Škì8Ó¥ókaA~aa¾5wGc)dÆ›—FìÈ‘/ÖÕ+í…Ò÷ªŠBÝ‹"¯ŒÔ¦È+Q’p•Ÿž3>>žä¼2üU3Â+Ñ¥15íTXÿâ‰Ad™ªÒÙÙõï¿ýÝ,¸§£QóÀò‚›‚2¤8ÉcEv˜iPò43f5ÌôŒ] “Ë#d<»Ï]}µŽ¢åo~ûÿóüwéëw¿óíŸþìZ™ÛÛÛÛÚÚTmllذá‘;žÑ©ÌöíOmlyÜéœåÔÕ-“r½çÂg¯œœœœôM)`eÜ›}nóÊÄáBä•jÔ¯o`L:ÄÒ¹9™y¹Yy¹Yy¹™‘Zy%òJ”ä^%DÑs œ222réÒåX*f!¯S¹Yc¼2¼­T[ŒòZ!am‹¼2׿8d¢$DY¦ª_Œ¼2üx‹xªõÏËË›œT™¦ÇÆF™j$ 8ž쌂%„=} (~D)ç¯rÍbRýÅLÃÓ)„(‰D–3)FüWjM„]]Iu/K–\É‹‹_ÁéHRn°çFyÊc@ÊôiH‘¡ÆÀÁÁËà%HPR '˜æ/g™f0/DPÃôù&™gJàæxõÇ-_YIN' !„ÜñP>ÞÑdš-ô^§§‡ÒZþ+¾,322¸ÓÕÓ6›²níZdùîƒuuµ‹-[ZÚ}ß¿÷üã?éô¢ç_xñ‘‡ºYy³8Ž»¾tçž½ûtJص{·ÓÙbü½‹&µDuËÄl¾f¸ðYÁ+'&|“¾© Å7fG^™˜s‘WF몵o`\b—Uœ 9òʤã•(æ†ÞhƒØˆJE<‘Ôa‘-5bQj8yÞ¼¢†6nÁ¬±V´üehˆ4Ê„ú)W¬´¿‹/ŒŒŒÆØø’óz©Ñ¿²¦rÇ÷¾ÌËË÷ ä~ó‰ÔÂl«BBZ#²Dˆ©É o$.†Ïe^ jÈ’eÉÊʲj‘Í‹ˆ¼%™‘%Š!Y¶l)ÿµ¼¼¬¼¬Lš.€³áVFˆ“æÉÀo’ö¤4™±£ÁXßA’¨ß2šÉN6­†Ôq .\gF sjjJÀXBf‰ž%ÀWÿîï~÷‡?hýú—¿¼ûï,’¾Þ{ïw_}õ§Z™[[Û<­­ ZîºëKnÇãÑ Å³uÛöM[¢¾§Ó¹yS‹jÙÜ´¾¹¹ifÕ-ãºÛšáÂSŸWNLúz{8ëo䕉ïÈ+äŸI^ɧûÆûÆss2«ì…2ƒq‘WjÜòJ”¤]XDè`.\Šåá¸Õr?›4¬d•K„éÛñ_©à›0»íÁÕQ £Èy%ÊD–(öŠŠ àG®X±LæT’ªNN!ÈG &B¹¡’G¢dÇM%mÄ &!ÁI(@0£VÔBÐO0 :5L dF©†I À”oоÃÐúr$#==å:OMMuMuuW·z¨3gÏž9s†)Z@]míÍ7ßôöÛÖ*íÙgŸ{øáèPËÇÛðÔSO»=­ £–;wív¹Ü3®·ˆê–ñÚVÌlá)Î+Ã`% ¯œ‰ˆ¼ÒHþdá•’ø¦.ºÛrs2«ìy¹YZ'"¯Ô¸#ä•(I»²ˆÌ+b¹BÐjJk ˆI:Ú;jëj%-×Xx¥š„¹Ä+Ç'ÆcZ³ ¯D™‚È2%ålggøÁæ¿irv¹\šØbÕª•Rº²²¢²²2lTåÍ.‚Ó‘düL9ÕBÞìYÒ¤r¦ =âƒxO6QÅO @©†I¤»3«†)ýÁ€àzj˜CqN “ø&'}S>.¡!驨!×®]óÛÿÐ õ³Ÿÿò‰ýOéë-7ßtæÌ™öö­üû÷¿ÖøpƒÎåZZÛ¾ý)}j¹k÷žæ¦õQß‘Ajér»·lÙ– øÕ-ShËbE–¤æ•§]maquWÆõyY^òÊH™5¯+¯”Ä7uÑÝ>Xi/¨²èìuïy%òJ”ä™üÁ+HN?!*^™——Û×§saø/ü}R‹·ÃE^,NLLhu!÷{ w_‘WBtÀ’·îûJ ň ²Œ»(ç™Lj ¨n™Ü[<+²$/¯TS®4 qæådÍËÉÖÞñJQÝ`dH=ÎfQiü-ô/ÕØ„/ë©ÆÖˆ†ñ­ Õ4üև굕|PýæNϵÀeŸ_¹ÏÌÌÊÊ,+)*+-T©pØvQ± ËLÕŒ†µ±"ˆ.UyŒ ©F=©ÆéTöQ­ü#S*q{sÒHnº q›²F¼ÞEf'#¯L^‰2»'ró%"<šš:×[TU‰Wư@‰|c££c ÉUgEMûq­ƒaÑxÂÞßYÄ+»º»¢ëT”F´W¢Ì2Ad9;%======FpÉK¸&7.Uà MHÒÄ$Úžj˜ SÃìïéëíí 4nª‚ ÎTs«œ¢†áLîùæ7~õëßhýúæ[oß_w¯ôµ¾¾¾¾®®­½]+ÿާŸùñ³Oë\.qÔr³3¢êbRQKT·LÎmŽY’”WNLú\îöè ,+/€ÒòbŽèÉö ö!M>%eÒ&Å;8ØP O•æq“T`û¡Œ ÷€"ÛÛð×âÍë€ÊQ”lSBù+„Ý>OPiØ© ö¨HÃ#(.Aù º„»7Žê2òl¨ ²ÌÊ,,È/+)ª²—hp@–¹¢¼å¨rZ„ˆ WÿT)¨ì]©Ê”ò@Þ8¡<¢Ús án¹•ÍI•ÅòÕwÉŸ›ÎpµÏKSë/Še»n{`¶§¾i`ðsð¢éwÍ7uÑÕ6Pe/ÈÍÉD^‰¼e6I§š¿/ã’M©ÛEöTUUööª‡ŒŽ^ a¼R¦"G“0'õ+™U¸ê8ª{‡RsOÓšå_TÚÒøj£X%6Á,–ôôôüü¼¼¼\Þ:Ø!AAl‚` ücØ"àq"A ‚M° ‚@Ø‚ûŒÁJ>ASpŸÏçv{úúú 3+«¼¢RÕ*ó2Î+¥ùlîñÊqæÈRU˜·Ps+¹xðÊxøÄDA‰JYÎ9ôÞa×i©å¹sçt23p™0¥ËpI”fˆ`Ó²“%‚fOOϱ÷ß?ýég““¡;b¬€ø&']n·Ëí™ôù  °È^Q¥Á+çÍš®ø½ï~GçW…¢%ÜyÇ:ù=žÖ={öF¼hÓúõúvíÚír¹c¿;ƒJ‹ÉF-Õ--È+!''{érg¹½„rœ y¥¬Ú<,‰?¯¤jA´Õ ¯„óJ©Ú,mÖtŽ@#½£Ô7u1 k‰¼y%JòÌý18Ÿ1H-‹‹‹x³nSu3觺ºZë§ÑÑQF-õy¥ŒùQ½†šk¼’tukjeffÊ7̊ݳ¤#™û‘0ßY„û(·ß샼%µ}Y&£0gQœèr¹‡‡½ë®[{þüù®îžšêùóæé0ææ2;;ûüùó—/OÇÏÓ¥‘[V$üaJ!&o˜G•2o˜A×”æ¼aNŒ÷tus5'Šý*!ÐÓÓÛÓÛ˜²² ‹Ta%“ü¼\ÙŒÂ7MÈezäî/yŸ2Š´Â%MxZq ïã’w©_T}}]mmmGG‡ê¶···µµKš•„GcÃwܾoÿkZ³gï>§Ó©¯Gét:6n||Û¶'uò°âW\«£I§Ó¹ySKDcó-[·77­onnJž±½["¯L¯¬­«ÉÉ‚7È+g”WR•4òʤã•ìxŽ j³§/ ƒ—lúï£oêb_ÿx¥=y%òÊY¶Î c`•kSñeÉ»V¹ÏJýÈ|Y Dî{‘H˜GÚeddhE.ÕYÉk½,|8o¿È¥§ý¡´ß/Š"def¥¥¥·¶¶j•]^^îëߨº‰äoœýž™Éù²L )lfÛª¤¤tÒ7ZáÁLŸ9s¦¢Â.Û8ñ ìG¨´‘|YjðJîûæ ¯ÔW±ÌÊúÿÙ{÷09Žúî÷WÝsÙÛì}W»«‹-[šñ#°lxó¿`ï.I°ËØñ ä–´ç„8yIHBtqrxOxžœ¼8y¹í®0ã¶l 1ø"Ïȶdiµ+iµ’ö"ioÓuþ虞ꞮêêÛ\ß§»·§»ººº»ºê£ªï¯ÁZ‡íúŠQ)ÜM æâå¹ÿƒ´‡:ùW"¯DU˜p”e}ž ±ÚîüßÞõN^hæ™™™Ÿþäi}’øÑ‰c‚Iâ¬[[ú„ñÀËû,Ãÿƒ/  IDAT0õ€haÜœ˜xõå—¼üòäÄDn ea\%É ¯$äØääs?{^ç•úLpÞàÊ\³ ³ÈH1/„ lÎJµ]‚•é|EÛÿûÍ¿.¸¹ÿßß©P®„Bn½å}›7m2¾oŸø¥€T2¹s‡Ã(ÂÝ{‚ü¨SKÇÝFÇÆ+p†5·D^*¯lnnD^Yi¼€Ê=È+ËÌ+µÞhvcÊãË;ur~~a y%òJTͨ¥¹¹··WžWºûöKüÛÚÚx?ž9=3sÚÊæìj§øàöŒ·†y%LLˆúæíüb·»›4„'y% ‘%* ݵíÃjÉNOgŠ'‰Û²Ë2ÎL‘¦¢’Á´ºa2†–7Ìü®ù¹¹W^~yÿÓOOLLÌ͛瀃ï ÇŽ=ûìsÇŽ˵-œ`¥Î+ãžæwT².¹dãÍBjY<¦òSŸüÁþ™ÌÁ={t::È5JRËt&SÔÝ-CS-óʹù)^yéä••É+¹“s0y%T¯Ô·7«tcÃJ³¢‰_Þƒ‡N™©%òJ7‰#¯DUžÖôö67[ î×­[[2^ ¼n¦.c(•â• ØXW¼R<IJ­ì¼’"¯DU¢YV±îüð‚ÏI:± ·”—Àêч^V¾Ô%†©Ú ÃÔ!¤Ã0LÅ4 s~nnâèÑg~úÓW^~y~~Ž¥¦LŒÜ8Á‰‰cÏìvb"+ÛÚ;6\|‰VÆbњ䕺Ä-¿÷½ï,šöâH-e¦3 &…$.@†XÕÔp¸eðªq^™Î¼!ÞGç•&xƒ¼²*x¥ß ¯äsðàkü'F“œÅ!àj–W¥2C,I>"1Ï´ÌÈ4uŠÁäri¿ç Nµ}ÀO å^êŸþéŸb)Tkç˜Òk®¾êÈ‘£³³öÿ\sáÂ…£G' »»{uuunn<|ð ËËÆÆFˆF#et½äÉnu~ª6!ùØÝ …@Þµ8%§`]¬rôÍ#GŽyí`fúäI¦lm¿dnnîàk¯¿öúëÆ?š bì°J´4u¶·E"‘|>ãc£(¬¿²)®9HUUÅÈŠ‘Ž`]UU5¢êe£Oâ.^' ‰D#öëLRlNTEQTµx;!dó¦MÏ?ÿs~sçô ïxG®( Bº»»|íôéÓ¼CžyfÿÐà­–û^üçM7nM§3333¼tfffÒéÌM7ÝèÿÁëîîN¥’3§f§ öŒÁª»»{xxH\\¬ž~ú™Ê¼qUi´5M3Ö¿ÿƒ³Ý?•J¦RIç©a^ ‡]^^A^Y­¼’ÒÙÙstuÁzxC<ÞÐO´4%šWV¯4öíˆd4²B ï=]^ÉΟ[êêhB^¯Ü÷¯ÚþÖÙÑÑÙÑ|ÛÄH$b8,©ìºª²®‹¬ »ÎÚ,Z9Rvuå–Æx5êõ×ôô©ééS®é_³4-k4†óh%Çp c½›W,3|‡¢Ñha=µx±VJÆ$ïÉ~âäÉ'NÚ¢¢kßz­ÅŒI±º3±1=‹~ÈŸBQ`í¨"wø®ÎÎsçÎE£Ñ‹.Ú ÿ¤išqP&)&YE59E©ª>íLQ#j$1.?Æ”I$”U4b¬··µO;f´ð Q &¦(äÌ™3]]ÍÍMú%¶ßd¼ˆì[IðÊ£ÓÓÓ^ÙØØ¨w¬ }7EQ5¿®*ªbÛáŠF¢…®_ÔÔõËß”œ‘X®oix™\´(L‚…gzhRþêÅí»À‰Dk"a<çŠR¨£±(ËŒõ³®0u)ûÊèÅâ¶Â¬Øº…Ȳv%\sõU6¬ñÅ—¬äÔ©™¦¦Æ¦¦¦ .èæ#žÿ¥ŽÅ—‘HDU•Ê$˜ÅŒèj !:Á4|(ÌΞ=yòÄÑ#‡fÒ³³³KKK9–jF=^¨×fgçtˆé})zñ|E Ŧ׾úÈʳ³gß|óÈììl®©ê ÜTM·4%Zšëçáü÷¾çï¿ôï×ÿ÷‹ÿã|ñï,ïÿô§üÛ¿ã#˃étF†(  îÞ# ê­s·@¨%ŒŒìpŒÄ­Ï­Ù¹=¨“( &îŠÖ6¯œ›_˜œ:á–W6ÇÛñɨ(]»Ë štþ…o¼Ro¡lŒ-ZŽ×TÞK}ðЩ-W­E^éŸWâlG”Ô—’RÿI¸opÐRfò¢ fgg.E§OŸÞ¿ÿ¹­[o_)ëÓ¿Rÿé•W^”³«¨;A?bŽÄ]7))V¡¨@…^–5¢ Öÿ?ÿ÷Žõë× öÑÁ¥ap93s:9xjfƕǥXyé&˜¬¦¾TB8òÇ>tèG?üáSO>ùæáÃo>œŸ;nÌ'W˜×-Ì1' yóÈ›ÿùãÿêW/êo™hà†-M}=uÅ+`Ó¥—þÆ{ß#Øá»O|ϲ%™Ü|ë­·Ù³÷ÁtÚ«¥RÎÄwíÞ 9ãÈÈ™€6Áž4X¡»¥‡Ö`»T.¯€ÉI×¼;ü(T U 5ÐÜ]¿Ô™CÓv; ¯d7#¯Dñ‚Ö¯´Éô1EÿÞuúôéÂ#»ÓÙaÇúò¯€—…¼:::˜ÛÁx™³a¥1¼X‚ù%÷#AËfÓ¦Mßÿþx…sðàkÉüôpÈ€M&7Ìœá›ZÎÌÌÜxãV»{hú"www¥’ɧŸÍw~úégœã,3C<ð“«Ús· sbxíóJ»)á¦}R—_Ê6Œùà±H# P(¯Z9~ÀÂ+saÄ•ìÙÂ@K«òÓÃU—Dy¥õêöý‡ý¿ÆáÄp±‚ñ²ì[ÙlÖRù°—–&h¡EMëy¤‹F t§¸ÕšŸjNóÍþãÇO?~Ü~bøuoÕVø"L²l«˜m6+Š@]ôëÏ& ¡”‡³ë@€“}¸ª(ªªF –PfÏX4¦*9ÏKvÖ¼þÜ666ÎÍÍ-/-®LfYÇ&§ºº:› ””K?™W¬^æƒë¼R%Öôö²}75¢Út¸XO0ÅÚwcÖUÕ —ªª˜¢#O}¬äæòëâ‡WâÄp"Ëj• Ú—ŸÔŒŠ²­­íÆ­7=:!® pyáüy#8nsi©#ÂVT¨F÷ŠÇã¶IM;vÊe› 9sö쫯¾úꫯž=›ƒ•ºaeKKBVv¶·µ¶¶Ø¶{Lþ•*ëµlþVÙy-[½,åe$ïøÃóµ,ö¯ä!K{ÿJ³—¥‘ÃM›.}qùãéÓ7n½ÁR8]]]ÏìßÏG–§u´$Ž §SKÈ';Ïœš ¸Õµ„Úr· YÖ>¯€¢(á¦}6^²>‹ÚúW"²D¡ühùø+żbD€óT@-W qxWz╈,+YZ¼,ÍGebQÚ#KÖ¿2Æ´l¦ÑKÌAt(¥ú±SÇ?nïeyÝu[„¸ÒjXiùÓìeÉÆÏ)´óM˜’Ê`Å”lø–;Ŋ䙥Î/ L©*ªÞò×-2#jÄ‚,Eéïï;rä¨R†Ô¸%ÇŽMvuv655³„6ÿ¬Ú¾ky¥Q{´µ·)¤ðâ³…þPäœËø^–ì!ìºᙼ,¥°ñÌ+Y¢N ¯MÝñÁÛ?øÁÛd@C:ýÎXúÕ´1[üèıÀ'ŒW£:ôä“O¾ð ÆÌ¶öŽ _ÒÖÞ!sx<íéêèéîŒÇcø@nÞ´iÓ¦M¼_<˜É´lL&7oßþiAšcãûd¦‡ÀðРx¾v:“ÙµkO€×+9½º’gˆCÞÝRfª»q9Áce«.xå1ë”pÓ>ÍÍÍ-Müx;(Êgcÿ W]n"YÞa ç–æÏ-I=ä•T¾(P¨²<4ü³:Næ½æš«Åm†gŸ{Žý×z;†XóÁyåš5½åxæp>8ªš„Ȳfµaýú‘÷oÝú™ÓéÌÓ?}Fg—úƒ]9:qJn~h…k~n^f·3gÎè°òðáùÏICCo_¿<¬lM´ ¬,Ö-ïûMÁ¯¶ñvRÉd2¹YpÔž½JžÝÑe2pj™J¥$©åèèX%ß8t·ôØÔ«~^ æ¨;Ö}6^ºÁ1>8 …ò[Ϙy¥þg¯º,8dêäòJóîÈ+QU[T¯€¶¶¶ÖÖVñ‹fPK^I‘Wæ$uy% …Ãkñ{Æ®¯[·víÚ¹¹ù99`§»Ô'Œ@SSÓêêê… ô ãpþÂ…¦¦¦j,¢cÇÄ_ŽC‡>|øðáÃì°Ê¶öIÃJhM´ôvw6ÄcjD•Ùß4d])¬³³ ·(ÒÏ›ÀÎ0²ÌXäØÂ[× €lsÅ^›¬>Aûµ×^H2¹Ù2ŒÿÆ­[ÇÇ÷ I§37Ù™Zµã|mý§cywwwËÌ­Ng2îYíî–AO ¯^ylòÄüÂ9Þ>/Y‹E¼2©Ê/ U!Z™zÅÜ 7uòcP<=¼ð’.¯d[šcñX¤ˆ ¯´MÇ&AœîMÁN 75†‰Å‚H÷µTY·>Ö‰·Îº®[¼ã‰áǧ¦¦Ž/Š}„Àu×m±0nö·ü Æÿ)s-@¡hµ±žsÃdg['†3ÛuS)ÝŒ275\_r“ÄUU"‘H45Ì  (BH4R˜8¯FTvV{D)÷õõÍÎÎ...Z@öcÇŽuuvê± D¼Ò®ª©7^Ù·fMcc£á¾Åºr)yßI}J¿qk"Œ©W$¢^¨ÑHÔpN5y±&°DQò ²õ ñÃ+qb8*(E°ªH–—JòŸÝÖ¯[·þ¶uG'&öïnbâ˜$¸œ™™t®÷©ËR7ÔÿÛØØØÔÔÝ]]U]ªgΜ9{ö¬1¦â ’c*sp³5Ñ–há5›-v¦aZXW™úš%žlU®¢Š×€…ªì:[Ý«ªjÛtÖ}- ‡pÖ-MjÛ/„¤²_E¼õ–'Šâƒڷﻩ¤ *Ú¾ýÓ{÷þ-ï¨L&“NglSñGhxxp×û¨2H-õ±–â“B~ŒçÈÈŽJ~SFFv¤ÓiÇk1®èž{ïÙ¹=À¬þY'¼’R˜Ÿ_àíÓÜÜØÜÒˆã+Q¨ë{^™h¹4­Åx/é¹åDsy%ޝ¬Þþ%X|yÿR.Øn»!Ähµê^ó¶gWÅáeͽ;ÄÚ_£ÄŠõò¨ˆRPMc ئ8{¸~EÆ;KŒ³3壣L}]£Z$16Yè¢ä3 iPí-×¾å?ŸúOfYsNyö¹ç:;;ßþ¶·½b€!µ=¾rvnî§øàÇ„Õc!½Ôp8¾U~áÄðzÑúuë>ð;¿õ;¿ýþµk\˜Ngt¿K}Ú¸1sÜz™ÎÔ—S33úR-erèСÿú¯ÿzá… ^ÙÖÞÑÛ׿¦o@Ö°2kkMlX7ÐÖšÀgLFŸúÔÿ%øu|ŸÍ€JÇéá»÷ì•<»ÌdíÀý%%gˆ>3= Õ½»e}ñÊüK›}z×t‰y%6aQ(ßõˆWê/Y²Ä{I§NÎ#¯ôÁ+±C•ºùÀ¯$Ï@ÃΕÑ®yË5Ž{ž>}ú¹çž;}út½ùW˜ä•kz{‹:ØÛ‰¼U½Â‰áUÝš¥2ëLÍK[[W\~Ù;Þñ6Jé±ÉI·gÔG_êSŸ<>sjƈ|!/e²sÉõJ‹Ü"cbø™3gŽ?®ÇÕY\\€xCCKKbMß@CC£äð†x¬»«£­5ÑÀÌâ‰7ÊÒô/ÀÌdð¢ønã¹³›®˜'†™‰áEÛÕÕuðàÁÓ§Os°ÓÉäæâq»7nÝšÉdŒˆöŲÎ îÃ+ ™d™–6J4Ç‹ûãŽ]Xä•°ï?ìãûáÄp±™>P41ÜR&ìÈA¶ÄØ^[ªìvKK•mÁÒü''§&§¦lÏ~Ý–-~°¨¿Æ:#'lÆR&¦uóÓn;Ê’bô5(Pvvot*¥”ä¢4×|jhhhmk;yâdáŦÌ ¿Ë ŒæææYš93súüù +«+Ód—À3¼ràÕW_å¹U644J¦ÓÞ–è[ÓÓÒÒ‰D$ÿÁ ‘%{±]Ï>û¯¬žyfÿÐà­6Ô¯«û™gö ¸äM ?B:;KgDÑÆgNÍ‹»»»Í4«ˆZV—»eȲ¾x%¤¾ÁÛgíú¾X,ê8¾‘% åYŠx¥±vžr]žº:šWzà•ˆ,Y…,­xKY² Idic.ƒ,UˆÎ®@U”ü*UaM8UU÷ûD¢&664´¶µžÈSK[d©ëô™3¯½þz\Ö(¯|ùå—'&&–––Ÿ‡†x¼¿¯/g4©ª$ïF©FT¢}a}-uS/ÃW´àe©¨ª¢æïDÿJUe¼,U•…5V•@–úƒ>y%"KT€B/˺ÖÛÞvÝÛà:Ø¿ÿ¹_ü× >SÓ©%@ÚÔù/4»»,#­t”Éa7§ƒ½Øï|ç;…Æ¥{·Ê††xcC¼£½Ílö,Ò\Ëó*MÓ¿pšþ}Œ‰ŸÃúWª\/K¶ŠÏÙÓäZl\/KƒfŠšÑf+L¶IÍõ²4Ųᰗ¥Rƒ·Þ2¾ï»¼¢ß§SK¶¬.»,544866Î=jl<•J^&眨ã6µ É\rddÇ®]{Ä´´Š\ ëÆÝ²îxåüü‚ Áæ–&œŽB•¨æóJJ{ÈÒ4ØÏöX8¿Œ¼’“ί±l«í#1Ú™¦¦&oØš¹iʶZÙ¦¦f¢(qÌ,Ó  @ˆ)ï„ËMjäJÑÔÜ3¬dU•éw°¥·ö©G³-+…ä/œRjv«gËL¥®)Š¢§­M#FNh{Gû[®½FgRŽÐçµ×_íõ×7]zé¦K/e«šà•G˜˜íKÆã}}}A'œj-ŠñÁQU/D–(€ë¶\{Ý–k'§ŽOM÷Ï.Mœ"ÍЙ"‡@Û¨)ÅdÓ¿–òÀÝ’Jhhˆw´·6 =’Q®48x«YŽïK&7& ¦Ó™ Ÿ÷Ž_6"‹Ãéa©%ìÚ½·*èžîn)sEÆu¥’É 4äªÝ&·K5ñJ˜_à½ïYÓ%Ã+)F±@¡¨~x¥¾Þ«çíÛótêä|oÂúâ#¯tNk0TIÚ~kê½Vñ½{[[ÛÕW_}äÈÇÐØº py饗@•óÊٹ٣G'$/\W<_³fMÐOòJT]‘%ª þ¾þ¾k®¾^øå¯Ž?qœq* C& YØ*{xWWWw·T¼òÕÕÕÞ¾~ùÙߺ:ÛÛ:;ÚÙɨ ´ýþOí}ðïx¿ŽïKm·ÁÙ#;·ßû{ÿ'÷qr e¨åèèØððP°×^cÔjy¸e=òJ:9u‚w|SSƒ ¯Ä:…òO#Ì+ö¼€6“U»¹á8¾Ò6䕨Ši?ø­!Bç•æÈç6jkk»üòË_yå•ùùyÉ$upÙÑÑÑÙ¡«³¸ªX^9;7;77711á¶ {{z‚û"ÑТÒô?… \ˆ,Qöºö-×À[`euõĉ“'NžWBØx8&W.ÆŒ5ûb}ÀTUU#°ˆj@)?û_?¡ÉšLÚ;R_‰ªT!²¬·¦¯½³à€5½=kz{àª+³ÙìÔ‰º…ö+¯¼Z“åÓÙÑÞÕÙ^ü5"ÌŸgt16ئ‡Êñ¯ŒšØ ›Këw‹õ²ä¸Œ³Ÿ=öÞ:¨xöÓhq*|-áw8ñït ݺwïßò mÏÞþòÿ,ÞžJ%“ɤ`zø®Ý{yø!É[“J¥FvnS˸¡äÈÄ*¢–PSÃ-ë—W ²õôv"¯D¡JÙh³¼dżR_3Ï §®òÊ’’&”7qâXíåpƒ }z¸|6R©ÔðРxŸÑÑñ0J@’ZŽŽW×idd‡Ìu÷ëž{ï  ‡Öh º RñóÁõ}zÖt"¯D¡JZ×HðJ¾z ç–‘WJ$ˆ¼åûu­c^ih`` •L%Zå/Ûàï¯ìžm­­mØÐ‚دôâ_ì#5(*p!²D¬M—^b,7¿û¿Ë[®¹ê¢‹Öç– ë6¬_×ÖÚZ®L656nX·öòä¦î®¼eåÕöíŸüÊh™J%‡„Ñí¸Èáá!ñÀ@=¶O% I-ÃË@Hª‰á–Þ:ÕÍ+9±wܯÄ+ @]#Í+{ÈþËŽ¼Ò1Aä•(߯k x% 4W„Y˜jÁn³q„>Ïœ(ºÉe^„‘¢(zLžË/»¼¥¥¥leüý•Ú­¡¡¡oÍšŽŽ¢(ú¢RXS¹)„YTf1IµßŸØrö¦-²Ÿ¯;øJQ(G¡—%Êk=DÝ5ŽÛÛZÛÛrŒR£”j¬_«ï¯iZ¡ßK)ÌÍÏÏÍ-°IñÖ‹NÈš(SÛÌvó§±3•;-ú°»“ÉOÜY=*ªi»ñ“ªªÆ:»Ýr¸ÊK–9D1›QšrÅÉ¡Å!¨àøcå%%ó±L%“ÉäæL†‡'“Nglãð  ¦Ó©åèèøÈˆ “D™â®"’ËKÆR3Ô „§Rº[êÏC:“Éd>ô?ÿ>$†p¤JÆW@Ss#òJÇšMUÕH$ÒØØ˜H$Ö¬YsÅWÜvÛmƒƒƒ[YYôÆo|êSŸsÌ-™êiq1=FÇñ•Ô]Wy¥í:¾ÕWË55-V˜:oe{ IDAT~*dÛc)¥Â4ÙBF—§à, QŒ'‘hëk©PÆVži–g#šCŸŒÒ¢f9“'E-êæÐ¢.ÏjáE¡lÏF¿J)!`×…ikokjjšŸŸŸœš\XXðÑõ¬^ÙÑÞÞÐÐÐØØh)k“wªB¸}76¨€9®Žq§L}7E!¦>š@ jÉé >àô¥¬ P(BdYgŸö zM>ë$êÐ@lM$Z‰l–ýÖ Ä™ë-S6)ûøB캦Q¦ú–+v/J›C¼†þÌ7Iò[å~Gp8/)úiB–f|Iì0¥$²´lܳ÷A^ÙïÞ³×6  Š£‡ŽŽ É?˜2ÔÒmš’Ò©åèè¸8âv5RËPƒ‰§Ó™t&3V4wl|ŸØvÀAð³G ÷’W@SsCq–Ï+ë´ÝšÍf³ÙìÒÒÒÙ³g=úüóÏõ«_½é¦›üñµk׆qÆÅÅÅ¿þë¿þ¾°¸¸ˆœZÒÙóËíM1ÿ¼rjza ·…Ó E^Éýçp|QÒ}÷Sqi)²rƒDê‰D"•HÍÏÏŸ={òäÉ”m‰ ²¡¡¡¡¡¡½½ÝðA)…°ÿ•2ˆÛ‡¼UE‰á(ªÌJ¥’Cžâð¤RIñ”êѱq·‰ÃÃN¦–îÓ”.‡ÔÈÈÇ™ÔU7C\WHî–¶¼²L­ÁZæ•¶MXä•’úéOzÓM7?~<ð”ÇÇǯ¼òÊÏ}îsÈ+kO³çW\ñʸ ÓÕG^i»Ž¼å±i€¼RB‰DbMïš«¯ºzãÆ½½½á•mÉ RÇ”}}}}}}íííeÎ3úW¢j]ˆ,Q(Tù%6¦ÄáI¥RbÆ'9%™M°\ÄuIRË*‹W“/ÛÀÝ-yq“2r#:ƒkÌ#¯4ñJl»Zôæ›oþîïþn5çÐo¼Å[“:<³àf|%uœ¿È+‘W¢UðJóþ„°‹Õ‘˜ü,‰ÕÕRQTE5ÖœQŸÖÖÚ6Ð?pÍÕ×lܸqÍš5køµ+W666vvvvvt\²qã@gGGss“qµçIÕlG©2âÿbÚ˪‚1&»Í°«´ÜÓÝdIq È+QÕ&œŽò¤Òx—É”¹¥ÉŽÊg¿|æ¼N…u„­»y‡[¶ÛN&Dá9E81\àk)31œðý+m×! Ã;îß½‡;=|tl|$eOGFvÜsï}‚”ÝΤ–q–ܵ{¯gËEG9ÎO/AÂSiÜ-y­ìQÂýÃâ•–¾òÊâi+++ “““ßþö·|ðÁ3gλýà?xòÉ'ßýîwc åº@^YØŒ¼UY5¿«ƒØö2›B µio›ÿÔ˜G”õ¸dþuƒ)ÄÎѱ£mW…˜ÿ°Ý®°½–¼¯–>ï¹Ðq TNwFS‚Ria;ÑHkkkkk+ôööRÀÉ“')ãîTãVk¥dj‡JsZl좎rÁ2K!öÁԯǮNß(„Ðü½“ï¯Q Å®hì䕨 "Ë‘€±•&kç,U»YÖí~ÒÿQÇuRÔæÃL­ÿÐgቼ‹-ô•‰a4I$ˇ˜Ó1.ª°N åY^š¶s(Û\`¿[–O]H^––¤ ‚©2ë/K^° °tºö¯,þ)•J%“IÞà¸L&“ÉdxÜJLÓ™L:vżR©ÔðÐà¨pƱÛð>®TÛÔ2XwKÁc+ð¿G ÷¯^YmXEQâñx<ïêêºúê«o»í¶w½ë]333Æ=ö"K”¼Îž_noŠ"¯d6—ŒWb/%Æ4.ž½•K)µvŒØ§•ík(:¿¤ PÂ{eL‡SnÔP~WLɽ#EfülΙQÄP4’ú/¨ÞÞ^½uQøWÀ,'ŒËY%`jNñ—ÊÔ>}<îPú²E¡'†£P¨ÊѰpz¸J>=†‡‡Äi†í))3CBž¥ª‚r·´ (cãûÂo"¯´ç•”KêH—_~ù<ÀnyöÙg±XPòš½°"Ï+)òJ䕨’ÈÓ|p—$JÙÂ>¢ÂUBÙóÒu!¯DU¯Y¢P¨JQ*•›ZŽŽŽñ~r|ð¢cš% –2P¯z©eî–ÁµØ£„ûW¯Ä†¬®¡¡!öÏ£Gê+'OžŒF£†=ØÀÀ6Q:00`ìF§§§¡hx».“å˜P?üáïºë®‹/¾¸¡¡aÍš5ï~÷»¿üå/¯¬¬8^ÑÒÒÒ׿þõ»îºëÊ+¯lmmD"‰DâŠ+®¸ë®»üñååeÁ±ÅÙ{óÍ7ÿøÿøío{[[[$éîî~Ï{Þó•¯|EœNÝppÃ+)¿ûˆ¼Òfy%Êã{Y.øÇ ‘ðêpEF„õgŒ¨cáz8ª\{G :½"ÓIÕ°ïT˜EúpnÑÙûWæ"„V–†¡%ëZiwÃKÍ+)6QÁ ‘eËç0u‘b©)9VÍ<g…HŠý€êbObl¢MËW÷…àˆùB}¬ jë×Óþ—ûHö,Ùµùæ¿~N7ƒÿ`õ¼Ó@Kq´nq°ït&# ž~¨¥‡d]A=Ijj6B•Ïá–¡DàA^‰¼2­[·ŽýóÂ…\LçÞÞÞ÷¼ç=Æö©©)ÞÌýû÷OMMþæoþfOOŸ,ÍÏÏßvÛm7ß|óc=öæ›o.--ô¡=öØc¯¼òÊüü|6›]XX8pàÀc=vÇwlܸñk_ûšL64Mû³?û³d2ùùÏþg?ûÙÜÜ\6›™™ù÷ÿ÷}ìc×^{ík¯½†hhuóJšäf{J lö×4ûí^×4sÒÂvAÇžæîMaaRKºŽ×)Ú§P†Üõ’IÏy>¶Þ§©ŸÄs 2õ¥À¶ c‡‰ÝRX%Š(ÈÓ +Ø™"î°gTmzCúéXLiC-ŸŠI¦õcáìcÚ_è¨Hà {E޲Úå\ŒAm#·6·ÊÜqß y% ‘%*tæ(9h‚w¯³³[ŠIeîSg^7íÃá`–еðQ%Ü´“…‘–øiv•·ѳÿ‡÷ïzæ«ßÝø_>¶a‰lgW ¼²•Ú~ˆ§®;Dôjtt\@÷üOo$Ôs²SËѱñÒ@ ándžºç•Å{"¯”KuRi¬oÛ¶ýéÛßþ¶m –íwß}·Ï,½ûÝïþÆ7¾aûÓK/½tË-·¬®®ÿ´°°044ôñüäÉ“‚Ä'''ï¾ûî»ï¾ÛqÀæwÜñÙÏ~–7šòÀ·ÞzëÒÒR??m9#Ë_ÉÙNy™w•^‚ö<ù¶šJ;˜ª_Ðí§ÔªÂyeQc,»<ùƹ"-Rè(‰†žXAjŽgî`LŸü B™OË{s½¦üU9óJt&±‚B!²D¡Pµ­T*™ä£+=–ïWGJñäçÇ™†=5[’Z†=Q=lyn™€¤4ë,G;¸>y%6X¾ùÍo²®]»ÖXÿ­ßú­––ãO²üÎw¾c¬···[fš{ÐÏþsÁ¯/¿ürñ0Él6û|à»ßý®ä)¾öµ¯mÛ¶MÜiù×ýWq"™Læ+_ùJ??í‘ x%oc¹x¥cÇׯt®KÇ+Qu®øWº†MáÃÄ0Náº$iè/qYx%„R¶gy%* !²D¡P'Ïqx€WZ—7¨‡Ô²dr;Ür÷ž½»vï 1ßö"òÊúÕË/¿üçþçì–[n¹ÅXojjúíßþí››N¿úê«–8ÀÖ*·ß~{CCƒQâÅÉ™˜›7oþÆ7¾qúôé3gÎüÃ?üCcc#ûë·¾õ-Ëþ{÷îýÁ~Àn¹òÊ+¿úÕ¯;vlyyyrrò±Ç»êª«Øþå_þå‹_ü¢c]rÉ%ßüæ7çççggg÷îÝ«(Š8'u=Š^)¯”¡{Ž/)òJ¬¾PA¼º®y¥‡S” ¢¡^5„Ä++ìæ”íà‰6òJTµ(‚E€B¡*Mzž±1îˆÈÑѱáaîh£‘ÛXS§™J¥\f)5<48:&¤9::>2’ ³XR#;·ŽŽ§….:µ£Û ×ÈÈŽt:-ç=“Éø²­ ±OQ¿¼²>›­”Òååå3gÎ>|ø{ßûÞîÝ»Ï;güªªêwÞÉî¿mÛ6vHã·¾õ­Ï|æ3ìfw×]wùÏdooïO~òcŠúG?úѹ¹¹ûï¿ßØá¥—^b÷?{öì_üÅ_°[î¸ãŽG}4‹éö÷÷ßyç·ß~ûÝwßýøã»ýÉŸüÉ]wÝÕÑÑÁËÉÀÀÀÓO?½fÍýÏûï¿ÿرc{öþÅåÀõü¼¨«±èåóJJ™I¥ñJ»YA:óJοp¸dšì‰¼O0—‰Ïã®WO™¸Iƺåß(§âf÷±8ç?BÊ^Çr'ªS™›è øvK/KáÈ«01œ˜IÔ#…S§ çm`Ï@ìw§l¥BfŸB%£1³Jˆª(‡ìC«0‡¯2ÿ"¥ªª¦ÿûjFRºÇl!'óç߬ÿBW¾÷‚H©nˆVü"ë³ø™rPŒ«ˆ¨&‰Öõéü4ÿ„0I…9›¬BrÓùu7Qž‰÷×1¨Ç…’޲¬DIúW y¡j Nühd&'Df'Ö‘õHÔ½ˆ ëPÌ:G"æÝ N6s¿D%K‘Å2ëúµ]L‡³ £¨œb<ÅãÆâILìj,Åô%+¤ã+-,¼‹µØb²wÊê‘ñvæy\õT¿X4ÿQl™J¥¢‡Ë±0‹g—`„c*•rŒÕ?ÖÜ·äÑLä•fy¥¸ÖR¥¡¡¡¿¿ëÖ­ŸûÜçX^ ;wîÜ´i»åæ›oîïï7þ,žÎnÙ¸qã;ßùNÿ¹ýô§?ÍZjÀûÞ÷>öÏS§N±þÓ?ýÓÜÜœñçe—]öÿø¯4‹Å}ôÑË.»ÌØ277'Å322bðJ]þð‡Ù?gffê¹)ØÖqÁ+!x^)›×>ÒÜt›  19¼²2¼, T£œî)Z 4Ê9@áV·¡HŒG%¯…oycLiˆuøëAX’ÛøëŒþ•("K U¡σ»R:Ž1ôFôdˆ—€JRKKdíª|ܸ[+¸<È+M3T%x%6^MüÜç>gÙ¨ªêwÜaüù³ŸýlrrÒøóرcÏ?ÿ¼ñç¶mÛHƒŒÞûÞ÷Z¶°ØΟ?Ïþ966Æþyÿý÷ÇãqÛ”ãñ8;Zžxâ AN~í×~ͲeÆ 쟋‹‹õüÌ´7Eƒá•4d^)ÞA”¸^Éî\¡¼U‡rnhè§ðÀ÷Ȭ͗, ^é¥h‘W¢jMˆ,Q(T…J)Âl·t_Ù»Æî#°¤ä>–rO²EŽAoÄqx†‡‡ÒéŒ X·€9z10ݵ{ïÈÎínƒüxy»víI;96–&3aKn)s½A‚ Ú¿šxe]µa}6Ù·mÛöÀèë?úÑfggÛÚÚΞ=ûÔSOûx§ø£ SQG£ÑÕÕUoE!N¹Ø“TOxÒUA²¼’ «H)•AŠlTóm—ˆ·ãˆÝúW:ÖoÜМž^*~Èõ=Ù¶(ÄÔe –jš©±m:œ ¹Ãl·´µ*êao);4›½ÍÅ_¥j÷øÑÊ€&þy%{oÙÄtf¹x;DÀ ‹ë^ô”)%ü\ÙVO7ªÜÇ~beTý^¾Á^8òJTåGY¢P¨ŠÖ°8<à4"Òó$nZŠ÷)ÍðFÉᇵ1ÖÜ·ô§>y¥L‡yeºóÎ;ŽèÊÊʾ}û`ß¾}+++úÆX,öÁ~°\ÙëééaÿdÝ6‹511Áþi‰®SÔGF@騕æ•ÔCß4(ÿÊrÄÛqwEñJJøð„ÔZpòŸ¬¯„ðye•Tý^¾m¼«RTpBd‰B¡*]~âð8²EÏ“¸'ž;æ-(ÉðSص{o L6ÃÖèèX:‘·¶ôy%¯Š¼2`]tÑEl(p=J8+üÖ[oíìì,Wö®¸â öÏþð‡‚Ÿ|òIöÏk¯½ï¯n«+^)Cô8/;òJä•(ï¯i=òJ@^~ÍàÈ+QÕ.œŽB¡*]zÞt`=Ž`Ö³øpص{ï#?ä!cŽÏõâŽáË)"ǹêã³™ä'@é˜U\Îeo`†¹UòJœ.äJÛ¶mûñ¬¯?ñij³³ßûÞ÷Œ_w!f7uðD7ß|3›™|ðî»ï¶ %´´´ôàƒ²[ñæú®\$y¥«¾)òJÛ¾·ÌTzÊöá ©µP*^É™—®˜l.óß{ ¹ ë¹(aNjöËI²ß&Êø€åÖrÿ±8|Ù{…F=”õZòÄâ¥Æ¹5 ±˜±åÿ$\Ë,Õ严ÉY)Ň¹¢@ ÍZ0û H=~ŸI¬>QA ‘e ÊRm±n‹¦/c—£0ö…N½¥bØ\jŠb¸ð(Ù¬fvÞaªoÖ q•ýð0ߪÂw‹ÊyYùWÒâJ–Z;ô¾ªOö¶¤ME³—¥ÂúZ²”L±«Œ¿»Ål@ÉøZš¼,#öI±^–Œ-¦ÅmÓâSé¸.~äÜ>¢2ð8Gæ82²ãž{ïìà,:ºIVµ,Y~|jttLàaêJcãûn½å}!"…÷¯^icŒ¼2(Ý~ûíŸüä'—––`aaáÿðôŸºººn¹åÞÑhtyyÙøóÂ… ÍÍÍÁæíCúÐ<`œåÀùÈG}ôQ‹åòòòG>òvÄzWWׇ?üa¼¹þ*—y%Ï¿’2am(›>µ4²¨Ý:ßïÒ¶¶q6ܤØÍçª5p΀yžºF³Y ,f”Œ™cVËfó¤f5›Uó?)ªšå³‹%«ú’û*X+v³g¥íµd³ºÝ½æö\zûßr qÉ&°U¾ÏMx¼Ò¸·”º»Ñ”RÄu%ãî *„DÃn¨…Ë(8ÝcWq>8ª.„ÃQ(TÈq¶ã”gñÔi}¨¦·¼9Fîöì˜é¡”dfˆëÔª²4ƒýï\^Y¼¿¯Ä6«utt°\ò¡‡ ÿóÁ~°8L¡D"Áþùè£...¾ð û÷ï*ok×®ýøÇ?Înùú׿~Ýu×=öØcSSS+++Çÿçþç믿þë_ÿ:»ÛßüÍß´´´àÍõQ¹¸á•Tò¥ u|%¯žr;ÓîD%æ•(”øE*ÅøÊ°³äz<-ÁŒóà ²bxeàOòJTÝ‘% …ªùŒÃ£Oìà8>Q˜7'SK¯Ž™n¥SKG«Çʧ–©T2¨¤<N‘W"¯ LÛ¶m³íˆc…oÚ´‰ýóŸøDccã[ßúÖ/}éKæíóŸÿü–-[Ø-/½ôÒ]wÝ500‹Åúûûï¼óÎ_|‘ÝácûØG?úQ¼­þ*7¼RŠYºå•¶;#¯Dá«Y—¼Ò_P¹„ßP+¯ôo'ðGÈ÷3†Õ'*4!²D¡PU#?qx@b8¤gŠW9Äs™qºR¨xj)0'•T2¹ypðÖÁÁ[·ßÿé0ú,aî_¼›®îtë­·¶··=ÃÉn¸ApÔûÞgïxðÆo˜·¦¦¦ïÿûlŒ ±~ÿ÷ÿË_þ2ÞÓà*^é¦6•Wr¬*^‰î(îëX¿¼’c±˜-ÚHðQEQôÿZ¤òUUEU•Âÿl–ªÊ!¿ä´È”CŽÌ™5eн:“Qôò bŠ€ÔÌ䕨zYV“,Fû‚ÝlëýëQØG²ÐjäÏ9»­-±à¢Pš÷©T(1Lp¨¢°§ÐÝ‚ògfl›ù¾–æœÒ Ÿf+!‰²xY2þžj„ñ²Tí׉¢– &/K³¥ÌºÅ¿Òv¤ý+Ë…±üÄá'OL}·x8§ oŽV’»vïÙ¹Ý?Œ“‘£É¦~½÷Ü{_ɲ¶’ÉÍ:¨´Ä$ ¼Ïæþ•Ê+© ^I±õêRñxüöÛoøá‡Ùâ!–ðÉO~òñÇ?pà€e{°Èº»»Ÿ|òÉ/~ñ‹õW5==ÍÛmÆ »wï¾ýöÛñ†H˜÷UÀ+©ÌËîÖ¿Ò ƒ²u©­—en±ÁxQMb—[j×Ov;¨“SóÛóM+¯Ô8 Ǭ–ÍfWÁlÚÈ®[|Ã&‹¼ÁÊä_É^–œŸŠ½,³Œk§¤ô°-¬£}…µFÃk-T¯, ÅCm9“/#ûSî®QJ6äNQbÛã„ß!u.SK;)è1œ4xˆWplæbãP @¸Q¡+Û;lRF5ÂB‹ãí jCˆ,+]&¨ìjnµÅ Åcù(<ðǰ3 ìÂìB¨ ²dëhM£Š~GQ¨}cš{4©±ÍM.²ôùÝâ~¾M_¶ MàÃA²TTÛí ~‡(LiE–Ì’D–¶¡xÀSøÞ†|ÆáÑ=1q]FÇÆS©¤7„ç˜8ŒŽŽŒ”ˆÊPK(-Huw¯ 3•L&SI}O(]Ôä•È+CѶmÛXdI¹óÎ;Ňttt<ûì³{÷îýÆ7¾ñúë¯@ooïW\!ˆØã½½‰ÜÿýŸøÄ'FGG÷íÛ÷üóÏ9rdaa¡±±qýúõ×_ýûßÿþ÷¿ÿýÑhoe€”ˆy_x%•xÙÝúWRÇ1•¼vWã+ËÈ+Q¨r´jd>¸  µX 4^ìÝ¡è_‰BI6A±P(TÉ™9: “J§3ç‡*:&^â€ÝÕN-­w?™Ô=.[\òàÔuÌ+M˜¤®yeHO%\×7Þ¸qãFÇ£‰Äg?ûÙÏ~ö³~r+E±Xì¶Ûn»í¶ÛB*7 7oóæKñJ Äódg™ùà"¯D¡üµWS,P-¼’º;ìxòJT­ ½,Q(T•ÉgCŸ&2ÄKi")ãk %´Ú”W*•L%“ÃCƒÃCƒ<üÐ#?42²cxxÈÛÌýrô@j‡W²ÇÊðJhéV‹‹‹<ð»Åqˆ%ª.$É+_öÒñJ™ T>¯Ä UÊÖB%òJqžƒEî„]òSºŠ-Í—§ÇÜ"/"rÄ,¶“ä™jr6¥ò`òí,\‡¢üDq2°4|,²E^‰ª[á(Ë—e–®QûX¶ËÌV¦$LR*¯.c?iŒ)§B©Æ^ä’;D±ÿ™Yúèö39&&«csù°S°Í³Ç9öHUÎÄpÕ21<¸Õ ÉÞIÓ2œ]· .˜îá+Ä®‘2Ã$Å)ÈØbŠwÛèÇ4Ó[~Òé´cHôJk™J¥J6‰>„òJ”ƒÞ|ó;¾¾¥¥¥_|ñþè^xáã§D"ñ¡}‹¹‡<¯¤‚—ÝÉ¿’m@ý*=Ëë³òH"ß–‡r«2*ªœëWVr»9“6l°= µ­-K—– mTX`›pF+NUÕˆ)lgl"|sS3”ÓĪ(/KV¬§¦i”Ò™Ó§ÝžKOÄ|FÖË<¹"ßÛ`á‘·{aΓ—¯?áuéì?©Q“ñ¢éþQÈS7JŒä¡¼ˆ24«Ù¾~ÛÔR7ÚÛź¾7Ʊ–Ûcö#zI]²µÇ]tÉú6ô¯DÕ§YVŠx&•¥ÁF ß ™­šˆA<crbqÌ4ýs›mcÃfGÓ(ëeÉ~‡S»‡—+NCYÎ&\¶HyHXh²dµ¦è=|diŠÞÃÇ—¶4Ó[ø·”3Øæµÿ8<â@ÂS,±ç&ø£~V< IDAT3ÍôVbŽÑô«,ß0ÆŠ&¡í_¼2Wß"¯ P_|1ï§?øƒ?( Žª¿ZGšWRÁËîf˜$¥/fæ°ì`ÃEñ~Ò¤‹Ë+Ævå_ù6¿åÑ%ÕòÞúûÒ:î_æñ•ÞŽ(A øL‡1)ý+Q(׉á(ª*5<<(øÕ‘ÍÓôpð3}[G„é—v.¶L–`tl¼”óÖk±R›¼’M‘WbkÖ6oÞl™$Žª×šGžWJXI:rLtrD¡JÚZ@^X øLÓJHy% ˆ,Q(T•JÃ#ØattÌ11ÂÓGkúÉa•RË»mÖVy%¶f}é²Ë.{â‰'š››±(°òqÅ+O©‰¢÷y% U±­…Úä•¶fŒV;2ënEΘ„!ŠÂYˆß…ÁžŒg¥ÅØtOÊ¢‹e.ø~Êß>䕨:"ËÚ—ŸÚ—k}¬ª¦uvQx¿pᬫª‰ ³j^Ì;Ùn¶,’2ÂM«ð÷¦CüÉlÍ.±X=‹ås*ñTH>T•ðlûÃãÈ=}"EÇô`tt¼”…†Ô2ÌHÍòJksÖ™Wb«ÖYïxÇ;º»»£Ñh<_»ví-·ÜòÈ#üò—¿¼ôÒK±pPA¯ìëi¢”µ §¼³Q|oQ•!MÓtLžª¿µP…ñvl™ö:ŒÞ¤™Ôqº)&˜)pÇ%¯Ìš¼Ç®3å´q ǽlêôÑ,þ•ÅÑW¢jFèeYeðQ¦–ñ€“x^–`öµ¤LkÂ8‹ õ`ò]6Ÿ¢~G£T±÷²4yV2¾–¼(;<ËJxVöhò¯ÖËÒì¯Ì3 T9^–úçßØÎ ³Ã ÅÿJÉð;‚MžØ ä?ÏððP:˜ZÊ$â'}:ÎRP:µäª,«òHóÊÙS m]ÍL’È+ýjÿþýX(ç—W†WRÊe—޵Š)ò!¾¼¨ xômûwg ÿqÇW“m/,Úí)‚·!M1Çñ•(#e‰B¡ªXzÞ¯’3»Å¶˜zto?™Ù!È$”cHc*•rÌÔõXËúå•ý½I:ðJªaÛ… ª*ræ•çHLâÅ·cšÖßøæ¢P¡¶W•ä•%¼Y!\ %/D–(ªºå?ã\i™9æbÉPKŸ`4Œ\é»çÞûJi¸Y…=ÚŸ®kñür~Wä•(Tiª"™ñ•p^‰ñ^ü–æhÑvä•(TYZ È+ƒÊòÊÞ,䕨r ‘% …ªn‡G¿lÌÛL°5ÓdÃ9‡*²)IN±{»½È+QU!ô²¬D±µ•äçŠwÏÐÒЧHÕl¤È&[0 T£ÔÌI™l1™¤4‹-f¡ñÍx›¯ÖìYi¿Ü}‚½#öE „çqÉzY2?™]#Ù²b½,¹¶˜|J“%›NRÅv|®*92ÏððÐè7ˆÍèØx*•L¥ü(GFvÜsï}‚|;êc9Åès×î½#;·;f5XŒìصkØ×²\y« ¨àsç _I .²œ9×ÚÕdi’"¯D¡Â¬^œy%P8¯Äåæƒ3o=ã_I (|…QðèçG¦ Êy0Y¿xk{Ýek–m®gfY¿¼2àl‡™aŒ…t.‰®‹RpÕÇB^‰ªá(K U r˜Ù-•[œˆ¤3¦@2ѺË2žÇZú ¾v®4^é¶IÊã•ØfE¡«dœxåt$Á{yû{šl^pêd…Bù­#^ ¡ç!øiÑ!<áðÊ /y%ªj„È…BÕ‚‰ÃS‚éáŽÓØA°«‘‘Ž8ZzlÊU¯Dà™9‡¼…*]%ãÄ+Ádd)Q9 ¯D¡J÷õ/þÖËì¼2¨’ < €¼2°‡…’"K U#ò‡$Æú =<€Qdj9:6<<ä˜Îðð €o꡽eÒÈÑ>R§–~¬3=—¡£á&ä"eJŸ½ 逸߹Âçƒ ì,`éüJ¬©ƒy% *êóÊéH‚—@K“9V¸Å-¨]£ _^T%|e)XœÙ Qˆ’oåE!…u¢Ø6€~ –ضðChñâøÊàò¸¥,pKLf|eé/y%ªò„£,Q(TíHL%Cr;‡ "´·ãpN–¾ %ÇZ–k(hù{R¾w® ÿJáÜðóÆÈ+Q¨pk!¯ §"­¼cÍQþøJjWàË‹BûòºÚye0—‰¼Òû…#¯DU¤Y¢P¨šR qxçnb7)žÉ±QBjDS®ºãíæ†/_XY:¿ B^I)6aQ(ß5ޝœæóJèïmâ¼õÈ+Q¨¼¼®öG^Ìe"¯ô~áÈ+Q•*D–(ª¦HÏz„ÖUlq#oŽaÄë‰ZÖW|páÜp:7s^Ì+± ‹Bù­qœx%P ±ìïiä¼õÔ{÷…B¹ýÒJí_û¼2ç4åÖ<ÒÎ;ÒO–Âä•ÄÍBÃLä•(”IèeYéâ¹IJî&ékÉŠ5‚¡îÝ$y?Y¼,mkYʯ^+ÓË’w €çe R¦“ìvàxî¾-¦¤¥ä…¸}J+Ab3Ê]»÷>òðC2éˆ]uú™J¥üdUÆ;r×î½#;·û<‘Ǽ¤Äž›P>ÛÍw@üï\E¼Rßa ¿grjÚö¤ËV–.¬Ä£€¼… »ÚáðJáKÚÒµ{ë)¿?›;ËmïíÖrÿ A³Y-Ë4Þ–––v×ÒÒj¡BX.¬g5Íhïe5MËæÖ©fª4Ê´ Mè‚ìëê®®sŠDôÅ|Ñö¸þþ¾þ>hlh4667þ'FcÑÂz4·‰DŒuˆÅ A“Œý@µ´y-º²·»85}‘s=Û\g>¦µ½,UU cU5þTÕˆªFŒíŠªØ–QÇöv ÆKòŠ*`|¥'VX›ã+¡>ÆW"ÐD(e‰B¡jMÎf”r‘â› …Ügn! yèÞäè¹ éLæž{ï+ËhÐ’‚;W¯{;ËÂ3Çæ@À+‘[¢PÁT?ö¼òœ?å!KÚÒM4Ç\¾õ 3OœºŠ9N%NÄ­+ÊÉ+Q(_ï«‹ý‘Wsä•Þ/y%ªâ…È…BÕ ‰ÃNÓà iÑŽÖ™å-C-¡|sØÃî€øß¹y¥®þÁs§Ï³Û‘W¢PAW?ö¼@0%œ‚îbY^I̓Cí¶W¯Ä: åù}u±?òÊ`μÒû…#¯DUƒY¢P¨ÚT qxÓ‘7Çt8‹Dq¤–¥ï€øß¹zy%˜ZÚ¸púÂÒ…e£YÌòJœŽBù®~¸¼r:Òz^óÞß–¦h¢)ê£Kê ‚‹ôœ ¯DUÙûêbä•e»­?ä•=¥(”„YÖ‘$Ý[H8R©¦?L¹•Ê;Ü_²>“2劷®( !Æ"Yˆ2{ᣮ+¨8<Ž·ƒât2ÔRrJ{ª?jYï¼R×@Àÿîô±y@^‰B…W ñJþ”ðÜ®…@áÖ—šÝÑ@Û}WÖ]§‚‘ÂëD(бèÍÿü¢–H4b,Ñh¬°Ä ³Õ´K4eŽŽ°Éš›Ûæv2'óÞ_Tä•]¦Ì9ŠCñã÷¸îÛêçÆÎÞVÏ¥‹¼…‘eu5¤È”  æ‹´4 ,˜ÒXtm]9WÔ¼AöpÕV¼íTtFÛœðre¹@^R‚¦U¡ÅÇ©‹ï.çFËl—|H*ö•v@Òé8LÜÊkRœap3¥= Œìp q5B-‘Wæ4Ðß›hiì?sly% J-TÄ+àH¼Gð>š‡Xò⃻D‡îx¥Û:³Râí˜W±CÙü‚<ä• š—,OúW¢P>…È…BÕ¬‚ŠÃN01¨ñzqñ>å‚29Ô3YÆ¡¥Æ Î Êj啺Äqx–/¬ž>6_Ä+±åŠBRµ˜xå›±ñû›¼¸ÙXr^) J™A^‰ªÆ×ye@—‰¼2¨rB^‰ª=!²D¡Pµ¬ âð8¢º Æ?Ö µ/£ùf‰¡‚°AYݼ‰fó@Këþ+‹« gY^‰MW* ÆÄ+í,, oÛæ‹æ•rñv<×uÈ+QUþ~"¯ è2‘WUNÈ+Q5)D–u*“yIÉÅZ=–À€ÒoRæÜ– |JpÓkCAÅá›c‚›™æŽ'…ঢ{Ρ µ,oÈ ’ÐÇeÕóÊÜOnÌSKûýÏY\¾°êµ§€B¡lßM¼²¥)šhŽ¿ì"ŠGŒ®/1ïÃ8Ä–€Ä²Î6“•!Qp·NÀ|xÑBˆÃÖDx'²ÙÈóƳo¤Z¶›°Þš±ÅÞïU'åRDØ/ìö«‘ˆÈɉc²$í8ˆ¼y%òJ䕨JS‹ 6°£Ì²?`±IñöœZQçj”“[ëé8»I1€@{Í2°¯Ø6Ò1)Þºä!rë–ZÖåÔQc:“áaµt:J¥d’ÙqϽ÷ vصkÏÈÈÿyJ§3¼<40sy.Õ‘Û)mÙó&MplPÖ¯Ô5Ðß›>ø†`‡³ÇÏEÔöþÜxÌç^brdÌqµ†êasÂ~$4vÌ&µ½BJ™Äò‡SÛkÌïg>ÀÈ•¹Ð¨y¼5¿2ÜRŒæ+5]5%MÙË5ŸZþ4§@Ùâ”^¡(¨é :1qâg[-ƒËÚ£mM±¾Ž–þÎf6#…<0IšóE‹ž:æ@jùÕ”k:Ô\Ú´èj¬ÛM)Ps‘‚åÖpŽÊh“y Ô\¢¶Åbת¡,jt¸ j“gÊ›.æ•P˜î‘WÂT%@(¥tBGßÙ&B ) 0 “0Í@yìIRª0'Ux­ÈÂV›H!3@)ò@mb`J„2;0ë¹t,;ˆÀ Q,Í]¢°*ÆO:;+^Ue֙튢ò[€UÐ<ã5¿%[å S’ªZè¥*ª¢ªj~»‰ä~Òñ¥m„Ûx„ye`/ä••òJT GY¢P¨ÚWPqxÀi̦| rçI/ïFZ:†¯Å±–uÇ+ ‘hJmÞ(Þge1{vê›8òÊ á•ö÷Ýþ‰E^Y}¼2?%ÜÏøJWÜJtY©ÝM™¹ä´tóÁÑ~å¾€¼2 ËD^T9!¯DÕ¶Y¢P¨ÚW€qxJ6=ä¨ey£Ü¤R)ÇLB­QËzä•úþE¦–6b¨%òJ¨4^ižî`û< ¯¬ ^yN‰h\'Ã+ÍQßóÁmß:ž%u겺;©Dßy%ª2ZÈ+ºLÿ¼’RÓ¢iÔµ4š«Ï¸‹à7¥[/¼«UTpBd‰ X® +KbûXrN%À‹Å‡*‡G†$ˆçÄãC!¸°?~$I-ï¹÷¾²gÕoE¢AY›¼RWjóʼn–&ñÎ+‹«Ó‡æ–/¬"¯¬^™?µ¨-˜ä•vÅR"^9i=ïq|ó–¾x%ëõC ž•4oR™sqTQ(&’ª¢¨ É-ª’ûon±_0‹ªˆy‹—Å”=¢X&{ ßfÝÑ´‘M‡ïߨ2 kÔÈ*Ê[F*IÜü[®…³D92íc=e®ìT9»y“iIù&òʰxey`ä•È+Q%zYÖ(tU‘ùüdËø]JæVÒUÓtxøè³ØAÎÔg†LªôEW²&ô^II&5<<(HJü(†¤’’±ŒÜµ{ïÈÎí’vœáQË]»öÌ7+'«È+½_5¥Ú¼1}ðÐüÂyñγÇÏGÔ¶¾&ä••À+ͨ‘×A^Y¼òœ?iµ\iÃ+“·y㕤àI(¢_n_™ß®“¡dÞ³’@Ás€fr¯ÔòÁN€h!Œ7e! {ˆùùQ=QW™úÐ!â¹¥Îáµ…t@ ¬…¢eÝÞu11Öõ?í×UÕÒ&³mžUTSͧ}¼Ù#•±õTS±çŸ¶„Û‘ûÉä ªðŠËø“†.yePç@^éý‘W¢jB8Ê…BÕ‹Äsº]ÙP:Ï4nð£Lxî]»÷VÅXË Éª·Þ‡Dƒ²öyeî™´kiM|e1{êðüù³Ë6°yeþ䕿› uÎ+õÁ•aóJ7û8h]wœm­¹é{—‡Wb{ å·Å€¼2 s ¯ô~áÈ+Qµ"D–(ªŽ`žáá!1¡*ÛŽ„4ØÓyVíRKä•ÖQKnâÎ.Ï¿°²˜E^ åæ•¼X¾¦< ¯,¾º’ðÊéHëÆu§¢­2ïo9x%uHdª#䕨ÚòJá1n*:€Íƒ»D~çšbÚÕŠºá¥&NSsº·÷y%ª®„ÃQ(TIg£c\ºçjB÷ÈÈŽ{î½÷«sfddG 9J§3‚™×ÁžÎ³FFv¤ÓiGø[U3Ä‘WÚŸ(µùâôÁÃó ç_]ÒæO,@C[´±-È+‘W"¯dÊd:’8i•û{šú{šÜóJ^‚”5ËXhÓ¼Ÿ¶¾3Qb§æ&íRдÂ%BÉæ&†+Tј š1TBc3 9ŧ’WJ˜í¶ëö R»4ùÉ>a™71œÎ[ódð(³®FL]3bdž@…N ÷)¤&†bLôVTÕ˜n™Λ .pR |n8òʠζóP!Ü>ôíE^‰ª7!²¬qY\#Ýàg;@ãÅP­g)êÒ$…ž•Þ4<<$B–cã©TR¥‰&õÉæA9G¿È ¡–2þ›°k÷Þá¡Á@?KßC^™»×9jyN2ÝÅÙ•ÅÙåH\‰4¨ mQä•E=²ñJK’æ|!¯,~*Á?¯œŽ$Î+±óJÜÕûë5>8ûÝ×)ž¹ÐòMBt¬HsLÉñJé("ª}q-›Õ”BÒ4J£4 Ší+ÆÚWÚ4ðh@‘Ð]%Tqãeia‘ÆŸÑh$eÖYLe×£Æ:Yšl-k« gòK-<$ª™?S*,²dý+™›"ƒ,~úþ;>A¡¢ ˜ZÀI"¯”¿}È+Q(ÿ‰á(ªî$¶†t5ÃZì .'›;çÜiæµù§ì%,ã¿ £cãFWAÈ+O”Ú|qjÓÅ®_]ÒgWÎ9?biivuuQóÉ+³KÚÊ<]™£+óy%ØóJ* 9¦-È+‹xå44MCÓ44žƒ(“a/¼rZML«-âk4¬=I”’WzÚ‡Ú8ÈM0g¥xœýX^‰Í$”—òʀμÒû…#¯DÕ¢p”% …ª;霑7\ÑíÐHñôpvä£8X9¸(^!ËŒµ¬‘¡òM0ä•ÅJ$šS›.ž<~’Fœ›HvIË.™¼íb­ªeÿ"¶˜û¶Dµe»ÌRÊÉ1a¶‘âñgLÓœJ€R#RhárB˜}ß¹Ï:èÑþi£6} k®,>}"‹lwX\É@4¾Bb«vçá ¢[cïŠKˆXËœ3èXòÎ\$å/¸åt¸lŸp›ìÃ7Mš¬ @vçª)1•Â)5a“+7ïcÞ¼ª“Wzì#¯DU‹WòÒ\&/5*KiPE'{/¨‹CÄç4σt.])䕨"K Uƒ¿]»÷>òðCò©•rz¸ ¬§Èj¦–È+Ýõá‰æTbãäÔÉÉãÓ~2³<—õy_V¬í|æXÛ €ÄMWCt„³ñr;|Ü;s§ÊPtiÁ6ñ¥UmiU#±•lYïB½gÀ¾œ‰ygÇ¢a¾_ÔþnQ D´ÿ´Òlz`ˆÿ"âîŸ\ áÄÛ±M'¨]ñJK‚È+QÕ!ä•Þ.“MO'x4ø¡ÁñJvÖ±£@àe‹¼UÏBdYGص¸ý•ÆC‡—«ªsðq›áÒÛbÖ¡‚Ãã˜Z° ±ê¨åèè¸À‚*ŽZ"¯ô؇èïI´4MŸ.nIË}_¨×rꦗ™WºKî òJ7‰#¯¬^iÀÊDS”9°4¼²,þ•È+QU)ä•@©í¢Q)™“±HÓ¡¨pÑ4#»%ëœBђͯQÍt¢¼²YËIL—k,`»Ý²ØÔÍÅŸ䕨:"K U¿ÇûÖ=(]¥æÀ@]ŽÜt”Lñꢖ°k÷ÞŠ¢–È+=_r"Ñ”Ú|‘op‰¼Rfä•ܯ²óÊþžÆÍ·%7¶3°‚㕚SÆJæ_É …¼U5B^)¼L+¡c3`Ù^|+¼Ô!¡å°¢`q›¢ž¨Zá*Í3R&k{Š 'ìæ$ ¯DU§ÐË ç‰ã¡ªg#Å_;zVzV°qxıÈ=$訑‘»víœQÝ)ï˪sk”R%qòJ_]¤ÜöD¢9ÕҺǥSTq©Ä‘WÏ+}]5òJ_EÔÒM4Gû{›8/c(¼’õEdâ¶ë¾–¹Ð„2¾– ¾– ÓèˆPÖ(P£ùñ¦QTšÆžÝ&à¯$e/ÉåݯDaÝY©ªF@ÿ¯.ÖŒ’õ¯ŒÅbñXœÙÎxYší/ éG"Ì1·îjÈËR¦k¹¶Vž»O‹ý¥ãé<³ä•A]ftKºy@}´äîd`‰¼U7ÂQ–(ª®åh év6·c ™À‡=Š'¤C£;ý¨ŠÆZ"¯ôqÉ6‰ôõ¤6]týµ—ôuôu{Ny%òÊÀïWYxeOÓæ‹Û¶\Ù“ÜØ^b^é©%¢É¿ÒÕ°ÇÊâ•Ø4B ¿È+ƒ¹L_H×Ù®^ÀýD^‰ª'!²D¡Põ®`ãð€“E¦ÛùæŽrœ6Ûzdd‡c†Ëžgä•>.Ùaêè@_Ï@_Ïõ×^~ýµ—§6mÐ fÄD^)³?òÊîW xeKS´¿§¹¿§ÉÀ”[®ìéïmN4ÇLû—‡WZh¯¥,¯gy%ªê…¼2°Ë ¾ª…WR§;†¼…2 '†£P(ŒìÜ.˜>::>2âb’r駇ëÔRp PI³­%3¬çyxh°ô³Ú‘Wú¸d«»‚-M†Ó%C-eÐõž±žpÚA&×éP—ÏaUL^÷–‹½>i®¬ ¡¾|ÝÜ;'¢îhŸ÷«d¼Ò©óLÝ2JW¼Òm]‡¼U ª^)í,átØuó[®ÅÂn×8Ç PÓ¨C¶ý5•n8ˆOJeÖ éb±yàL'öwGà AìŽA^‰ªv!²D¡P(Ȩ‹tûMwíÚã8…Üí%  ŽŽ‰¢œ»e¯a—¹ µO§3nË*Éd2`hðV Tä•®NêŽWº#zÈ+}ðJOòJ¯IåñÊ|G×€´àkI) ù”ó¾–„2^–ÀxY|-)ÕŒi[œð8&šáêQ+¯T„O2•Ë)jó™PUU#aL'MëѨasF£±üz,ʳ¿Œ2ë‘úó²´{D~".‹‚ç_v1âøÊ`Š%ˆlÈ+=—•¬ÆÝA^‰ª|áÄp”‹f„[•þŒ•£ê*j89B:’5· êQq‚¾„¡* rSÚ%³=:6þ{÷ý¾±ìÝû·ããûÆÇ÷ùínÑ_ðÑDF^éȹ\f y¥»y¥×‡¤rÇWÚÑ=™–R¯˜÷{D]¯ †W¢Pþ…¼Òãþî ,k…W:LG^‰ª[!²D¡P(€âð8'BTÇà65L-ƒïnÑ_ðÑDF^iÛ F^éØB^‰¼Ò3¯”!zÈ+Q(Ç/œ_^IæàoÑ4jH. %ð¯ Þk²*x¥ÿòJT= '†£P(TNÃÃC‚‰Õ£cã©TÒÕôðáá¡t¤F– IDAT:#˜ÆLmÇ9éúÏÒDò¤SËÑÑqAž!O-ƒM﯉‹¼Ò1qWé ¯t™1ä•îoä•4ˆâB^‰B9~áÂ_4K²îÏ?\Ë×0‚3PÖŒ2Ÿ´þ?ªe‹ ê¿xÁl…Ií Ó&Á“¾bõ®é]³f¥lŒnEQl`¬0ÁìWi™9§(†ù×Ü,¾Pðµõµtñ”"¯DUŒY¢P(TAÁÆá€‘‘÷Ü{ï×0Üðð x&»üªR©ÔÈHJLZ¡ÔÔÒ_Sy¥»tWºÌòJw7òJêá="@(“ a÷!l_ÛüjؙՄÓ0h`‚MC㜈š%Âw­8·Ä ¶X†[‘ X+*D1<1-0‚s aRàÞÀ±Æ$sù¬SÂÆAõóÊrmÙjjjjêøäÔñãLJx/‚z^Ìýÿꫯ¢½½&‚鳤8• ŸÇW¢jMˆ,Q%j^”ôÓRåå€*£ÃNÔ[šŽWQ]Äså4>T/®{Ü9G^阸«tWºÌòJw7òJ›«3@X’~§Tã4`Øñ;”c(%”­)ÚO.ÈO!¤mqWÝÀˆÆÍžoP#5Ÿˆ˜ŠK¿:Ói$y%>Ó‡>™Ã¢($÷§¢G*„(¼C8ëˆ,ݶ«Ë^,È+Åûÿüç¿˜šœœ S†Ú©|ñÅ—Œõ«¯¾Ê2úÒÛÝb«Ý 䕨zY¢P(”IÇáÑ1h°iÊœÔÑ#r×î½›iú”£gäy¥câ®ÒA^é2cÈ+Ý=ÞÈ+eæƒS»çEsõPÈU°Û5§tœo4µfÆq>¸†­ ”ׯ—¯?ϪRð~P¡xŸTWû˶Yä>ü““Sããû~ø‘_üâÕË+-zñÅ—þãßøÏÿô¿~õ«Oœ8ᥤœ3ƒ‘W¢ªQˆ,Q(ʤÀãð€ã\æ0bË8^ŒŽŽWZùW6µD^阸«tWºÌòJw7òJê-Áày¥Ìv䕨ªU)ÇWÆ‹ý+¥è£¬K3× æh?š¦eõ%«eW5mUÓžûÙó_þò#ûö훚š*ݽ(-S{ñÅ—þíßþãûßÿÁÑ£‹‹Kº——–—– ËÊòŠuYY]YY]]Ͳ‹!v=›Íæ‹VÓ²š…o3Ò,Ϥ±È>¥È+Q)D–( e•84·Hßâ1úôð0.¤ºˆçÊʵ̈÷ßd—Äï‚ÞòJÛ1òJÇ..òJä•óJ× ÑcPˆJä¬çÓœuÓ>6 €é”æ_©S"¦9Vj„{„{˜‹€9œÉ5_ì©k©ÅEÜ«Ò.¡æƒûu— ú2ñó_<òð?üâÿUê{Q&¦6=}ê©§þó©§ž:yrÚ<¥€ã+Q5+D–¨ênµT…ðÎV£Ä„ÑÃàDÇ1! tÄK-'¶Àî=ÎÔ2°.‰ßyüøñUVÙÕÕìj6›-l0;V2–•f±eÍlƒBç0s±!)eêBÛ*y%ªâÁ"@¡P([éS¹#øFGÇÄ®—¶iŽìÜ.À£cã©T2•J~-âóÀ®Ý{GvnüÔ%Èy¸]’`›fÈ+ÄÈ+ÁB^‰¼Ò> x% ± Aˆwv*ûÔA(¼2$c tª…Ê+%ŸÉRÌI/>µ©7ÆÆ¼Øy×Û·Ð }ú€j i¹*ÎXô€`úðRÈY†Íi@5Ë–¢1£Æ!Zah*-ÞÁ|  ôè™…‰³ç`ÿÑÓn/múäôS'§ozçÝÝÝŠR(¦M!ŠQ†úO(¡ì^`ìc¹”æ±bêZTéàøJTMGY¢P(WaÄáO9¯ã7eÎë8b±La¸ÈyX]’`›fÈ+ÄÈ+»—È+‘W–ˆWJÛJy6/‚Mn#§(q»±iÀ²žËgqf¬Þ”î÷ñhƒÈ+öS,Ør,/‹R®M/K~IV®|%øW–‡Wirrʯܰnà]7\ÿGŸú½?úä½ïzûµïzÛ[+“W‚¦­omܺ¡sëúŽOo½ä¶ËûnXÛ¶.wu±?ýÉÓ¯¾šønJáFß ¯DU†Y¢P(”HÇထ‘è0œx8Ž!€ 4?Mÿ9Ù¹]&Œx]’`›fÈ+ÄÈ+»—È+‘WVÎøJÁèêéu8 ìa >î) åX±Ë|ýƒ9¢rxåø¸ ^¹aÝÚwm}Û÷ÿþ]· ¿ë[€Rвú$ôÊä•OR š¶.¿amÛ.ëý@²ûý ùÂL¿šþñþ$°»ùÿ³÷æÑugÞÿSÝ÷Ê’ŒY¶e-–m 0[  ¼$C`ÀÖ‰ V“œ,Ã$™É;`4g–7'ËL2gÆàœß™yg&ë0 '8H"É$ó†ÅØ@ ‘ `ɶäMÞkéªß}»»ºoïÝwÿ~ÓÁ}»««««»««>ªçyB´T¼ª"YBù©qx(ˆ„ÆÎ6P!€Ê3€¸~#ã¥9$I·k^Ü!¯ m€W‚W‚WÆ~"¥Á࢈ »ÇGN¸¼²/BÝa΋¡=ÞŽì¾PãÒ¢i\vyhsh‹Óc,ž•ì-ÑáQrQiîð_iæ³gÏHØH;‹¶ßþ'ëÖÿÉG®Xy‘Î +ŽWê? vY·²uÆÿþ@ë%-§„¬ƒn}æÙ©É©©É)/išÃµ¥‡_Kï§_V®.=üŸ†iáÀ+¡²%AP€ ‡§Tæá.€xyRË0…OgH’n× ¼2¸C ^IAU ^ ^YY¼2bæà•PÕ©þ+cuDø“[tÎöZËŒ„ ={FûÂñÊE ;ÖßtÃí7ݰ¨£M‡ƒ•Î+s-“$Ä% ¦á¼–KZ¦‡©ŠƒnÛöÜT.¦išÇ€ÇbØ—4î‘Iæ’æEÄ~‚À+¡r%AP€í©ã…ù¤o…ã†a¨eb—¡ðå5¯ îƒWŽWÁ+Á++WÚ]Fê^(…äÃÑ}]>‘gbæêþ‘l'rO#<ÒäÖ‰ £îbv‡š9·šÌË¥·§Å¨Ôž*XŠ’ “^©GFFŸx"¯¼â²•ëo¾¡sa;q^}¼RQº/iiü¹sÛƒÃ:tèW¿z1¨«às;“ÚƒâHðJ¨ …ˆáAÁêî^ë:³)un5(y*§¦‚Å.©|`êSÿÅ‚$èûƒW²’ˆ¯ŒÔBÝ_ðJðÊ´çWF+XŒûœûF`ð ¥ÖY¨u^¹haÇ«W.êh3¨_ÕòÊÜ©¹øèâ™»ß|lè¸Í:tè…~µêÒ•Ñ; Á´ÑŸõƒWB*̲„  ¥BÄá ž¿+(yÈS—sñÁÞ¾~y‰—Ïàà`ŠC}ðÊ@V±`à•Ñðʸ xe òJ8”¤³àòüx;BèÎ …Ó/¥ôkJËy:œÒ4WˆîË”µÈÙÊsa¼JÎEö_†W^qÙÊõ·ÜXS¼R¿¤öFu]çôöÕ¿~ÆÆÆžîy—hwei÷k©¹ù±ÔoKî’}:¿¯BðœžI½»géwŠ!(¶€,!‚B©@qxãá.„w9SË®®tL¿ßLk’`Ä^ÈJ" ¼2P uÁ+Á+Á+!(vg¡ñÁEôEšS2MÜ·}ÇË­¿åÆ+.[™ƒ‰µÄ+õ½ ™BQËCƒo:Ѳ“RZ‹-‚’ ó’£ÑÜñK™WbÒ:”¢€,!‚ªqxˆ¨§gƒÏÞ‚à œæI…d¦4I0þ¯ d% ^í¯Œû€W‚WBPü¯:‡Äá•…éäô÷?1::êŸfý-7.ZØQ³¼2·Kˆ:Ú0ËÛo¿}èÐá"µOó‹â}èСѽ{OŽë‹—&%å(ó””ÇâÏÜŒÇc°p)w;êEÙ£÷[Á+¡)ƒ*€ Š$}Fä€G(j݈»««+FÎ=÷Üí3›2IÎaÔݽÆ.go_W×Ò BQ ó|/ïâg,=Ã$°œóxCãðÊ@V±`à•Ñðʸ x%x%Åÿú'=Â䕺tjyûúÛÓ.B„cFFGG÷îõI^éÅ+õ½™ŸÙ²jdܳÂ_}åÕ«>xU¡Ÿºû¡{D”T@–A‘åO÷6Þ»é;ßþfŒlu_™½}ý>9÷Üsw ¡@ÜŸZ´ö^ëoƒŸ?÷Z/FW¼2èî€W†l€W‚W‚W†©ðJ¨l”ªÿJ¯Ô•£–·ß–b"Óß÷cžó¯¸l%x¥¯Ô˳nž²e¿62áYÉï¼óÎ’%K øÔ¥×Ø#‚ Ãp‚ È*\žÀ`8ß­SKÿ4ïÝT¸X@•?b¯ d% ^í¯Œû€W‚WÆo¨¡â|lÓQ`†~÷ߎÃüÍj]Nª»¼tœM!Äþç÷wîr=çðððæÍi†¦¦l‹å£R³¹¬tì±Éð)¤Eþ%xίåŽí;Ü/„ˆˆ:v\~ÙJÝ-'x¥¯Ô·_<ÓÏ<|ç;;ÿ‰_¤”ÓƒWB…fYBÅQw÷ZŸéIl¨ý§pê>%ÃÌ@Œ§À™žDÔÛÛßÓÓ…g 4æH£ŸX¥¼òé­9Çó;‡G††GËíî–ícã˜ýûÇ|öNŸÞ0}zcèóTîøD”êyèlŸµ¨}¶žÉ—,Ê˼2L‹^ UrSâöÜêXÌu~¥¬ááá‡~ø¦›n""Mãn¯9‘9’1ÆŒ½Œ1ÁóLFNï(”KED/¿ü²žÐµTëoþ(qM"}à•î¼’„hÏŠ§ÐK'ÞŽ¿tjIEá•BˆW^yÕkoçÂöEmõÓÃRƒWzóJ½<+Nþ÷×û©¯„jW@–AñÕÝíçÑ2 X œÃXhb†ZÆvÙYùCðÊÀQn}çÐÈýõꤲz¤ÕU0ðÊâ^õðžc[_ÜõµÝöà–ß åfb‚WÊÛ‹Î+†âúr¦ãƒ2IÎrbϽóã¾™ÛCZsŽ!Å\ýÞ÷þsçΑêp×®Ý>ú蔡ɩIs±ÿ´¤Mi“S“¦÷K‡WKÓ¯%ç\Ór çâ•W^‘ëÉ|õM—¯ºØð_™ãƒ‚s¡iÂ$†à•y¼’„hÏð¶¬ç“ìõ0$÷_Y|^   ¥( K‚ ø*\"ò„“dgHùY"êíë¯ÅP<à•Á]ꜟʯþã<ðpoÙÏ©¬*^Y’ª+êUï9¶yËën༼JëîõÊ„zï…ð_òØ'wßaÄÛÙãvïÞýØcå¤$ZÚX¥‘H†”“SS梓J‡4®MiÚÈȨכÒÙÑÞÙÞJšfñJ“E AB¯tå•úò‘™“>7÷Í·Þ:9>®/¦&&&&'&­EºƒS•ähJ\Ól¥Lüݯ„Š, K‚ Dò„“ê•Ü<Ä=ÆðáqFòJVVδÊÒð©r,xe\õðžc›·üöÁ-¯à¼¼ª‚Cšé}¼·eÐ_™}{÷îÛ·Ï+Íå«V¤ÏÎ+ ó+xeÎ<¼aÊ«n=–ö3ˆù•PÅ È‚ (©ü¹^ooüœƒ¬³ mjéÝõ¯téïÚsÿC½+ ¼¼²¬®zxϱÍ[~÷à–ß‚W‚WB•ßapÑÇ?þñE‹ÅÎ>µŒÅ+‰h¯7¯ììh[ÔÞjÁ;ðʈ¼RÑšñ ¹FÇŽKõ¯„ªA@–AIU¸8<d]‡’Æï” ËVæð¼Ò¥C|ÿCWšxìûUu¯,Ë«Þsìkÿ÷…g^ÜW*ðÊ‚òJŒº~-C9£e+Ÿ1ÀQ¦XÊþ+m–ןҸÆùú;ÖwvvƮ瑑‘ÞÞ^Ù{夡©ÉÉɉ é§eK¬Miî‹TÂW^~ÅÃã'­^y‘á¶S\sõ_i÷Û ^iã•$D»ªµ©š×mݵkWZ¯aÚßTðJ¨4Ê  ‚’«»{™öÆ{7}çÛߌ—³N {û<™`o_W×Ò®®®B^ÝÚÁÁA¯zñÀA=‹2š­9^¹shäég_J+´ÏkiŸoïhç f„°Ê³dìÀè˜#“3—Ÿ–‹`%wu]&ä¬r§VŸÛØ"„[ÿ?7’1Ö…T½B†&ÆÈÉÙ§RÏÞ¥HÖ^úg9l§³Nn†H°Ÿúä)Dtìí÷5–ÍfëêêÎ8uѧu !_4N³J›Û"Ý ¹ŽÍ{%ò®5@ÉÚ–ý©Ó.iRÿ˜›üƃ[~^Y`^‰8T<^™µüÝS>³,¶-X$æ•$¡=ðJÇáÞ÷ãøñãà•d A”¾ ‡Ç?s"òq¦™âÖPqðÊ”xe[GËÚ?¼öÆ_¸ò<Î%È^Yl^)¼axeQy¥Uo$.™­|aqæ’ÙJÈwpxäøæ¿^ ^YfL÷0;òvn—Ï.)R»¦ÈÊľXâœsáºhSSŽEj399©‡·q=‘¦i“¶íÖ"…Ì™\·n][[[|j¹wßOò“÷ßÿý÷ß?yò}SÒª¾ËÒøÄ¸©‰É ]œ ×ÛÔÞÚ¢MMiÚçš¾® BhBh‚s¡q#2—šG[ ‚W‡ÓE™q¯_¡išQ͹E³EH²î´p9‚”3¤”|~¹Ü>²À+! K‚ ê“ÿ<Ä„T1ÐSdÌÃk…Z‚W¦Ä+»oüðÚ¯iíh!à•¥ã•ÂWZ£ðÊbóJs×%³Ø_t*ÏbaZƒZ‚W‚WBeÖ_HÖEHH-8ðÔ“OÆäJ‚ˆhÿþý^û¶¶Óˆ>§Rÿ"XÓ- SqçÀ̆¼’„ùÑônyFGGìž»òJ#,{ŽŸzë,äõñ7/56ޝ„€,!‚*RzŸ ½=–ƒyxà5Rb+xðÊ*à•­<ÿÏµcÞq¯,)¯t@%¯ñ xeix¥ù´^2‹ýÅBÖ>-øÕ9¾ùÇ¿¯¯„ª†WêúÈG>’:µ /dÙÑÚ’ˆW ©WZ_‹2'ÓiyÒ¶Ožƒ@k YB•­ ‡'g†cw÷ÚÀP%à•¶¯û;~ü¸Ý©«µ8,Æ]ÝZú¾y¶o   ^ YBU¼ ‡‡‚¥Ô‡Ï¦r±à•%á•;‡ö øäÛÖÑ^Yö¼2hl^YƼRO¹nÖÄ–£u>ÔrëöÑËW´‚W‚W–•ß|³ÿ‰ŸTÍå<÷Üs•XìCc‡žþùµtÆJ בÈÑCFœÙ~Z &“?Bnº…pòJ»!yíòJò.„¦iVË(ƒfi;S˜Â3™Œ1Æn}Š¤Ã‰I•WBå/†C–å4O™˜‡Sˆ8æ5@¼ y%‘xàá^^ ÿ••Â+}§Z€W–5¯Ô®›1ÞžÑ|ÞÇÍ‚W¦Á+1‡ªM:µLÔÒ©‘#ŠŽp~døÈõ¯‰ƒW꘼²Ð- æWB@–AP–WÀ8<"lwÑ@a˜âK-k”W>½u;xeÕðJ¿±‡m<^Y޼R?|Eý¤Ï+9ÃÞJ€Wº¦¯L“ƒ§¹qàM‡J Cc‡¶nݺjÕ*ýÛ¬fTÓcfFU¹ÈY+Š"ÝP.qÎ5M#!HÑô¿È‘\a&³ãLŠÎt#qNB3þB#‡ß1lÃkWêõS¦_ðJ¨\„Y–A—?¡ëíëOn<ñ)¢\ìÿÅ,L:ݶØé+Wnõç•ç·-l‰2¿‚ ä ’hW§ÚTOópÛDKðJðÊÒ<¤¨ÉòÕÁƒ·mÛ659955599¥Må–‰ÉÉɉIá%Î5MÓ´IMÓ4®qMãÚç\8+D8·° #\8x¥i áïË’ss‘V5¹Æî‹o|*‘Ø.]„ˆAAi È‚ ¨ê¹çnŸ½½½ý ót%™ü!¥÷O³ñÞMB-k—W’ïË¶Ž–‹VÉ%GAú»¿nú{>©¶nßk´à•®i‚x%ˆTÕ:tèЋ/¾±íDÜrš!¸÷qɳ‡Ä+õÄÜ„wB盵Î+Kú+xúd`(¦”ûÎä+ K‚ b¨Ðqxˆ¨§gƒÏÞbú‘¬jYÛ¼2hŠeTÿ•h (Yƒd󪹢nÜ+¡1ѼÒ5 x%Å –2ÎsòJ£ :“Wr92‰Á+‰SñÙ^`ó–<x%”º€,!‚ФBÇá¡ ¹œ©€Ñ ´U§"NüŒ×¯‹Ÿ¾òy%M±líX5ÞZJa¬iü÷âi'}’nÝ>êöâƒW‚W¦ûHZòÜ…j*W:tèßýnJÓôEÓ´)M3ÌÀm&Æœ ®q=UÎ6YÓ¸&.k9KeMË™‡ë‡iZ.Âç‚óF4h íù©M^YÜ÷¼ªPYBIÁ'{û’Ÿ" zx`4¤mÕË8€x­óÊC#>'¸påù±âƒC”ƈӈ!¾"{Òëe9‘÷âƒW‚Wí9•QjµL5{öìSO;M³4¥iSn^,sÌqjJãš&¸&¸©œkEÁ¹\pMÇœ°ËÙƒs\Ÿ›)J.‰×(¯þ/‘DŽíYº9†cQMÓ8·JÆ…ìý2•æ ¼*•€,!‚ЧBÇá¡ óp"*&%ìéÙPÔ²Öy% {"ËÖŽ–ÖŽ–è¼=YJc¼hðJáŽ,­MšhIà•y%Ú+¨Ê5{öì|àQú(–ÏJa28)J±tÜx¥ì¿’Ën.D.2OMòÊ”€~*Ó'á¿*[YBU…ŽÃCådNá¨eòé¥%¤iŒxË‹W’¯Uø…+ϯ„ µ7¯Ôÿ¿"û¾×‹<”›hIà•à•$+*¯4^n3ºN>¯´û¦´ù¯4Q xe¯ä)´6E˜> ^ •V@–AEUâðZ 9ô¿OJiziIøA#Þ²ã•þVám-1x%šAPÃJ¯$¢™÷½^äá‘wC7Sà•y'<=„ª)3Íš5ë¼óÏ×-†9ç¦Y¸nésI)›çâêäö‹\Ã.œs‘3 ·dù²ä¦8w@@ðÊ\ õ䟅ç^ •\@–AÅVâðú‘,fè›j ^ˆoÙñJòµ ¿påyñx%:´”B›cç•ú¸Mz½ì[·ï ÑLWbä]€‡¾,ËR3gÎ<÷¼ó¸«4®iÜî…Ôò¶˜c–škê¼2G'uO‹7.¯D¯‚’¶:ù¼’µ+“a’Ç‹^éJ!¨‚5sæÌ³—-‹ßÉ$“lüÑX¸l.¯ä¸!WÚxexžo+Y%%"Ær‹Õ3òƒûŽ8¼*YB•@EˆÃH ‹lŽj©¸s?‘ƒào”LBe)Ÿø¼Òç*ZÛçƒWBPÉÚ7^I$V¨ïy²uû>ßf ¼2Ò•BP+Wñ-Av^)·oçÒ|>.³E7Ìç•¢fyeÜ9È)؃'O–*–€,!‚J£"Äáñ÷›I)¡‡W ¹zY1âÝ,=¯|z«çË-±y%‡m %m{\x¥þß66nh ^%ª%œ_I&_“y¥=´Ž„ÿ¸ ¡xüx¥¨U^+üN°÷Ii2f¤>h„"q)!(´2¨‚ ’H牃ƒ®{õ8<]]] ÏÒÓ³áÓŸ¹Ó'ÁÆ÷õôl(ÚU÷ôlظñ>¯«&ƒZ³H‘:Š!F¼±{‚å;¿RWkû<ŠÍ+Ñy… TÚŸ<^)„hc#"ëzÌÐȉEm§ä½àà•‘®¢À/‹cÝü£NÿâóŸ5×õ¸0¹‡‰sÁ-׊^PFaŠY–3—͉1ëæ3–ûœ#FDš6¥ïúýÀàïÜ{)×^{­OɹOÀ!i;çÖ#ªqÍéðʧŸÝáuÖd¼²Úº°Ì[™L¦¾¾¾©©©³³sÅŠÿøÇûúúŠ5ýí·ß^»vm˜Ò¢í­P¼áÊ+‰D;‡n1À+#‚ ÷B—žW>íö7FxñJòâ•V”!„Ds¹Õ<¯ŒÒÚˆáAû“æ^ •D@–A¥TâðPˆ‰Eö YiÄk}~%]pñ9IxeMù²Ô4m||üÈ‘#»víÚ¾}û<ÐÝÝ}ùå—ïÙ³§@gX$=€xÉA £Ùjà•f¶à••#„¦i'Ož›1Ò2&I±KÍdTKŠª(úÁf&Ì:œ cL!ÆHaBQˆ &#!˜ÐWÈçRæQB!j•Wúü57B“3A˜79iJC@–Aå¢âÄáñwIéÙ¡G’¿i<•ÂÕf²ÑlUñJyèWŠî×®]kûkÄ®]»ô•ýû÷g³Ys$ÙÖÖæUKBˆ¶¶63e6›=pàQn$éH,Ný öË_þrýúõ‹/®¯¯oii¹êª«¾õ­oMNN^Ñøøø#<²~ýúeË–Íœ93“ÉèCåõë×ÿà?˜˜ðsª˜_¼¡¡¡ÿóþÏÅ_5‰ŽÈ‹W ð ƒWFJ#0‡ü^DQˆC8ðì³ÏÆ.ÕÌ™3—-[fkl¥Å¯AVrx×?ÿ_ïuø¯‚\gM ’gbš6ànñv„¼«&yeì9¯à•P- È‚ ¨\Tœ8<dNDÅ7Ä.Çâà•Î3$ᕵ۵íèè¾ÿþûúÊüùó¯¾újsûèè¨×Ì^xattÔüyíµ×Λ7/I‘Ž?~ã7~èCÚ¼yóÐÐÐøøøþýûŸzê©;ï¼sùòåÃÃÃ^rÎÿýßÿ½³³ó–[nÙ¼yóï~÷»ãÇkšvâĉ7ÞxcóæÍ7ß|ó’%K|ðÁ0Åàœõ«_]ºté×¾öµ—^z騱cš¦ýÏÿüϧ>õ© .¸à­·ÞÂwÁÎ=À+sÍL”¼Jó%,Ä!غukìRÍœ9sÙ9ç$ëïä ¹xñŸ‰x%åÁAƒW’Ä+)¯µÉ+_>en’›UÒ7A¤þ¦@—€,!‚ÊHʼnÃCeižœZöõõÿéŸýÓ;?{çŸ}N_úúŸèâ'‰{cµÃ+…O}$ᕵLdÚHDóçÏ7×o¿ývy×üc×Ûï¸ãŽ„Eºêª«~ô£¹îzýõׯ»îº©©©ü]'NœX»víç>÷¹ýû÷ûd>22rÇwÜqÇ6o¾ùæ/}éK^³)ßxã믿~||ß[»áÊ+EÔ¦ ¼¼*Ê[[Q¼2¸ ’x¥á`.Næ.Ë—e^Iµ8¿Ò×Mä»ìbk’ù¯¯„Ò%AP©hqxOTì)áJE äûûŸx"²¯të€&ᕵÜ}ì±ÇäŸíííæúG>ò‘SN9Åüé…,üqs}öìÙKóÚ±c‡ÏÞßþö·ùÓ$5MûèG?ú“Ÿ„}›|ðÁÛo¿Ý$üè£úg288ø½ï}ßÛ;êÎ+ß0ðJðÊBSŽÈ./Ce%Ç)‘rµ{h´»hÔ=Kê«yʨýÕü'“ó+)9uTr1ó»JûQyRTEÊR* ¤±±±D¼rÖ¬sÎ=×ÝQ¥MLZœ2KÈ…)Ì)þ|ÿ‘ YN*-Ãs–k1,;táÂ+ ¼ÒÉ+K`’N@ž€ R(eYB•—Ї§»{­¿SË´¼g¦[*Ÿâƒƒ©tèBR<_(f8)ŸRòJ9M<^Y³Øßþö·ÿ÷/o¹îºëÌõÆÆÆuëÖYðÀÀïÿ{Go¼ñ†üÊìc«¯¯—ö>£}Ÿ‚qÆ?úÑ:tøðáï~÷» òÞ-[¶8ÒoÚ´éç?ÿ¹¼eÙ²e<ðÀž={&&&FFF6oÞ|Ž}¦ÏøÃù— ¬¢SO=õ±Ç;~üøÑ£G7mÚ¤Ø]ªå—¤†™?¯ášðÊ0¼’ãq+é“.Ã$Û.9$ŒÃYAp¼•ɨ5£ftF©óFc=“ɘR±FY™ ™á~Õ–ØŒ±£*ÖùÆ|êÉ'cWÔìÙ³Ï5y¥ŽTìÖ\S¥Åª,™Ÿšl3—“iMí?ú.ËÝ…¸Ôy%#R„¼QäV\Œ¾Ýy%Õ(¯/ÏôôñÂÂ=¶7Ar`íÄÎ.­\`8NðJ¨²„ *;'Ù¡=N·qù!ˆ»RËÁÁ7Ó&ÉW¯ ˆ^âþ 1>>¾wïÞ^xáË_þò%—\rôèQs¯ªª·Ýv›œÞažOè[Ö¯_Ÿ¼óçÏöÙgo¸á†¦¦¦Ù³gò“Ÿüú׿.'xýõ×åŸGŽù‡øyËÍ7ßüòË/¯_¿¾­­-›Í¶¶¶ÞvÛm;vì¸é¦›äd_üâ>ìS’¶¶¶çž{nݺu§œrÊÌ™3ïºë®»îºKNðÆoà‹ 5 ¼R€W‚WBåõ!L³oïÞ_üâ±O1{öì .¸ IƵKN=Õ;;cÚ«<ñÕ {m3§\°øP¼Òè?Ô¯ô¹?Ž?"†ý@ˆ¤Ïdˆoÿ^ D@–Ae§¢Åá tYš8Ýá¨eq*x¥ËðÊ*ïÈÊö‰õõõ­­­«V­úÊW¾òî»ïÊÉî¹çžÓO?]Þò¡}¨µµÕü™o.oY²dÉêÕ«“—ö/ÿò/e—šDôÇüÇòσÊ?zè¡cÇŽ™?Ï<óÌïÿûuuuŽlëêêî¿ÿþ3Ï<ÓÜrìØ1ÿP<===---ò–[o½Uþ966†/‚íUõâ•"ô›Ð2€WBP²5*0¯L§²~=|À‰l>+%2^‚WŽNkôªj'²L>ù1m Dž9à݇ $ K‚ rTÑâðøãÑtÏ•b P§[‚_wÝÇDIW¯¤$¼NâˆhÍš5_ùÊWUU½ùæ›ÍŸ/½ôÒÈȈùsÏž=Û·o7Þ~ûí²åWl}øÃvl‘±)½÷Þ{òϾ>ÛŸîºë®iÓ¦¹æ%¹òÊ+[:;;åŸ'OžÄ“c{óÝy¥«S4ðJðÊÂ<ƒ!\O¤ãÕ’dSp²;¯ôôÜh7—”•ì¿ YóŸl&“Íf²Ùl6›Ibî8©kEQåÚk¯W%MMMË—/÷÷[éU!¦ ¸åoÓZršÓÜ̘\ïÖª’[t›qbÂL¢¿UŒ1Gd:´ãN¹gQkó+…­k ùÊ%L ’ÓFðJ¨¤²„ *G-õôlðOàå;²Ð5@üÞû6…¡–¾o¯|^)§Ç+k¼3[WW÷7ó7[¶lq%}²m¸B¶óøãË•—XSUÕ¶¶6Çôö0š3gÎ…]dQFÙ1¥ª*ªÊaˆôê‘"Ù~Úñ®~sç63½¶ë Ê˜j"K#“´"‡å!bB9—‚ ™’ a¹¿¬5^IB¼xŽÜIæá¹u²ìÁE­ú¯‚„™6=\›Ÿð«‘tvd Á8x%TŒ~5ª‚ ¨lU´8õÔSzÄž#GŽ<ýôÓæöTïèʇ§þS*"aDǽöÏ9ß!f*–ïÕú”yóÊ„-x%•F---W_}u˜”¼’Òà•ä,¼7räÙCm‘}£Î(…¯$)bOíñJáëÈrNssZÝ’¤ öàPYÈ‚ ¨|U´8<"âMIÌɨ»{­Á7Þ»©¯¨à²æx¥°®(¯„u¯¿n»í6ÕMNN>ñÄDôÄOLNNêëêêÁ¸‹©yóæÙF­’·Í|íÞ½Û1 ÷I @±Õqç•Ò4˜²à•I°y>Ì-\& Ü# ·EìÈOà•¹§¸´Æe²bÌõ²!Ÿ!º‰.ÌÅNEleò…(1I ñZ/‚Šÿ¤3/#XÆrÉÌÏ]cÎèY¶…ΙŠKÜYÓ‘eÖte)n{ú²Ìf\O pkQ3ªë’Éd:::-Ä›ç6_zÙ¥ÆÅº8 tú¬ÌXçÈÙ òŠå¢¥K—ÚëÜÒèÑ÷Œ{À Ý™9Vrîx'óx¥ õjŽW’¯Ìöü2Æ ^ YBA¥RÑâðž«ðAºããÔÁÁÁ¾þ'À+ Ä+)^‰®­Ÿ-Z$‡×£„˱¯¿þzŸ™/…ÖÙgŸ-ÿüå/铸É'Ÿ”& k 9Gˆn¼RÂg¥ç•.™»´$y™ÛÒð€ÂËÏYi„Ç0ÛµmiµW s‘›}¯õªó”ƒSZ‹gȧ×I_1x2OdévPviã¡.;::®¿þz¯ ™7oÞêÕ«Uo™¼Ò†C¥3ʨÒÁT-z)-sçͳè°LŠ{y÷˜*ÅáÑy¥Ó¥õS÷eIL‚Y¼’j™Wúx±¤<×Ò‘^c!‡“K‡ÃËäm€&TLYB•µŠ‡'ø\©:ÐŒ¤@j饥KÏH™n¯`^é_ò$¼×ɶá?ýéO=ú³ŸýÌÜâxÇ1Q‘óô­\?ô¡É?¿ñoŒ»¦ÿÆ7¾!oY³f nnjm¯Œ++šW’ˆrEQy%…~)#>0>ÉÛÚÚ®»îºüíóæÍ»êª«•Á;³¯›l¸¹Ùï/d/ ´È w`Dž7¡:~¥eH^ƒ¼’„x¥ÉsŠ¥lîm³eXLf$æ#ùqÝ£_†OXðJ¨È²„ *w3O vŠ4£—-ûˆ:#ŽþkWxeAõ±}ÌŒ'~âĉ¿þë¿>qâ„þ³¹¹Ùu(«Ëáhòý÷ßO½l·Ür‹ìtò7ÞøÄ'>111áH611ñ‰O|BnŽš››o½õVÜÜ´èˆ48';¯ôB„>Mxe~Ûˆ– ŠôF¦Æ+uµ´´|øÃ–·”WúrÆžÑÞ¶ïË% Å+x¥Ì+_žíç,%©UxáͽÁ+¡â È‚ ¨T´8'oyä‘G.¼ðÂÍ›7ŽŽNNNîÝ»÷ᇾ袋yä9Ù?ÿó?ŸrÊ)¸¹)2w^éÓµd¼2p ]þ¼íø.¦Ì+Ú}vs IDATõ ,X`R˼2ñY½9Ã×BeûðÁï3Ÿ¯$w^éhÐj„WúO±lhhH`¦÷ ^ U¤2¨‚ ò—‡g`pÐu¯‡§««+­ÓõÜs·—ÌÔO©üËVÀž^àöêå•FŠ$¼½Ø`Ý~ûí[¶lÉ¿ûþ±ÂO?ýô±±1óçç?ÿùÏþóDtÇw¬\¹2­²}ík_{æ™g^~ùesË믿î_°O}êSŸüä'q[Sd$Eã•’H׆Bx7Só+…'—Œ„#"ÊGäµQ±}½åæ“‘­)¤¼0d³~$›M»Ï‡2qaÆÃ¦íb£Û~ÝÌ4_V¦(¹Ù6ºëF3‘üG}]?í´úiæv=þŒ^2Ý/¦Ð£Ä0Ýš–ÔLn\œÍÖeT÷1ò´¼¿ oÇÐÖÝ'‘²žüÉ)«äš¦™Þ<¦¦¦4Mëìì¼þúë÷ìÙsî¹çº?º;BD¤ªŠkI…™Ï¢( ³’e묉ùuÙ:3çl6«I~E£ùóç1Æ…9Ë#G:f7.šÝh¸¹–î ý§ôËâ•Lä~×&¯ôŸb9§¹ÙnƒÍ\[[Ë`Úm ÇùM?üWB•*̲„ ª 3¤òÔO—¯ÞÞ>y‘Ëæïp³c´àíUÍ+Íd±y%&Y†Ñõ×_?{ölÇÆ¥K—ú“G¯P³o¿ývŠekllüïÿþo9F¿>ûÙÏ~ë[ßÂ=M—yóJ;q+äüÊ$¼Ò>¿²|y%¥Úˆütµ¶¶ú. †GÞû½\æ{@ì:Ó¯øÒ®CBŠúmί$a¶‚'؃iF§Meίú¬¯ohlhð¸7¾Î)Ëw^é`—"¬%M‚G­(T0YBU†Š‡‡‚Ìɨ æáƒ½}ýòòéÏÜi.½}Åô§ ^iÀðÊkÚ´iûØÇýg2Ѿð…³Î:+{ºÈ’ˆæÎûä“OnÚ´iÞ¼y>É:;;øÃþÛ¿ý›9 J¯)¯¯„*ë­•›çܹs›¥h0?ùâ®Ãvw–œŒ_¯$ðJ# ½ìÍ+‰hÎGÁ>’>0°‡Ê\èMBUŒŠ‡‡‚hêæáºÒÁòÛkƒW゘¼’ Ž÷7Œä¸áDÄ»í¶ÛüijjúÕ¯~õå/ùÜsÏmlllll\¼xñu×]÷·û·©/“ÉÜu×]»wï~ôÑG?ñ‰OœsÎ93gÎTeúôégžyæí·ßþ裾õÖ[ùàJ¥9òä•&v¯L‡Wbø ¥ÒjŠV΀‰–#Gö{?×70y¥‰/õ\À%qz¢ý´½ žžëëÜïˆÒ[sƒWB%|YBU’ü=9ööö÷ô¤æbRŸ×é3¥qã½›zî¹»N-½¼vFÕš5×'êén¯ ^i ¼²°ãF"2£„ëºôÒK—,YxÔŒ3¾ô¥/}éK_JRÚðWTWWwã7Þxãª7ÌËõ¨JWJUíGú„}.§ä¦Ð¾Ým¤-œ@3JÜRŒÉ%½Ó„£ní Ð½dXÍšÜÄÙ\VJ¥à\ȇØ—ÜÚœ 2OvåÑôišæ~EºÈX_‡ä:`ŠÂŒ¹ÛŠ¢ª’»I5cy‡Ìf³æyë²–ßF%ß—¥q·õœÉdÉQ¦¬l¶Î×a¯)ëçãòU™u£(ŠYuŒ1fxd S4ÅQíúe'žò-°Õ›¢(²/KÅÝ—e6[gº—ͨù&êjkm;ûì³Ì¿Oç?“¿yàÏ—w F–—Mft ˜ñô›$‘Q-òÊÑúé>¼’ˆæ4Ï!¦;¦ ö_iºB?‚å߇e8ópr ^ U„0Ë‚ ¨’äïe2õ™ÝÝkýZ¦¬¼¬ø@ðö*ä•Â÷:âóJtjÃèäÉ“÷w'o œb Õ’¼y¥ü^ú¼×"j³&Ó=áÑLùñJ¿–§y%…~YƒƒyGϲ¨ðÈä_Ë–-óO¹ep¿Ã¥K|paLj®I^ù“ŽÓ}*Pîî¤RQr‹m›¢(бSa ³‡;KFþ0>QèÚAÅ%AP…©˜qxO708˜®ÍrAÑö‘ò©@^iõúcóJôkÝ5444>>~ìØ±mÛ¶]sÍ5¯¾úª¹kÆŒ·Ür ª²^P^LHJÂ+y”92¯„*ôU­x^é8饗^ê“lω‰ÇÞ<èã¿R╼2_í%x ï‚Ò%AP…©ÈqxºººüZ¦îC³ À@¤}¤|*”W’4 ¼¦¾^Z¼xq}}ý¬Y³V¯^ýÌ3ÏÈ»þüÏÿ.ºáòs%_z6£¹‚Eá–/KÉ¥¦iºŸJ£PR&UÛ‹+åišúêöÃÖ…K¾,3ÙŒyß²™ŒyUU5K"ßtÆâò+®xüÇû\ûžw'ûÃáN›ÍÈÉ+™|Ýà•’fÏž]ïu'Å6zO¯&³qðJ¨È²„ ªH3õôløôgîôI°qã}==Ò: @ò£‡ûàÚ Ù"vÄ*—WŠ KŠÍ+Ñ 3Ï<³¿¿úôé¨ ÈþzðJc–U`³æƒÿƒö¸Ï¯¬^ A » î»K7¿ÒNŸ¬ˆFvÎLtkß®ÜÕ—¯~vë³>'Ùóîäc8rÃ’Yð_†WÖ××Ïin–ƒ#©ª"3ñŒT*£fÌ£LIQUÉ%S˜¢û®Ô×M¾ïÿ0D}¢À+¡â È‚ ¨"å?óQÓn,oHZˆ3æ.3/ÏîîµFÇ(×3°ÅÒ·ö÷?±téiBj•WJåÉ+уõÒ%—\ò‡?üáèÑ£Š¢Ì;÷üóÏ¿á†Ö¯__WW‡ÊòÞVw^)íh˜[¨fMxñJ¯4<Ä@¼ª¾×Ówx¥Hß'¦ÏYçÍ›{ù!¨åÛG.ž×Ðјv†X;¼ò妖WšÞ‹¶öv×VŠ·;úC ^ •£€,!‚*UÝÝk|bêÆÚålÞÕµT¦“zÇníšë)©UxeNc{$ã•èÆºë…^@%@†“n¼ÒçýZ}áœèÍšð2.B R«ƒW¢½‚âv‚Ÿÿp9–¯Ô5oÞ¼9ÍsòI³ç½©-CÇ×užÒÞ¨’k„W>Ñ~ÚÞ†SïE«Ý…er÷”"Ödûè_ D-Å%AP¥JÃãçÔ²·Ïœ˜Šzz6lÜxŸµLÕ<¼Œ!µ>¿’í=’„W™%¥Ð yñÊtÃÓ¥”J‡VÓÇ%×–N——Ö1Ò€Yöq™sðçÛÚ„á•äùãÒô w`*b K¯&‹‹œçA›/K»“JÍðSÉ5n®kÒvý§¹®Hë6CÝŠòe)_çÖ…‹è5tœ1ÌÅ2ôŽíæ.fÈ–Þü·vcrÙŒ#¦I¥’e a•JQÓù«ªÚ =WåGÕzWÝYSc6_–fJÝÄX—ª*f…™yå@®7"ZµjÕ Ï¿066æ_[†O´7¨ë6沫^9:múOÚO󜴶¶6H.,CðÊÀƒHüyÿJ¨|d ATÁ*r šÚ908˜:'-ˆÔ« ^)èÊË.xzÛ«ÞIðJ*]›äÁ+w44'oÖl³5Eš4vq.oçòº\4×4”Ø'¯$×yeÜàã¯ôE–ÜŠ¦¢qkFËÉdÀ§Ê(³ü‘¥Ç.ÇÕ¹r }F׋vˆyUˆ×ºÂ˜Â˜ ¯TE/žÌ.™/³ô(3Kn?»þÂ2"âLö?(¼^Df{ö˜T½ª|å^wÇÆd‹„Ên 3d©‡$bL¸^œYóò¹ÆW¯^½mÛ¶±±1·ˆ{Þ×þuðøÅsê.nÊV7¯6ý妖0“+‰¨­­½±±Ñº;fô'"UUU (çüW’3”:gl‡î/EQT @ÛËüìÁ¡²%APe«Èqx‚§v€“–‚ DêˆU ¯ô×›¯î<í¼Eñx%z³”y̯ôNv¶6DjÖ~úÌh>±£IׯLöY+B/¯–'j“4¢ŽÛÞz1Á£ÇŽ ï"¢l6knœ6mšDT3€† dš@D)Ȇ-Úµ¢„)Yú<-6²œ›÷ýwvÖô;¿ó‘jÆ?âJÄr !.½ôÒçž{nÌ×B\׋‡&^<4qqSæâÙ™ªä•/Ïny¥©%dÕµµµ7D f }àÍJx ðJ¨ä²„ ªl?Ow÷ÚAóðÔ9iy7ªŠW.ê\@Û|*#&¯ÄDKJ­9Êã•#ÙF¯ƒµ5æ½ì~&Ø:²„èÐÔ±cÇ;Žúâö R= vEÄK饗>ûì¶Ã‡‡9êÅÃS/žºx–zñ,µ:xåè´ÆÑiÓÃÃJ*¯à•P|‘QA•®îî5>{}æ`Æ–¿ÃÊÁÁï«@yˆQÁó+wz†¹üÃkñy¥Ã8‚ ø€Á1¿RˆÑìtßc¸ýpK¶ßðá•Ï3n{.m3Œ“Êá¿Òu"ó;šíu÷ë‹n–®/$-LaŠ¢(ŠÂ˜b¬æ¤ªÖ’‘¤J‹C®»ÔŒ*K>³ŸQÚÎ̲3ݽ'3kÎÒůX±â¢ðuÿâQí_‡'^<ªíç•Ë+wÌœÿDË’Ÿ,85<¯¬¯¯Ê+ü&‰vpÁ+¡²fYBU¼Š‡‡‚ Ò 1»³Èc¦0‰Bo¬{pAD‹.Úµ×5‹C{În™ ^ A¥c9äà•;æz%_}áïÈÚæwÿ•TÂÝþ3Dø/GŸŽuÆÈðëÇ˽ö8<º/K/“|=À ²³Ös’/C‘SÊ~- EXyJÇ(žeð~©%_–fÝo§™DULÏ›BvV 0f¶œsÓk-cÌržÈˆK.8ç---«V­|þùÂßÝ—Žñ—ˆˆ¨­ŽÚë¨-KíuåÎ+GëG§5ŽÖM­Ÿõi®ohhoo—ý?ÈTUÉw%¡ßˆlƪêlVòe™Í˜wJö8¡*ªé´Ô4ÉÎè™^)úƒîý‚WBå" K‚ jPñãðø¤ÑÆ{7}çÛ߬P.}Lå³±’x%-îôB–blß‘Ù-3bñJôm!(i»”Ï+Iˆï)–­õ_BP1{ à Eê$lNÒʪ¹¹yÍšëŸþù0®-eLÐÈQiE#7Û7aóÒk’J³:Œ¾†‰#­dR…1‘3(5gqªæ/ã(s—™³´w4Û0:mz’Êiš3gΜ9ù5ïÇ Ó0÷ö{ŸB„ñÀ ðÁÒ%AP•¨Èqxˆ¨§gç?s§O‚ïó7!/Ï!IÜ4ÕÀ+‰hQg‹›;KADo¿¶ëÔsRt^‰Ù[”Æ1WfGëIÎ!’f^ ¥/ K‚ êQñãðèn4ýOZ]Ô²Êy¥þÏ•—ï•ù^ÛEqx%ú°”¼íqúkÛ1}¾WòÎÖ†¼ñ¥ƒWÚÖ« A•ý¤ ÷<¶DŒœXÍÚC,Ï—_Z²Æüx;ž‰m`2W¶ÜŠÅ íñw˜-%{”ûâJ3yUñ Å#SK%œ¬"©ÅÈlÅÅ+.¼èÂÙ³gº5,O577Ÿ~úéÍÍÍ>Õå*GÀ%Õy§T3¯ÛaÏö'4½w³rï TÁ²„ ªÄÞÞ¾ÔOÚݽÖgv'õööW3ÙA«\^ID‹¬‰–ÎÌì?vxßѨ¼(‚RhBóJÒcïóJ 7!(ñ›) qHtóP-ˆ“¤Z‹}r¥…0ûÜOiƦ×üÎ9sš–_ø,¿ 0à²L›©†††öŽŽæ¹s]ˆ£]¶ ïjÆà•ùéò ´ËäZä¶Gz£”A߇ƒWB¥%APUÉ?2xo_!æ<úÏî,*-/ÝA«h^ID‹;,ZØâ•ù;¿Ù ^ A%häx¸D>Èrõ…M¼Ò>­R`¸ AqÞËÊâ•é2ðË>{vÓ–àü Ο9kf{b%Öœææ3–.íX¸°±±1Ö#”Ôeòj ôf ^ •P@–AÕ&ÿ’…˜óØÕÕpÒ ÒâÒ‚°´J畺Ü2¦JS†¸PÌTŠ$¸žRaLxdÅ'–~Ê0×… {UY?ûé,\k—0ËÂHQ¹«Ñ«ÅPTUÒ nÕÙÇX²d1MNLîÙ3rôèÑ£G¦Ñ+æÍ›×ÐÐÐÐØ¨×<³ÛQÛªW±ÖUU•¶+Š¢?B­d¦Q3¶dîëL‘OgžÑæ¿Õ,•~#„ÿ¯„J.̲„ ªB?õôlðO°qã}•È ÂwЪ†Wêÿ؃ð8õë_Dá•èÒBPò¦HŸÙL¾^,ÅêåMy/¸ôz6SxI!(ôë(b¾ÃÉD.‡Ñ-aaÂ~Kq[œ~-s‹ÓÜ5#FåvQöȘÉd2‹užwÞ¹—_¾úâK.n7”~ͤ¤ÆÆÆyóæÍ›7oÑâÅg/[vö²eóæÏ?eÆ ùz]# ©¾ñv2™\\¤ŒÍ³eÆîæ2/þŽ¢¨y1w¬›ãp`)Çxò^ U„0Ë‚ ¨ ¥Çáéíó´ïííó÷zO=÷ÜíÃC 4Á³Ð d­Êx%]¹ú‚§·ýÚ«jŽî?~dÿñYóg„á•èÓBP:Í‘ Óç{O±úKû»ló_iÏP`Ü A‘_Å ã•)2eµwä`å‚-æFι&lzÍþ–Òx•1£ÎÇóÎ7aÍÅe°ñ¾ÿÄbðJ¨L„Y–AÕ©’ÄáÑÍÃ}h‚gA¸ZõñJ]wÜrOýæÉ7ì?†Wr@KJ£MÉNßqÊ|ŸF`õò9öwÙÇ%x%E ãø¯L!EÔr¤îB:0BKaÚ<‘ðv¤_æj¾,âí$­7|7 b ³,!‚ªVþs{{û{zÒŸðØÓ³aãÆûüœZn¼/Є¼,Ø@èZµòJ2B‡íÚçUMïžó¿N ä•¿AÉÛ¤‘ìôþ9K|ÕË›µ5øóJa›timçBp ¾,¡ÒËáxÑôܧ›¿ZëÌÝÓS"byËÓŸúºÐFqa›W2’]CZ;•œ§HF†×A·~…í§*„È3Ï"HH3Y(7R¦ŒYgÔíÄ#Ãu£[µ ÉW££ðÌv!V#“á×4æ,K!„"áÞ5".¸TbÏû¥*"gÑëÞ3» Qbfeÿ•¦ÃJ²ù²$Eµ|YêVßæ•:‘žp&¯Û|Yš.SÉß@âçŠWºÄäù§ g MãrÔæñÉ)3ÍÄ„fæ31©™i4ÎÍIšœsÓç¶F@ΖÈ"‚~m`¤öÓ«Í´mÿ—GN¸ž¬­uA{k+546˜Íõl¶®®®ÎXÏf *“Íf²¡™6mštHV*¹q"Ϡϥü.Êa¬¹üÈY¾9·ž%ι<ÉN“fõŠªüL£Ÿ؃S±ýWƺˆb[µW˕Ԝ»þ+qï Ôd ATÍ*Užî>æá2K/æ°£x¥®+WŸ?4¼×Ç<üØw÷ô;g]±Ø›WŠÿú!=[ɾ™ÂØ,ÚÜ r½è\ìžcF[b9‚¹C, ㎠ˤNÏ(g+—Ü(‰u~aq ¾˜e'![݇9ÂfÍ—;»0Æ÷Îlí³Q¤S˜nf.Õ§™£õ|æ pll‚HÌüsšÞôúiÓëëV-™»jñ\=3£¾ÉwÐØbÖ±í‚rU!nÕŸ0ö’-s¹†Íl¥¬÷KÞ+å#\s6~Ú·•ÊxâŒuëêlOŸtRëÑ «ö¥ô™‰wÙ*¬ÒÝuæf’KÈ+;[ëµÕ翼BD¦u!²"â86 åAMV‘y%zP ý†B8Y,>¯¢‘~ž¥¿›Åè‰OÙ¥d8¼*OYBU¹º»×–dÂcOφOæN¯½ƒƒ•ìÔ²†x¥.óp":~àÝ7ž~çÌ+¹òJ“E‚WÊg/¯ò6×gÀ†çÀ++WÞ¶¦-ÿå Ã+…a\)ûµ”׉Höq©YƘœ%'Hpkb¦Ð¸0×-W¶Â|Âm¯¦°ž@{Áòf€Fi£´«nù ›SιÆ5"Ò¦4×úኦiSúºì{‘1‡@i6¥´]UlçµÏ²d®‡ÞxÕŒüàÉϸW '[…È• ÙÃ*ªjþTÕŒjXÚ*ªjšÓ‘ª8ýZê%°y Wi–?Àä„‹¹?qŒQn¶›ÍÇ¥üü;ËÀ’ð&÷Kžß}Ãç§¹^Yî¼R‘{ À+! K‚ ¨âT’8<ç-œ3MðÊty%-î\°þ–P˃ïýþ™¡®+:Á++‡WÊ·¼²ìxe`¼]«—7Åä•aÒ¸oLªi'-¯ÄDK¨À„+ºÿÊô¦lêèV¡ƒSŸ42Fv½†Œt8gÌt¡(Š š9ç\˜2h–¹ÿ”–±P.}X…m îzÉršâÜeÛ<à;S<@<óðe)m'¢L&c¶‡²û{VLaŠ œKM«$NW– öàPåJA@U½ô8<þè°øç%" YãŽݲšà•úžÅ-W\vž•éÔòøwÁ+©üxe(7Xà••Æ+o½¾uQ[ƒóþ‚W‚WB%é7”7¯ YÈãJ¿fŠ "Ÿ(Jz/0¿ªtYBÕ„º»×øì-: 4ýÞ¸ñ¾2í%wËjˆWêÿ\¹úüE [ü+îÄÁ÷ß|v÷èïÇÀ+ˉW ðÊÊâ•}MKÒã•‘bˆÇæ•¢iâÞ…Ì˼ª8º^Y¸“‚Wƺà•PÈ‚ ¨&¤ÇáñIÐÛÛW S÷Üs·ÏÞÂÍñLÔK î–Õ¯Ô×ï¸õê@jID{èÍg÷œ8ø>x%YYQéxeи¼²lxåHvú·Zέ›ø>®^ÞTt^I'Ê3N7‚WÖŽcd,žRlR­Eµi«=‰ý`¯Sµß*¯Ô+ˆ¤šÌ->¼)?qþ!ŽÓ¤Ó=JV3UÉ+“)T¼á_†ÀB&½ßà•P_–Aµ¢RÅá ŒZ¾ñÞM=÷Ü]6N-Á+2¼ãÖ«xø>Äu8øþ[Ïîi9³iþÒÙ$A)ðÊRðJéyÜfðÊRóÊ‘ìôÓç{ÃJÛûØÙZù…MT^I‘y¥KKb?QeñJ Â} s­I;t„.fÖv&\q™Â¤X:’«>UòЍJ¡xtšieåfDZžsãX2>ÇÁX æ{ˆNéOiÝ )9?Ô3fŒ9ºRí !W pfæÏÅôkÉ5MÖ Í¨Ö1“SSæS4e­Êû !¹Âôº«‡É„ IDATœPÕ®1æšÒqv¯pXŠé*ÔÛ—%cÌ|à™Â,¿–’/K!„š‘ê-cÕ›Êó!`rII¯‘çåšÉÁ+¡JfYBÕü'<öööè¼ÝÝkýZîÔÔ4¯4©e˜¹–D´ï÷‡ÓûξÃà•%å•~³1À+ËWî˜>¿¿iIH^yëõ­zˆðp¼25‚W‚WB)õâ Ÿ(ßåhü-ÕBÆ ^ð¸ç{p¨¦d ATC*U r¦908X8ËôTûŽà•9ÝqëÕ‹ÎY‡û޼Þ7´àÈ»O‚WÚ²- ¯$‹Æ¹ÝfG‚W‘WŽdûf/þÖüsvLŸrø1Þ…H“ ¯L£Í¯„*Mà•ÄfE‡bÅ Až¥«…W¢-…R Ã!‚jKÝÝk|‚íl¼wÓw¾ýÍBœ·«««çž»}N]8ËôôzŸà•¶ª¹ã–«wï{ð‘ÿ Y™QãœiÍÓš—Î ðÊ¢ñJá=ÉRHÿ€W‹WŽdF3A˜Òå,¯dÞ/>“íz%kHEÚ!"ÁŒui»`ÒCjN–DL8Û3AÎ]Ú=Ë(RÚÎ\Ömf´Ævá‘Ø£$¦q±lòi³>V3M>™b®ë.­ª ±žg{ênˆZÚd °ÝeoÌ¡ØLh­ñ©¢Z†áÉ0\6wÔ›ívxœ.wKˆ¨*‚WFi`R¼ŒÄÞ'+WºØêG\¼ÀAªìLP`s&1ƒû‹ ALÈÛ Ì¤W(#Å¢~,WH& ÁÈf +MÌr ÄÍ„T\’¹5YQx%c®2od©;¡³12¾´Ð¤Œ)ÈRU¥u²TóÉšyS\·—åç22w ƒ,UU5=úeB"K7_–æO!D’šL‰W: ó_)<ÝjG»û9§ŠÞ‡Û»i–‰Ù» òÝ‘’i’“KÅò€É¡./çrEiªÕV+R^Yž•‹Äeÿ•î(+£Àòöè m”{åÞD(®OµÂ«~d_–Œ™ ˆ­J~ø¥VB5n¿Ís+1/ï²î­x%d AU€J‡‡ˆzz6|ú3wú$ظñ¾žž à•Á+MjùÅ¿ºíÿúÅЮýQLJÞ:îØZß”­ŸS—}XH\ñØÛ'½2=ñÎåbÒPi³°›… Û]gæØ#rÌj°è¥È«T&ÍZTô\‚ÌégúAJ^ {¶Ž‡ÆdÀm£­®¹Nlš{×õ†NNiïžûлd®¬óË•d³æ·OÈår<\y eç³¼˜5L^ leÎ/ 9ù°óÂyáæ Ëý»]™e g™×ÖÊßoÓN:s6Ÿ2AD´£¾9a;¤ÿ³zy“l‡ÂòÊhi„ìnÒÕbZoKj· ?Ñ jJvß‘j ²:E7"þûî}Ò” ÂY¤| !.#D¤ì´_Ò¿ô!&Ï bÄ|°¨Húx€WB* K‚ ZT€voOOWIN­ûÓ,¹y8xeH^in½ã–?Ú9¼ïÁGþ_ÂñÁÉÓ'Oƾqïíœ w¯Ìc=”\à¡ÕÉî%šÔ´IM{çð{û'J5ÒŪB‘îUêG•úBÙå&WÎYÔVïòòV6¯!Ú@ðJ¨^ÿºk“WÍçMn&^ U±€,!‚jQþ6Úå†åo^•Wêÿ,îlùâ_ÝòÀý¿ é–ÅêÔŠR ˆ—„+*ú‰DÏ#(7¹rŽëüÕôx¥±Îˆ1Ón1ßeî·ÍÔQ!s¬ì¿’$³rÁi>¬ùÃôeiNzenõÈ\ÊK,º»«ÊÀž†´Ša÷m·òV¥uÅ4úVm¾3j&#%“Öåí²a¸<åJž|U6fá"ñy!äk²=Wr}*Š"†Këþþ@óÁ~-“õÒjŠí¿²$¼2DÍU'¯L!üWB5, K‚ U©âðQOφïó£–%4az`à•žÞqˇvï{fÛë༲$C+QuWTÚœW°’Rç•Ìüe{¯#I˱#cB±üW:ü *f†œ+ÒpZaL¼R(f0-Ë++£œ‡WÓ—¥KÚš)æqÅà•>¾,%çZÑŽ)mëî¾3™Œ„&=×U›/KòvËX/p2~dsÛ'{TmüÑôõ)×­y#r»<¥3€c +°h¼ÒáM2Ry ZËb_EH^ió¢è²J1ýe0Ff."ReŸ'R($aw`i[—QHq­BÛödŸzЫ7ÿ1³ýqA±.J~™ãQ´‚~1G2«fmOrp×,Ú^ •·€,!‚jT%ŒÃCAÀt`p° gOÖ‰¯ 8pqgËâÎ7p ^™þ%øØò¼¢Òž¨°¼rõòÙ¬¤”x%÷HϬ³Ep‹á9'=¨ÉäÅ>ò•F#¶Q3‡ B76)Ïv±ñó,‘Œäl^k#“h n½rŠ>‡ÚÌÅÜÏÎ9w-¡p´6"ìE‰Ä/²?§“ÃÍ{!K¹ ®ÈR7÷ ¿Ã˜¸ŒyEâr”1?æWBU UAT³òg‚½}ý:µLKuö£ðʰ.îl¹ãæ~±çæ+.=§¨ZðÊ ¸jðJ­¾°éo>sjy¥oéñÊ€BŠ0Íx%TýítÙðÊWb®­H¿æR­·•|¦*üWBPHYBÕ´zî¹ÛgoooáNÝݽ¶kéÒR=V ¼2ÎW^vÎ{nZÓ vY’A#xeu\QiOT^¹ú¦[×´ýÍŸžvùò¦üLKÄ+)â‰"¼ªBWÆÍ²yeò2¤A<“ÞðJ¨RÃp‚ šV ãðQOφOæN¯½ƒƒ¥pj ^Øçz  ¢Åówοò²svïÚ•[Š5h¯¬Ž+*í‰Òä•«/œCD—_ØäóÊPz¼RŽŸCÄ¥X:Š{ÃŒ£… bF<3NÙâê "Å ²CĹàBèYðœKNdù´ÌƽìÁy𕆠ŒæFcEÈ&Ë⛲oDÛÀ)£fë²DTWWgnœ6mš¹^WWgî’׳Ùl6›õ:Ä\—Cñ3莴½\ð$“ìËRg¤ØMhŸŠ¢(Òºœ,#Õ­ìËRQ<Ê?KÑØT¯´×¥ÕLøT±|w„ÝØÙòkiï‰PS:ýÚêÔ&“½Qz—ÁÅI¥YoÌÝ0ÜVyð_ Õ°€,!‚j]%ŒÃCD=÷ÜíïÔ² Ì4qç¼2ð@g]šÛŸÞöº¼÷™ç~›ö ¼²:®¨´'ŠÏ+W[†ÞÔÙÚ°¨­ÑøéÒ‹.oŽdždz£: jUÊ€W¢W¸±)w^é`pry¤¿ZoDÆcºFAÜ–Nú¡2ÙË„ì.Ó Ú%œW‘àí!Ž×KFªIgô:…‰,õz³Õ0cr^zÌ# Á%T{²„ ªu•6ÿ4O*<3M0º¯ <0¸W^¶L>öÊKÏŽQÿ"2С˜÷.Á‰„;‰øDª^gb‘Bæ6æåšO´ê"6‹ŠŸb,ÆýAO]µñJ˜FD­.ðJ¨ìU³ó+›-“H¹R¸™Eð_IIïE¥Øƒ'7®‡ Sðe A•2š~oÜx_ùHÀ+ŒzÕ"^ýƒWz\x%x¥×½‹À+E"^毄*Sà•qKU|^8ż2ì^ ]˜e Ah÷öö÷ôt•êì¥3¯Œ} x¥ëí¯¯,¯´rO¢g¸£ #A†oJ…vš‚ˆ(«é™ì¿RU˜™„çŒ9YvÂ8‰ÏM—}\Fyœ5 :«WˆàöÐëBQU÷S6«»ž”Qúù²4veuÕ×ס˜ë^¾ÉiþY©¾,½Êͤ ÷w@i¦Q<QU•Ü|YúÔž¾%Z­‚Wz—Š1«l.µª§ð-ˆâ~jæñ…+…²øöz ¼*3gÁíî\’Ù’ÙÞæØ‘óûñÍ.Cÿ•à•PêÂ,K‚ ˆÈ0ÐöÚ«Cž½{íŸïÝTÐDé™W^éz;À+Á+‹6¿RxÜ;·úU]àÈÀ×ǧ„‘ÚÏ(4“ñJô ˜½ƒÇ”3¯dÄô%os.†cGiS˜b-dýd®"¦0¦DP@bÆôó)ªÇ¢K5ä•ê!EUXî"ýÊ('È]½¾0ÆÝfÎ+¦ëBĤ¨QRm3$}hÁ+¡²%A”Sww4,ðÙ×ú0S"êíí/ƒ xeàà•®·¼¼¼Ò5sŠö0€WB¥æWÆÍ³1]’[s'~DéͽaU·€,!‚ œ§:ööö´þÌt`p°ÐA%òÿaÆÛ!Ò€WFɼÒãŠÀ+Á+Sá•Á¯‰½µánù€WBU'ðʸy‚WÆ=EÁ+¼*sÁ—%Ad©»{­_èð¾þ®®¥…ó)ÙÕÕàR³ÀÝc¯t=¼Òõv€W‚W– ¯”Ý« !ùNÒÁ ¡˜2f®3΄¹.TóêTÃ¥Y™Âu+¾ŠÈËyn† àWÀãq<™®>‰HUÕl6KDúuÉÎ(eÿ•uuuÓê¦IÛ%_–v÷—VþkhÆnõªÂ—¥,¯«pT¾«O‡£O‡ûK¯ÌKQoà•¡Š]£¼R¯zìzKZOà•P³,!‚ ›zî¹Ûgo¡­³ý]jRáíÓCôØÀ+ÉB˜«¯ ª.ðJðÊøO&‚ìÁÃâÂX iš¼Òõ¥ŽÏ+Ñ € ÐsˆôáŽò­¶%(8¯Ìw´¨oµ¥qú[´ùº´œ\š‰ÙýKªî‹±¦¨ÌÓ3¥ª(ªîºRõ[v«*Sü—ÿŸ½w’ãªï¾§{fö¾Ú›$K²…eKZY²IÂÅ6IxS„Ç©`KK.¸¸XvIÞ¼@ŠÄU…8PJ*6¦žâ<@BÅठ)0¬ä@x( OB^K˜z_Ä®%ëj¤½kï;Ó¿÷žé>Ý}N_¦{vnßOmÙ==§»OwŸ>sú£s~ÇByðœaÚ¦µÒ»R bI aø.¨/N¥ãSà2e„¯Í”%õ‡‡ˆŽûÓð?ü‰ú½uÀWFš…8g _Y­ÐQŸ|%|eÃøÊTÁ1á+AËÐóƒ§î혴'aœ)ËÓ^æš_–Ôgñà }€²à§¾óðPTOÏpmúÿá»ÿðÿyÏñOÚ™¾uÀWFš…8g _u¹à+á+«/™YûÊ8C¹í¾´>ð•1s_YÝíN{šð• ‰@,K~ìyx‚ZŽ;RÓ Œîß?19©Kðð#þÝg?²‡Yi/Ü}÷›î¹ûMéÞ:à+ck‹°³†¯Œº\ð•ð•Õ—L+ÆÃ¨Î°ðÀ xI¢²ÌL‚Œò'«ü]eÿL‚IŽ)çÚ Ø¦FYÚP³¬Kþ3šGS(Þ·…ú º1—B;²§<Ñ·ì|4 Ó0ˈiÈÉLÓt7‘–åõ-ËÒwaug§ûª/|eÌ\e,¼à+cž&|%h. ,(¨ï<üð'¬åßø@ 3æÄÄdmÞ:à+#½Iœ³†¯Œº\ð•ð•Õ—L­¯,»IÚ×92ÛˆYúÆÞ¡(Omã~oIþ‘+ËA-(¤ë,¤Ü–cùJ+°^J—ð‡Ô¿síÌ0¢lµÊÒ4íh˜†³lJëÉ«&M(ËÐ3Jª,+™#/Tm.c»úJáO!ßE)f¢$ן‰ÝЙªgâðýyžxÝ…ŠÚ… °iâ\7‘ Š>‹t·{s|%t&È  ¦¾óðPÔøô‰ÉÉ TœÌJYÂWFn_©¼ð•ð• Ù¿’“ž‘¥9¨¥.Úœ³>;®¶fЭ·ð¢ jÐrHôÃä·Ú“ ûWRÖóƒoB×ÅF耙¾<ÀW‚–Ê€šºÏÃcOIpüø‰˜Ý*Ž ‡¯ŒÜ¾Ry;à+á+[ÎWrÔ~âì¾´>ð•ñ _©9BøAà+AÛe @KÝçá;¢M‰È×ÑóíDd ¾2‘Ú€¯„¯T^ê&ö•±¦ÄîPêr,KA$ˆ…°ƒ9 aÑ´¿$B‚Â0Dy¡üç~+ÈÙ›‚Ý} i=±”Æ=ºw‡ºeÕ±ô‘ý¹ÂBýK·œ·( aHûÔ'“ð}!ÿµ6"9žÛ¢Y¿‰ÀWÆt›¥fñ•µ> øJм –%-uŸ‡‡ˆŽûÓßÿƒ?Ô};99ùð#†aOû_ _ _Y¥$øÊFó•nÔ5çK–ƒP2Uyî•ãÀ i™YšoGøbP’”F¹^Ùf©ƒÌ±¥˜ÇŽH'¼+}Ëìž…?~¥Þû˜“‡,A‚ôq C/¨¢ËR†¨L¹c'Ì¥-.Õ›h–}N´t,˘„ǯŒ¹y ¦ ¯ÔT¾5•#Až<Á+ÉSD††ôÖ3š<‹¨›º ®UJT&ãDáLw»á+ASƒ^–Â7’ãÇOÔzx8EEÕœœœ ~wš¹Âá+á+á+“\.øÊ&é_‘†£®WSXu­%“žQdéµ4wÚÂ>¨²µ ýý+ãe;é[£eíϾ4;P–"¨û<<áQ5‰è‘Og“)¦€ñÚºrCMSäL‘ð¬•ë9“³ˆW8*ƒFq|e’³‹QctupwGyyd‹œ¾2Ní_ š ô¯Ì,KÕý´&¹п2Ú$ÂW‚6½,dÀ±¼?äÛññÍs*ð•µõ•S3Kç.ÎþäÌ•Ÿœ¹25»lÿÁWVŸ™¾2É«ÁWfî+‰xe¦ì?ž8Ïçyjž§æà+ãÔ›ï+-ü¦ƒêÛð•eÉî™êBÁWÆ? Ñ&—b‚^–2 ^óðÔô½#‘ ‰lÖµ¹¯œšYššYŒÑ†¯L’™l|¥½^Ä>kåzøÊd5Æô<ÑôuuPw‡Óõ¾¾41ð•Ye©MâWÂW zYȆ±±°ˆ–!Sô4æ{G"ûÙ¬k[_¹¼²n÷©„¯ô®l4_ÿ¬•ëá+ÖÒYÛ0'ÎÓ¹+¼¼yYà+á+A£¶ÚÍW²ú/äˆqBCZ[%f‹ÙbÒü±e•S°Ål•¬’ÿ¯T*YVØ_©úuxf‹¸¤Í^9“%‹­ÊŸæTC Bu‰“•øJÐì@YȆ†‡§Š÷Ždö!ªYמ¾rjfñ'g®œ»8·¼²¡Ïd¢f3|¥: |eä5¬¯”¬¬Ñù«tî -¯ÂWÂW‚&ý+3ËRêClBÌMØCSŒÃW‚–Ê@fŒ ùvüø‰‰‰‰¦z‰a¢šumè+§frægS3KQ™¬ºi_ _™à!m _é,­¬òù+¶¸$øÊúøJ†»  ð•e‰£~$ÂW|e•%€*€²%­3|e’ØÍÖå•õsg$YIð•Þ•ð•‰.{Ô¥k9_é$pz\Fí¾¾Ô»±_™Q–b\§úûJ¦ô™„¯ ˜~@–´È<<ð•Ivb7[Ï]œñNüMð•Þ•ð•‰.{Ô¥k]_é°²&&ÎÓp?W&ç¯$øJÐX…6Œ_uDݶr1˜=è}öž—ð1ÞY§íÃÉD‚„yi…r5 ¡>GQþÄD$„ÐßõÑMà+A+e cÆÆ‡L¶óð#þÝg?ÝØ¯ 1ìCT³®­|åòÊúÔÌbJ_ÙÙ™ïì*¨ÚÁ¬|ÛX[]_[-úöÝ×ß!mÃGeö6½9òLÙ`ý-`VyEÁà`ûŸ=/cî2N1ÑÚø”r— ßeB†èíîèí.DíDÎ~à•‰ƒ›hNÙ½Øì¿G¬8éÊÿ8ô Sì“ÕwÉN`¨vÌî £t§gÖügÔeR—Éa9ñîz¥(VJQƒþU~z–×h÷6øJ‚¯ÕX@ÿÊ4Y’ÖYÒÓ§<83“ÖXûmfMî…³,H¨÷ Ô9ôü&ÏwÂÿË—öÖ4‚¯L±(KcÏÃ3~\;||üxxÔ˺¾‚PŒ÷öˆf]»ùÊsgãn+­ì!¢ÎÎBgWÞóÞṌ^_éiØwÙ «+««öÖý]äê+g•ôdÖ¼êÈß+|¥”EÖû–º“(}e0ò­ÞÃDÂîÛÁÒAÙï+¥SJ±\ IDAT†RŽ™¨¸ì_o"gš½Ý[‡z‰½gäõ\r'¯"t²É¾œ×ózNÈÝ {¯ {ÓKBùꪞ¹@Æ|oVÌž4þ«$¤5‚W`¸CÜ1/uà”‰rea¹R+%Z)‰ËHÔÖînyÃVîî }©#øJõûJ¼lƒøÊ̲”Ö6n¦¯¬þnrú"_ Ú(KÙ36v$LY?1:º¿á‡‡ÃWFì„™UƒÁ)ÜWövv:; ±$i¾¾Òm9:s…αÜ%¯é|%Αd½GžQo‰}eùëðBÒî¾Òsʲ¯tMeu¾Rra]†Õe剨8³n¬XbÅ2âß'":Õ¸a«ÕÝ_ _ êÝ>€¯Ì,Kémc+øJ·ÿeŠ[_ Z(K5áØÞ2<||üıc¬,á+#v’ÔWvvº: ƒC½$ Áô¾’½ ¾2ÔWÆÔ_ÉÁ¦õ•Þmi(oÙ)§7ŒÙ 3æstîªèî Ý[™à+á+A½Úˆ_8¢o[OèVÂùi ìʳÏù+áITå5 ½YöèoEü4© `iÃɆg ¹<~\Ä-š™Ú»$ª(tð• !²PšyøÊˆ$ò•…ÁÁÞ®®ɯ'ð•î‰×ÜWj%ŽòÞÁWÖÞWJÅ’‡r¥¡\ifØ-æâÔ0ËkâÜUºaDmð•ªÂW‚Z´6ÅWBä¦Û§ØÔþ•,ý¨ù~˜ä£X–ú õ´ ä_jÖf˜­*•eøY !Ô X;7ŽðÄ¡–~Døí©l"k«žõ/Y¡ƒ¯ ”%€ZÑœóðÀWFì$‘¯Ü¹sØNð•Á\l’¯Œ}á+7×W:‡ò¥¡\i¦hÎÍÈfy•Î]v_KøJõSøJf  miÌþ•ŒJÎ|"†pâW !œ+/„ãZš•õLlnH9ݳÒΤðö±ôÝД¾ÔÌ`S 7’ãÇOLLL4Ü[J›úÊÅmûºº:’÷¯¤R&]¢Ø)Šº`eM,¯ øJøJP—¶Bòš>îìÿcf¶Øª,j°,õúÊn,fž+ÊOøµýí©Sq}åöíÛîºëwÝõ?ZÀWÊ›ìÛ¿ïž{îÞ·o_Ìò011yíÚ5_N2p…©e"|%hp ,l6Ç>ðþoÇÇO4Ø;H;úJ"–ºXú·íì, õ¥¨¢2r‚Z®„ÔSóFŒÚ¾¾dÜVÈ|›Ã3¥SÅ^–•Û·“G6½¯t¾Ý¿ßÝw¿iß¾½q®¼l-3_ Ú(K›=î[{ž†y¯Tl _ À¦WFž‡hÐXÓÔ•Ž–aµ |%|%ȸ­ù6è+“įܾ}ÛÑ£ïØ¾}»ÛJh9_éìmßÞ½wÜ~ûÐÐPä•·­%|%ñ²PÆÆ‡|2EÏæ¾ƒ´©¯$7Š¥bÛÎÎBgg¾€úIkª üÿ©¾2…¯D}ª}4uIäÛ$ÜöŒñvþ,¶¤Ol)•+-vÿ”ßûRkðeÞ*Yå?«tòd,_iw®”®ö•öwÃÃCwÞqûÐÐPdyøÉ鉙é™õõõõõõõ 篸Qtþ666ÜË^²JVÉù«Üÿ’U²|m-oÀKyC¥ÒfUÛVÖ«Ô (KuÀž‡'$Áøøñz¿ƒ´¯¯\^YÙvp°7Íüà€L*§A±¦ô•D´²&Öð•Iü)Ê‹ï·ZR¹JÖ—_)Û(çÏþûç³§öSdüJ&>sældüJOçJj_épÇí¯½ýö×F–3gÎ.^[LÞþTÖiUíп4 P–êC#ÏÃÓξ’ˆ–W6tÛvvºº žæ=|%õ¨œøJÐn@Y¨9O›ûJòÏîI28ØëiÞ'ñ•hâUåd?_ƒöðpV<àËk¾¾ÔôqÌ|ΰŽÈè,œGäû§~ž¾ÒÙphh赯}Íàà`ÈåZ\\:󙤷¾´!P–êFÎÃñ¾ie_¹¼²²©Ýž€:¯¯$¦A±¦ô•26¾2úR ¶Õ´ÊÑ)+þŸyuüJïxpËbËšý÷ÿ?³³³NüÂrøBû“žb±X*–JÅRÉ3rœ½Þ/Üçü³ìAåL§N=~=à+}™ºýö×FZË…k×ÜÛç»RüÊòí(Ù1NåQÿž!ÿVeä¿¶± _ š(Kõ¤)æái_IÄË+ºMí.–ð•Ô±r|¥ýäuRQWK(ƆÃWF_ L’´bná)z*_Y±N³³s?úщèÇÏ<;;;ëN·ãÌ»"ÿIZ«X,J²Ë#&µGô¶”þ*ó·XLÌgÏœ r¾R—ÉÈâg^8#MnTòÜ]o˜Òé’çUlá+ASe  ž4þ<rèС'N ®hWC¾Wjªô»ì¤¢î¡^^ÕU5í_)¯n_ @’ç•ã<Ј_©crò…k×®Õ4K¾ÆÌŒV±ÑömÛ¶oß.?\ð•žÍÙïh9uu*ñ ­½Ž„¯”%€† ‘æái__95»Øe°çT•¾’[zxø÷¾÷½_ú¥_zùå—3ßó‰':ôÑ~tuuE{[…¯$¢.ÚˆQ Õ}<8|%h›'5¾¯œå+mÒX˾ҿâÅÃfݹë®7Ê|¥gsU6††nºiîz.////-kª,å¯|%h ,4 1úW*ö30Ø#í¾RÍK/½ô;¿ó;™ïöÈ‘#ᯎ =,ˆÚW†;Êå5X_å+ñ¢Rµ!â÷¯œûÑ~œhç““/„OÕR]–ì_i9àáÌÌLHËWÞv«üpÁWz6×g#DYÑ•+Wíh•E;€¥[ìüÙa.­h–äþ)oxd‰ 1@,K ı¼?d øøø‰cÇ6-¢%|%«±)|e3·€·,kcccqqñÒ¥K_ýêW?ùÉOÊïußüæ7¿óï¼á oÀ³ jV-)|e'WÉ >¼+빓óPL_)½sT§é™¨ñ•š)¹ât{Œ£9¸C¿RðH;áOÕ¯ë¶øqOWË’5? r¨Í¤ÆY¯‹e2IÔ]Ð,%Ë*9·Þ*•(öxð gΞÝ}à ÝÝÝËÒ0 gÙ‰kIDù\ÞÍaÎ-í†a¸¹4=3Sqv䆯åå[o=dY–]Dídr°E!|¥¢âf&¢W¿ú~ðƒÿOyg—––®][ìîîrÊ‚Uòĵd!]œò!&oÔÑ„Å8‹ÚÕÈô²Ð@Øóðè¾µçáÙ41 ~HÜ̾2bÿíë+=-'Ãèèè¾í¶Û>ô¡}ï{ß–<þøãxAͪ%eÿJî¢õ•CÒþ•ûJýAËWPåšdìuu¾ÒæÜùóËËËñ²T¥œúé‹?ÕmrÛ­·úWVå+‰h`pp`p@wm——–¢Š§M_ š®áK ¡h€yxà+9¼Û¶¾2È-·ÜòÐCÉkNž<‰§Ô¦ZRûJwë°Ê!¯$‚¯ £–ƒæëWÞv[šƒÅ±–U»§_ID¯¼íVøJï‘âúJûƒƒƒº> |%hO ,44E½ÍÂW&ð•­ÚŽ=răõüùóö•+Wòù¼¨°sçÎÁ†;wîtRæóù«W¯’= OøÇv ‰ðŒ}ûÛß¾ÿþûo¼ñÆÎÎÎíÛ·¿á oøÌg>³±±yFkkk_üâï¿ÿþC‡õ÷÷çr¹¾¾¾ƒÞÿý_úÒ—Ö××C¶ f綾^ú‹¿ø‹Ûo¿}Ë–-¹\nddä®»îúÜç>¾ z05¾2ºrHÖ¿R*¨íé+ñÎ ªx<“}=0°¥¦Ö2Ž{Ò¥ ‰byÛ­ð•©|%é"ZV’É“ð(VÆà+Ak‚X–ޱ±#ãÇOè¾?~bttÿèèèæe¨½}e M\µ¯lÍÆìõ×_/\YY±¶mÛv×]w}ýë_·?^¾|ùäÉ“wÞygpO=õÔåË—¿þ뿾uëÖ4YºvíÚïþîï~ùË_vÖ\¹råÊ•+ßýîw?õ©O=ùä“»wïVnhYÖ§?ýé|ä#W®\‘×/..ž>}úôéÓ?þøÎ;ÿú¯ÿúþûï̆eYûØÇ>þñËvrzzú[ßúÖ·¾õ­Gyd|||ïÞ½¨ñâ¾ fí+ƒÿš ÎJ¥È¾4º©ot´Úùv8b¾Ðz/öÙi^郑û,¶¼ ¤Ã9—زXÞij¹¿ÒPþg‰È¢qä‹'À¨eÉÉœ›"n”_1+K»ÇUÙñ+mŠÅòrooïÁƒ·<ÿüéªOóüù Û¶míììô!„!Ê=“ ÃpâZ‘•·œ¼å n\Ëœ™s2<=3ãîJ‰Ètèà-VåºÉWC…i_Iç£ò¾Ò^¹gÏ?ýé+ë´ÅÅÅBGB C¬« ج̰ƆÔMÏ}3„áæ v |%¨ èe  9ö÷‡|;>~b3_}¢šfð•ÏW¶f{V¶D´mÛ6gùèÑ£òW_ýêW•{ð­àRfé oxƒì+ež}öٻᄏX,¿Z\\‡ê.ùK¡ßWrx-T“ùvâøJÝ2|%h‰2µ)bæþ¾þ£™YËôƒçææt_mß¶¾Ò³yU¾’ˆ┡ |åfþ4Å+c$Ê@ƒRõ<<ÇO<ù‰G?yüÄ“““5xiG_é×%UùÊ–lÄ>÷Üsù—)¯¹ûî»åîîîßüÍßt>NLLüä'?ñíáôéÓ²¿÷Þ{Q~Þ¨‚$¯T~%³oß¾/ùË333³³³ÿ÷ßÕÕ%ûÄOøÒ?úè£ßüæ7å5‡úüç?ñâÅõõõK—.=þøã·Þz«œàŸÿùŸ?õ©OE^¢›nºé+_ùʵk׿çç}ôQyˆ¢2' údŽ~=„¯ôlXeïQ’5’#§ö÷g`-×ÖÖÒûJfžÕ+ËC‡RB_Y¹ ð•þ} èÊÐÌôt0Ÿš2_ ZIJРØóð„µTNÔsâÄ“D49ùBÅÝìÝ·oß¾}{oÚ³'õëHKûJifâ+[¡!ËÌëëë³³³ÿýßÿýo|ã‘GYZZr¾5Mó¾ûî“Ó=zTîÒøÄO|ðƒ”øœ]œ‘‘lÛ¶í?ÿó?!êïz×»|ðA'Á³Ï>+§Ÿ››ûØÇ>&¯yÛÛÞöØc ûãŽ;î»ï¾{ï½÷øÒ—¾ä$ûЇ>tÿý÷kç?%Ú¹sçý×mß¾Ýþøàƒ^¼xñŸø„“àôéӨ뒽&ò•TMüJ_¯(fRñfvm`à—k†È)t¢õbôxs©ú¬u¦w‡úHˆ–å7)ìké„¶´,vÂ#ZÌ–noüJyYËq-ãTÔºõ²SÞKÿBR¼KÝAÙuILÊ»,‡z´,ËþØÓÓ³÷æ›Ïœ=[õ)ÿìgWFFF …<ÂF¹†a¦“EOA2Dðìfí@–†¢c“Å–AËçXyd¤d)\¢Âʰm}% lЖ^‹­R©(Ý)Ó(I·É”+K7h¥´, ABŠ,ÌÍ6˜%|%¨=èe  q !{üø‰‰‰è~”/¼pæ_ÿõëÿó~*Áá+õ;lO_éÌ‚mFggçŽ;^÷º×}ô£•}%}àðÍ'óÆ7¾qÇŽÎÇàØpyÍž={~ù—9}nÿäOþD©IDozÓ›äSSSòÇüÇ\XXp>8pàþá_éP({ì±8k‚cÌeŽ;æøJ›w¼ãòÇéJwëíPí+½ë9ŽòSHÁ•'˜-ç£-sÊ–UùÈÌ{°Ó”'8‘Ò³íòØb²ØvyTþ³,飽lÿ—©òÑÞPZö­·,Õ¤³`KÎ0shï9&Žœí°d¾/ä?ž+ï–+V`ÿ_9±´w×–› äf²› yëîîî_ñŠ4ÄÔÔÔÊòJ©X*‹ÅbIú«P Ã*YVI{%n9x€ýa2Ùûo¬h0èJíì+™ÙÎÒËÊÊJŒþ°Ñ¿Q{Èx~pøJe  ‰˜‡'Ðó¸¦WæÝw¿ ¾¾²¦>|ø£ý¨o¥išo{ÛÛœßÿþ÷/]ºä|¼xñâÓO?í|Ÿ/ô}—"|ÏÁ€˜°-•Dÿ0ËÊËgãÁ™XzßõYÅjÏöìù§ÊŸ²ð•þê+b"òؾ2´ f¶gH×*ËÊl*¶t°*†Ë*Yr2YZ™²Ê„²¬q¢,Ý©u¼ëeJA•ÉzaéÛ¹Súå’`•<¦²\è(lݺ5ü„™››ëééÉår¦i0›RáqE˜!„“IÃ0*±5Ù0,fÉr²o‚òÙ isŽú¡T~Õæ¾2$ÜäüÜüÈÈÈ&7Tˆà+A#‚ ˜óðdÈRÛ4k__©må+Ósß}÷9–accãÉ'Ÿ$¢'Ÿ|r£2|¯P(¼õ­o­Wö¶nÝ*¼Ú»çÂ… òGßì:>àVjãa¼¾’2_9b:dhv’7¤¯DPKP‹g7æ³íÐÙÙé«™“²´´òoQJÙtþü…ø5QˆatÖ(Sú«³öô•Ì7Þxc-~Rì"YøJ°9@YhÂçá9~âDæoÑí8øÊD¾²[·¯xÅ+ä©ÀíYÂå¹Âï¹çž¡¡¡zeïàÁƒòÇoûÛ!‰¿óïÈþçµÓ&9šùÊxµJûøJ¼‡ƒlUƬµL*›8àó¬²W¬êsê|þ­no­ê+°˜&J_ 6 ÐØóðLLª'Û™œ|abb2$eж|%ÁW¦çèÑ£ÿñÿa/ýë_ŸŸŸÿÆ7¾á|2ñŽ<øŽˆ,Ë2ŒŒÿÉùo|£œ™O~ò“<ð€r*¡µµµO~ò“òšÃ‡£vÚŒ×É*}¥ï 3VüJ‹=‰-Ë}ÀK–¼Þ×’•ñ.=ÉCÆ-ïÉqT½ª›Ç V6¡uf,©ªei•ÃSêS–J¥RÅ• ³h–—’a¥ÈtRlYùq¶¤ã !„f08:/Çx^´O,ËädÅJŸ÷R©hébYô«#«–K‹“À*YÚc)ž:Ëù|~xhhzf¦êK±°p­¯r•"'HT~²É4 w`¸iHq çd ƒìãž8žLLdYÂy€<ƒÄ%+g—UßWäW_QP‰å”Ê~¬áe[ûhx«Ã°Š%¡¯düÃÈ(KMCø<<Ÿxô“Y½ìD¾ü·™¯ôd¾²:î½÷Þ÷½ï}kkkD´¸¸øgög‹‹‹öWÃÃÃwß}·nÃ|>¿¾¾î|\YYéééÉ6ooûÛzè!ç(§OŸ~ç;ßùØcù"Q®¯¯¿ó˜pÖ ¿ãïÀÍÝ4“¡¯ô×LLÖ¿œz%®2½þÿÒ~ek›kË«Îy´;4SiS|em*øJÐ @Yh8&&&ÆŸ?~â÷ÿàÿþЧ/Ãçá rO5½,ÛÒW†4Fá+³àž{îð­Ü¿ÿwÞ²Õ›Þ¤.À/¾øb†yëîîþ·û7yŽ pÞýîwæ3ŸÁ=ÝTªô•‰j@˲²²²±±Á|uE¸bïÌT•Ú¾2…¯ä$ŽÀbË·&ôŸœ9xÓ£[eð• 1@,K ÇÄ„gŽñã'œÿÚÃCæáÉD „ú‚ð—üVô•_™ ÷Þ{ïg?ûYyexK"zßûÞ÷¥/}éôéÓ¾õÙ*K"ùÎw¾ó©O}ê¯þ꯮^½ªK¶{÷îGyäÞ{ïÅ ÝT"|%ßQ½¯Ê*³@î48•)w­ÌââRGG‡YžÇ3Ç`E"2 Ã4_pƒÈ,76Ö7,Óäœ[‡†<[‹³Ì¾)ãH¯/åð•±[¨živ41U§º&¼} _ ê”%€†Ã§,elqŸ»SÍ®kÿµ—¯ô¥¯LÃÑ£Gee)„¸ï¾ûÂ7úå/ùìÙ³D´mÛ¶ƒ†ÌØS}»0—{ðÁßûÞ÷Ž?ùä“O?ýô¹s绺ºn¸á†×¼æ5o~ó›ßüæ7çóyÜÊz ñ•ʇ,qÿJ<§´kkk’µŒóã&½•MDÿJÍ„¯Lð3¹-3…ÌýgÈ9³<{8|%¨/P–ŽZö ¬²…ãž2ÒW6S£¶F=ΜYÂm~ñqÏž=‘[õõõ}øÃþð‡?œ&·ñϨP(¼å-oyË[ÞR£ë†Þ|ÕIÕë!S蜼a>YД´'¶µÌ¤†ô•¶üÂxpÊbN½#ÓêÅHC8K¤Ê@c111‘áÞŽ¾Ç²ÒÍ„ÛF¾2"^ûøÊ±ººúÐCÉk"»X =Lé+íËÚÚZ.—˹}-•±,Yß2ñµ ¢}¥²¦‚¯ÔÖôì?/&¶ÚäN¬\iîj#“F OøJÐp@Yh,BF…×ÃÀWÂW¦â¥—^ºîºëÖÖÖžyæ™?ÿó?ÿáè|Õ××÷ö·¿<ˆ_©}%G×ìIì>¯n$;‹Óþë ©(nlX•9Äí!Û¥R©R£ˆJÀJ2MQ,×7ÖsfN®ƒœX–ÌdÁ$‡¿ô6*ƒ•áÆ^SÉR(L‚¯ i‰…µ>%M©_ š(K-ËáÃ÷¤óð•YúÊö |ã7ê¾ú£?ú£àâÄx=¬ÖWVS«ZÓ0LÓÔº0•qcõüàþ¯Èëò„¬šËE% ¾²Úæj²ét”{@üJИ@Yh,ÆÆŽŒ?NÉ'Ûɶ£­×¾²’&½¯D×eß¾}¾AâÄ{áLæ+õãÁå q-h#LÃa3´px›(BúG‹+šð•ÛúEÛÃ,|eŸ#Îv‡„e  ;âü·j}Y} Ë6õ•™¯ÌŠœ8q¢§§—$­›”¾’TÿÀXâ+ã½×šŸp_Éʹ)|¥|(øJ¢[£¼É ìŒ !?B)@" ,4:™èË$N¾¾23î¸ã޳gÏÎÏφ122òs?÷s¿õ[¿uÿý÷ <Ú iݤ÷•ò³æ+½S:3]ƒÅlá=€6¨EJ¥òSoEç™7K%'–%‘tRb¦’U,‹‚Ȇ³#‹­Àðp©9@ްtãWJƒÄá+£êziÖ¶Ö,v.¯¿ÒÙXy”×Êw ùˆ²¾„¯›”%€fBÖ—_?NDÇ5ú²Ê@–Zë§[ß¾Ò» _™€§žz -ÈÆ4è}¥ôŠÙ¿’T£‹%-O±XÌ™Ú×ÿ$²Ioè4Ñ;HÜ?V¼Ü³Í|eøçÔ£¹“î!ŽHM™%ªÊ@³2vä°ó_»÷åñOf)¢×ÃW–·Šã+Ñ– E…æ+çr½^_)o©Îd¹“jÀWÐê‹Åø½í"[GUøÊÊÄàR³ÁW†·ãß/N8ßNŒ»_ ê”%€VàÈ‘ÃÎíaãGRMÞŽ¾2dDüÚj±o ÁWPgbõ¯t“ õo¨k’ãWrÜw\@sî+)¥¯ô¹<µ¯´…%|et‹hä âW‚&Ê@«‘NV|e0óëk¥,|%Z´¤zËô.GøJÖÕ_~ãÊåœe"*:œå|Á]/kM’¦@ñ-CY&{^4nŲ<¿J¥RÑ^˜ºzuêêUåæ·Ýv[è1Y¯JåIÒв$™hqÅf–,˲,{oëëëi®ÄÆú†ccí£¸¹’þiÂ3Û ±ßÖ1ÛNÓbÍufMïKABêAôkLè_©.'‘¸Ú'#™¯LºC2ÇÀ%"›d1Þx5®@i š§¥ÃÂüš¯-žÔW¢U @F.Æ÷ŠÍº ¡»£¤ö ÚñàuÈ™Ô*º sËW¢ˆ†yÒi}}ÝJñã¹¾¾.;Ê [B¾öéG‹SÐHVæ¶fRhDøJ}‰H­‰“1jŽ T˜ æ@Yñ[é­í+©»3ùEð•ÔÓbø}åªQÐUÝ–êao4_YuU_ ZøI¯¹¯äTŒö•ÊÑிƒ¯T]–È ž¢-ˉnŒ~Pa‚Íʈ٤oq_ID=]Ze¹8·êœBµ¾­[RZ EÿÊUQHò°sŒš*ŽR´’Ô<ð•$cSúW–£T’¦2^UäÓpÉ}¥jžžvó•Ê3Õ!*(ïŸÅV2% _ š(K€¢^à©|¥MwgNãJhmµ_ @j#µ¯œ5ztû°3]¸®¥Úc¶½¯d Å Ôì9ŽõS¸ÖHãÁu§í+Y3ß|eB_™²PÁW‚¦Êдñ¢ßx#€Mæ+™¸;ØÑ²’x}µ˜ÎW¢ @¢Àý/óªQÐÕBåQá,Ï·Ã j‰l|%%«šà+A<Ãá,¯¬”RLÞ@¾Òû•,á+I1f¥6…Šá+Ase (šd1Þx#€Íç+)ØËRJ¼¸°¦i«ÃWPkÙxš˜WD¡¢,ÏWw‡Uo_ó {‡_ šö®¿¯LëšâøÊЩxà+uÙΠ­šÅ-‡¯ H—зÛËWQwW.d‡ë«ÅBg¾€úUMî yˆ¯ìê(Åð•.BÞ‰ð(B7x³H á>øBPå+6‘÷Åöìqùƒpjûx^ÏâÉç(…t"BU9 uå,gL>GÉîA…P_¥J,9ay¥»l!ŒÊzÃY6 Ã0Üž"q–½;öÀÃõ¬h~éØç©äß/CºË¤+!ÇÞ[ã-—–pŠ ¼ƒÃ(„XZYÎÞWŠJ¶YZ]Ï©¹—ÀwÆB3 !˜Y>“ §” ýMj_Ysý_ Z(K \„´üZÍWÚ‰G;§fW•;\\Xê4åf|àLá+¨]uäy‡ŸËõꞯîBIÚPÝÕQéÁÉ,\Oa¹Î‚YòlÛ9*ûJA»åÓˆB’B‚ÊbT¸5†(¯g¶‚N±’YAJ¾Rxä_”>è–Ù’|ŒgçB¯, a×-zõ¥«&eMéS–¦)-{”¥é;œ´¬^¹Ïjay•¥éÞ,Í.ßDÝO¶dîÉ;†Z¡-à•ûíÚb_¹±±Áª¢[ö춯ô)ra8¿þ†aX–U6òBV–Bš››%Ú#„°KŸ} 'ý’ÊTQç‘•¿© $gÛçÂWfû lÎ3@­ÁÀp D„´üZÓW’=6\³ÃµÒúj‰ªò•hïA¥TyŽfÍžZh¤<÷N¡٬‚­Ù–£æÛÑÖøÒ—ÈÙÏÙW§EÍ&ÄqF¸,Å÷\³™Ÿ_(‹UgªX,†ˆãæ³§§GùíÜÜiƒTºŠ0øK×L9ZÜu|íç+çççkVHá+Aëe „6ðÛÌWQwWNŠhéßpqa¾€úTJ’­›ËõêR÷­W6©¯¯dý¼á‘~¾4àSØ„¾2 :e©;#9¨%‡F‚¯ô÷¯\XXÐ]çB¡¢r…0}1 F@Y*5ö~ÒʾÒ^®D´Tl¸±VZ_+ßXà+Ø,]—óC!µGw¡ÏWêj¯ YÏaU|%hÇç5*2 1׸%Ç,Øq¢ Tá++#2B}%c<¸ŸŽŽßPRE© é@,K€’¼Á¶¾¯$¢‘¡®©Ù]ç®®l»¾—ûJ´¤¨Þ8K+"_™xGQ{tJÝ%ÖÆ¯ô¼;… £“ÑboÈHÓ,¯'y•}ŠÊt%‘aYlUöf1³ÅÒ1¤ÚÀ²¤X–RœJ¿05C«5ØÿúÐùÁMé@ÁÉ|ˆˆ½ÓàH/Nf.ŸÏ‘ý_¹?T>Ÿ/ò•å‚óU>ŸÏÜM: ®×çLO,KŠ5S P>.ÑÓï°å‘>ÅâFù†æó¦©~Gö‰$ È8gŽöÎíS*ùE¹ýíÕ«WÓøJ«d¢%“™•?²Â;\tsù¼ó¤är9‹Ù4s†aÊ¡W…0„0‰è¥—ÎíÛ¿×4œ$a•ª€ CøZÉ’€¯$¯\Èb`¸uTÚ¿(Ï”„ê4?èe „ R½3k×·€¯´—G;Cr8{u%©¯d(KRWLĺ!á•o( ]KPÌ~”Q룫I]5EšÖÂWFžª)ÁSÕ¾(ûʵµõª"Òg2ÐBèé퉷­ûà(#T:ëá+Iã+™9d`xUVŽÑˆÕ§ Q²Â_­£_Ä5MÀ&ö•D42Ôr¦kÅõµR"_‰æ1©%ÏæzWݳiw±LPKdã+}ºc¬WV«ð• žÒ¨öE6¾2Mï9]&{{urivvÖóK¯ŸQ‡´þÑsè6÷•!w§·¯/y£•ãµWce€†ÊP535F d}+ùJ›Ý;{CÒÌO­¬¯•à+Ø,Â+Fa.×§¯…x¤o-A-¡«â´ý(£æÛá‰}šÇW¢¶)žÒM8J†¾’ËÑ.£ ½wÒð`›'ÒWz:]Ea»ùÊ çÏÇn“z÷/„ô§JÀÆr3d¨DAv –%Puã«•}%uwå»;sË«E]šå…õþ‘ÎØ¾mXª®„xÅ(¼\Ö×B<Ü·ÞÝaj9~¥”œ bvÓ9Ø"ƒ áÆ¯d»‹³ÏY/„p^Ž-ËŒ)/WžżÀäéc¥‹_é„›doÜIùôåõ¬ZoÉq*#e2‘©‹e™3íГrüJ9¾a¡àƯ”—óù¼Cз‰³læ<¯f‚äø•²²@pºÈÇEË’åe9–åFù::ryõ;rgggøAYcä-ËrrU,‰hçΗ/¿¼ººšôÔLÓTÆZåŠô•90%å¤2V(œ+P(äíå]×ïºzåŠiÁͯ-\Ûºuĉcæ<¡WE%έ! &¶?‹rŒEBøŸï²u1xûZ¸eôtá• ê{Þ½…ÂWVõhlF“€ª@/K ºÆW‹ûJ›‘¡Î4ëÖüÔªþjø|%š±T*„¥ç¡éÛˆzÀ£æ÷̦d™åyؾ¹ì{—­Ütÿ›¯ÿLÝ[ïyñf•Ä`b•³KªòL½FúÊ®B©»ÃJç+YSÅ‘¯ÄƬã¨}%ãµ$k(pšÛ¶m»råÊÚÚZøN6ÇWÚôöö®¬,ö@Dô göî½YQª}% Úø•m9Ü&lTxL…Ð?ÂW‚æÊHÔøj/_i32ØyîòFÈŲûZö*YQøÊ`Nà+7ÇWÚ'¢|Ç“–|eóúÊÝ#«!ñ+Y™²ÜX‰lÂÙÆº'¤•õ†!Ø i®q°¤°€ì]öÜ/ËW`ŒJ2Kµ¾ Óê\P)8&‡Ìî˜z9Ø¥04n(—ÏÛ} äžqa±,+_å+:úIÞÄY–Çü !ä uºeHÐø §”l#·Q¹¡…\N˲«3¤¥Àú gJ¥’³Þ,º¥Ë²,«dÝpýõ/^\Ñǵìééq²$ÅŽ9T¢rTÃ0„T®¼1UåX–;à& ®¯o8…Í0 &¶£RÎÏÍçÌ\åè†3ý‹aöF]tN_ Ì·#1??¢,ûUÓ…{;'¢òAþB¸&¦0„îyIU±ÀW‚šá@üÆW;úJbîîÊíÞÑ~ÅŠëÖÂô:|eCúJŠò•è_Ùˆ¾rÅ(ü´sG¸¯$¢‘¾umŸÊ8ÝuãÁ“u{¤85IÔÎI9?xU¾2öA9N}@5š†c¤°ÙµkW—&®åÀÀB¥Út Þ±c‡î333ÓÓ3žz0DÒ±~~pßY´¯dæ .讼ü›vÇ3è€É5Ø' ,€˜o!mê+íÿww奠–êô¶µÜX³¾²ž¾R%ƒ‚ï[ð• ç+Ãç·ÿwÃðJwG‰TEw}¥.~euè?;øJМ-…jÕÒöë® ÆµØ¢±jÄ ·Wû”gΜñûJJæ+}sµ·•¯\XX¸gâÝgøJÐv@YqÞBÚÚWÚF;»;sáW¬¸Î‹3ÅÕÅ"|%5¯ä0_È4|¥îL7ÍW^. 5Œ¯äU“çÊ7¯¯Äû6¨¶¥ÎÚlÛæ™'ÜWÖtÂ({fÅ4è33333nGKåüàúÚ/áì=­å+‰(¤‹%õ©F…‡ì0¥„¯MbY emë+íÿíÞÙ{îÒâòj1t?¼ºX,®‹Î^Ó,ý|¥S¢jë+5™ôíDÀWzT_¹"ò/w ‡>›å…á¾õä¾ÒïR¾gÂ.vK÷yBŸv°GOäG»ôTNLT.!=›BºˆþmCÖGËÃÊA+ÏWäÎ4vt¾À),¦›×¼A'åIQLÓt>šfÎYÎåròØÞœ´‰¼,ÇÐD,ËZ¸Ö]•71MÓên=î½fÅcï‹k©ë)T{—íµ;vìøÙË/¯¬®ò…ÊqÝüX–á|Ss!„óišr¹’Ê!›¦)%ˆUÙÜ‚ˆ„!•C§Lž={vxdØž>ÇUi¯·çÔñý }s¥Ý|exKŸ¯ôE¶uþkN;r¨!ŒàÝ!"#¤Ž.‹2®eUMãˆç€4@Yqemî+m$k©ó_L•î–½Fg¯ _Yq% ë+Ñ¿2 7ÝW®ˆü\®wÕ(„>›å…®Bi¤oTE7üA.¿É²ED•É2,"*O’ÁLB¶ˆplƒ!iAÉ áê£|Ò¶¶°œ™ |.2 2|³ß°;÷ŽÇEú–YÚ°*_©3¤•\éÞ÷ ð¥•OSz”eE ™9Ó™¨$gz•¥fÙ”–…OMBYf¤,%÷g)“™fN¶Ež7g3§­QBÊ[¦S–NQÞ¹sg±XôÌØcšÞl{Ò«•¥Sh%NDù\Ω°œòID9ÓäÊøKA‚íº~×¥‹—„0ëH±ÙÙ¹³g_Ý¿ß9Ž0 fÂÑ–äÖÞŠÈU´mæ+)ª‹eOOOÊâ­jV¹‡$Mãìö €ò——ˆÓ(ƒ¯tؽ³·»ÓÔìÄ“~mÑšycuÑ"øJøJùzÂWz/ï¬Ùógâ„ IDATra(¦¯¼axe÷Ȋ湋3|[3œ##Ÿ†,'M¯*$‘ãÁýiTƒÐµ•6ƃƒô ‚¸ÓéT/›¢Ôü,ì»ví Isæ…3ÓÓÓŸª°ñàm¿Ò^¸páBHË_¾ÿj |%h* ,€èF|¥/ý–¬K¿¶hÍ¿\\[´Šë|¥T¢6ËW2Çx瀯TŸiM}åŠ(\Îý´ãº¹\oÔ³™&~e-|ed¤Ô¦õ•x÷yœ¤Z§!|¥ôýÎ];CR¾pæŒ÷§Jñ3âWÑÂÂÂÅð.–½=ՉĀchwNVÎá+AMÁÀp ¢Q_©L¿{GÏòJñÜËKñ¹´¶X""3OfAzD°ýï¾WÀWnª¯¬Œ®…¯Ü,_¹"ò«¢ iÊÈg³óíxÒÛaéÊëP–vÉVCx® “„‰ÙÙ†…'–¥”y¨(±åת 1©«©H5ÜòÜ9!§ŽÜ¹ç8!ãm…ðDzô-;MÃ4*! ÓÐ…¿44cÌ10¼^Æ7XÛ7N¼¼Þ0B"„퓵Çb©Ú”ï²/½S`L6åÊ¥¼‰]IOqÉÿÉa <›r(LOP‚¼gÙ¹NÛ½{÷òÒʵk×ü)ÍÎÌœ¤¸öööš†)„rüJC0{¢XÚ;ôW8Bί«¸œú©» ÃW‚ÍÊk”ÁWjΈ‰¨»+·ûºžs//Æé^R~ÅÚ Ò†µ¾DDd‹K#OfAz¯Hâ+­ ²6YÂÚ#%øÊª|%úW’ÎWþÔ°–í7¾NÚèâuÅcÃW®ˆüªaÿô‚r=S%xeu¾²,/Ø"á)ÏÂyB”ãЕ/¯í,ËI ÉU°á†’¬²uk´(ôLÌÓF¾2$„% ŸÿÑáGÃ$âW‚fʈÓF„¯ {ùß½£W$ò®ÅáïcÖ•6|i‚]«´­äÒ²RœQ’«‘C•FÄ}‹Õ ¥Q`ôï–n?VQ}Ó™¨Ä´T¤5e…¯$¯$Òèã$ÏHÈ›Udá‰õìÄxIöÛe ˜ˆVE~Uäµg¡pÉJc¥s¥Õ¾2ò@­á+ñÚ°Á£ýê–[œ:õýmO>uêÎ×Ý94<äùqQÅ«&øJ/…B¡££#í½‹7ŸRD{„ô3Ð|%¨'P–@ãP•‹i_iÓÝ•;°g‹¿»e_©oX'ƒ«]ÃTi4—N³]f¾2ÞM4TmÉlN_÷þF'ÎÊWV:WRí}¥¬YøRûâZzµ7õB$‚ê­X%¸ªdOD>›•trh¹Ê9z–†0 !‚ë}›Èƒvu» 9"ˆñ zÖ;_†ÁnôFϨjÝ~!¤‚Þ ž'ÒÆ\[’׳ÁÁ¼¹#ÐÙûÑ_Î…gIî,û†{ƒ»G4 ÏEs‹±`惇nyþùÓ!Jë©§NÞqçíÃÃÃð•Îr¤¯ÌçóƒrKoñŠóø !œ"(×òr]úW2þád”%iªr1íä+ì9y¦æÖ–WKð•ú#6𝠦¡jK&|e¾r¸o}¤o]sÐT¾²¢#•ñ.=Q…;›‡÷,„/v:)†]9Î~|óíÅeò¤ø•B¿Ò͘÷@ŠÁ¬Þy0P¦ðž‘FY9!=Ö@Že)ÜÙ0 C8ªÈ’Í2”ev?âžõ®ê²,V]|Cï,=Óï:¦ù”¥%åÁ`C±,GÔP<ÜnèY3g*ëCX¾òé+<Ìl†<Å“VYJËÒSCB&زe˵…kž9^<±VéäÉSwÜqûðÐPðâc<¸’žÞ^9d­ðÿc†pö'+oO U¦Bí廿¯Ijп¾d‹K„‡ª\L[úJ{ewWn÷ŽžÝ×uwwš±M|et¯Ìþ,ÏW÷­î\¬‘¯Te2‹aÚcœ¸g<¸î²T9\SiǼtûá naÉbª“ÔÓ­¤º+= ‡ìïïO~òä©ééiåIÀWú, ©î?sx1Š.5˜o¾d”%T…ÀWFëªîNs÷Žž{úG ‘—¾2ò>ÖØW&Ë |eŒ÷Ãľr¸o}tç’$+ ¾²>¾R9˱kú |em™È<$NpèÐÁÈãž<õý“§NùvÐ>¾raaáäSOEúÊ|>î+6V«)UMøJP00ˆl¬ÁW*_þcí|d°cd°cy¥¸¼Zšš[ ¦¯Œ¼ð• ŸgÑ0¾r¸o£»Pìî(Eå¾Rq øJPϦAd…VÕ2FDþx7‘CdºÃŠ…£öOüd2ü€33³'OÚ{ó^{.ìöñ•.\¸xáBä ) NK{47—£vˆxñ+Ýûê nÈÃ3/çð• ^@Yá5øJå˲ww庻rew¹²±¼Z²g鯌¼ð• ŸgQo_9Ü·AD#ýDê´ëâ+‰Y ôèT”C= ß„?“R,K!eRŠGY>‚þéìœyÌ‚åMïNw"ü4cÍ^É€P­¡&§, oüJoI-HS hãWê§âúšø‘®çêYÞÒâ\üË+DxÔ?Q™¼*PäL÷1”£I–J%Y3•9p çܱã–ßÞÍ’î×Av‹î4P†!onš9ç¬s¹œ³Ó0%KÈž}J§´eË–·˜øÉQØ¥˜™ž=5ýý½{oÞ»w/µ¯\XX¸páBdçJ²§Ü”'ærçÏ1„·–¤K”+7x®Â^föcÏ´>NŒßÕ|%¨#P–@ˆ[¯T¾üW¿óîN£»³ÃùxuvMN?5·_™½¯ŒsYà+ÕÇJë+‡¥)¿»;¬îKÓ§¯þ¾Rs1úQF÷µäµŠº $“£°%ô•Ñg @ Ú æÍ Q?W úûûFŒ–­eh}æÌÙ3gÎÞ|óÍ{÷Þìß ùÊ8‘+ml_é»Ú‚D¶…¬Ê“÷¤d¡ _ ê ”%£e_™…¯ ºÜ:Ø!§ß:XHt¹bÍ•¤Îdˆ«âô÷‘9ÍýŠt(–Ž\•NUêäîrr6«Ú†u^L}_â\.Nññ+AÃe Än?ÂWÂWÂWV{ øJ}bøÊ¸Å¾Ñ}%hñ¦@ê¾”}ç8å!BôSj)žSëééÙýŠÝ‹‹‹ËË+ÓSÓÉÒè¾2ŸÏ {xü#ЍÙÙÓ—vÿ¿4P–@¼¶9|%|%|eµ‚¯Ô'†¯Œ[ìá+A èb+‡¼¬|krÉt—9lö\»÷®-ÅäМÞ8ª®ÍòX,ÇÛ´…—o“M'?9Ó5 †0,Ãrvk˜•øŒ%cKÿ–-ý[v\wÝÒÒÒµkצbºËö•…B¡££Ãþ/…Äœ5 ç,̜霓!Ų4 C^†»¹½IyÙ‰_I,¯7„á ‹éF®Ôå ¾4P–@hS-ø _ _ _™$ |¥>1|eÜb_ º­`%¡yÔ„ g*›°é}ÊÿÂ[4í©x8à}›k¾rÖ—çcqd¦$È„œ@ÿ;~ ûçIg¸zzz::;F¶Ž,---/¯°eÍÌ̆^Íš¶í’¢P(::ˆ¨<µN‚2÷œÒÍŽ‘#ÿý…¯”% oª)Úëð•ð•ð• ÒÀWêÃWÆ-ö›ê+ñ’¶8u æˆ4µŸ±'ƒ™1rÉÌÝÝÝÝÝÝ¥bihhÈ^)w½d©:òd‰µûOšóø©®”µ.!›ã+“î¾4P–@h›¾¾¾²ÚÁWêÃWÆ-ö ä+E¬Ùu@{µ2ñ•ñÚ#úïSOÿÅ”/1®•:Íàà€³yö!–ºl2{úoÊ{*÷BmâæfêP$ ` _ š(K@ÿ~_ _ _Yíà+õ‰á+ã{ô¯ ÝVHë+‰r!÷Ý5äX„iîøJÙX ï˜o÷£ð·Ÿ !Ü>WeÃ]iÊÁ5 ¥RÉqlÃb7®¥eZÎs'J.Åÿ¿½»{’ã:ï;~ΙYØYî ´/xÝ],d Puåâ‹,J¤*¡ãP®Š9w®Äö¥S•?"•„ºQмÉM.¹\@J•2IÛå*%XÃH˜$^„…ªŠI»Ó'³ÛýœÞ>ݧçm{f¾ŸÚ";Ý==½½§»Ûç9É[lšø3YfN§6ÒZ#ÚËžýkü@·?ˆÖNÝOãÔ²Ôñ쵚,êÖ²ÔbZÌ— µÊÖëÉ®ŽëZª¸~¥U²¿ÿÖ[hãn{F𼈓¼•aØÄÎ^ø;òÊr+'¯ Úÿä•ù?—ÝE^I^Y´»“Wr+Ž áy¥–”øjýÏhmt+gÚúÒÆ´¾Z/úi“÷êÖ,rù&Jogªõßøk§T6ç~ÊöŸ¯,µ’Aø‘—=B _ïø Ü]y¾’FÝCd E¯¼òr™ë-òJòJòÊóWúg&¯ =ìw+¯¼v{œS$ uÞö%Ó­ÿèŒõvËoÝqrtÆ[ä= j­ÊÊ×” ¼tîGÐÉäŒ:µªíe|y–:ãçdÓQ·EÚªÜȲ0˜+û§ü€©eÝçøddéÌ/%3Çx,­E¶_Ú*hÚš_›Ö"­ñ”œÈÒ˜Ô[´–’rk•÷X7y%µ,£èèÑ£¾—ξu±ãTˆ¼²Äî"¯$¯$¯ôüŒÈ+‹vcן¯ôß–ÏÌL‡œ^‡XúƒwÞY[uÜÛV#|²wKïK—sÛÝwì<:ÜY#•¼È0Š´Ö9©%y%y%ye©yÈ+ý3“W†öUÈ+Ïû±ôõ wƒVbÒ3±æ°޼ÒI^¹ÛÜû¼²ü ä•tt Œ¨••£|ðAæKg/\|õ•gË„WÙä•iy%ye{ƒ"¯,ÞòʰO2öÐFjÓ…G;Î+•µùg†SyófÒZǵû€”,éèüÒŠJkEÉo¥Ó—\Ô¯ôEÿ­ýœÌXvgj1ƒI½âöµ·V¹]³S[åôì®9=»µŽ;†kcܲ˜ñ";êWæïöt•Ѱ£Ž¼ÕGd Q+GþØóÒÕë·”z–¼Ò¼2dÿ“Wæÿ\Bvy%yeÑnìYðëw&|§ ÆÞA'©MféI+[~g4›¬vTï<°Ý·Ðy(mF^™®‘™ÿ°[`ÓÉÔ"m}«´ÑÆ&ÃÈ„55ÉIsD:tÐ÷ÒåË—ÛImlçOº¾EÁfØŽËpeS½°º±¯zÞ}»_g3ØtùJž¯Äð ² ƒöÊ„½ôÒ×}/]½~«L„A^©Âwy%y%y¥çgD^Y´{9ÞÎ…Ÿ,øÎÓSSÆè̯4•ñÕáIò—ýtèСsW$¯,Xèä•«¼¼²lqØòJÙšbôÐ10ºrú†Ÿ}ëÒ«¯¯Ì·0?'ÿéŽSÖÑeVÔ²Œº••£êÇÞW_ãoþ×?$¯$¯ ÈPTQn¢È+Û=yeñW–Ï+¯Ýnä?b93=ÍY±{÷îÍÍÍå?E±OXKûº`ùâzÂ)]4ƒ3“m[”oׯàØÈʵ9¥›ÙŒ+afíZk#ñkmÛÛÛ…äšµÖI9KÏv†Ù»T~?µx2­ý«Õy‹Ö¯Tª½C"àjÈãÆ´èž²Œºc++/¾ð|Î ¯¿q®ð†„¼2 M ¯$¯lï`Pä•Å@^Y>¯TJý÷|1§ñçK¤Ü»wÏët'¯Ì›¡ˆ[¥:|ʳ˜í}ßù®wßîÃ3¡Å›¤àùÊœ6ã´!(‹È0Bœr`âëk_{1g©«×ïlu'¯ ÊP2/[É+É+Û;yeñW¶•W~ïܱœ–zzê _Ø'ÊTÖ2¿j5ùZMÖ³”g SVܳgÎø^ºrå²ÿôT.l*Ÿ=fEígI¡«¨H^Y¼‘½§ï%¯ÌyÄr¢Ñp§D1"K”RêÅ_Èyõõ7~xõÚí¢‹HòJòJòÊÂÝE^I^Y´û’Wž¿´3J¸Rjy‘G,šÓ¼>2yeB´ æ•¶k•óÞÑ–ûP•}¾2gì‰Æ„VIKí!S·5L†µ,PJ©¯½øÂ‡Þ¼yó¦o†×ßüáüão=yì€ç’‘¼’¼’¼²pw‘W’WíÆ¾ä•ù%,•RGžššäÌ8²ÎœùÊ¥K?É|éòåË'Ožôö™­á€å•©TH—t¤Ã#­”UZi罌SÒ1Š¢øŸ™Ù“UÖXã¼µÍþ NùKOãÐ%ãµlo°v7Þ©wi²µÔ‡M£´[Å(å÷eýÊx†xž Ö¯ŒWøà?²œhж ,ž²`Ë×r´TJ½þæyÏmy%y%yeáî"¯$¯,ÚýÊ+óKXNNN.9Ì9q”8pÀ÷’Ó7œç+KÌ¡:Þ}‚¼÷ï8ãƒÇ+ÌéÞhW¢D–€Q‘ÓÍÄhm´^9º\œZ¾ñÃV]KòÊ€4¼’¼²½ƒA‘WoyeoòJ¥Ôòâcâ3ÃÖWmLj1­ îjA©CþÈRŃðW–˜Cu¼+zŸWçï¨Jÿø*>ÞNn¯ðÆÖµøvÏðkYÒzŽ"KÀPßxäÄ”Y^úúoÉ}¸æê;¯¿yþ®ß.Ä”N””çæ?󆿃•“WíòÊüŸKÈî"¯$¯,Ú•É+—ÌÌLï<;¸#ë$ÜÑx‚MâË>žúC¯ Z~·»ýj­sá¹|ù2yeÛ W[»‚¼²_;ª³¼2§Wøý [c_Êq0µo\Ì€¢–Y¿¶ùí*Mî ¢–%Žïüþïýçÿòßòçùî›çÿô¿õä±…òùWa*¡<7ÿ™7ü¬œ¼2hÿ“Wæÿ\Bvy%yeÑnì×x;ùõ+•RSS“ËKŒº¥”:xàÀEÏKkk÷îÝ»777ç}†%¯´Vç/`·’ ÌÅS¥-ÏK±6{³}+w\Îìþ˜?ñç¬RâÎIDATW˜S˜2~ÇV4ç][ª¨h…ëW¶Œ´…§,Hû·¿÷o çùî›çϾõÓ¢˜C• h”çæ?󆿃•“WíòÊüŸKÈî"¯$¯,Ú}É+¿wîXa^9ùħž:É-È©hyYV´L·†Ã“Wöz3v¥dÙGéMêÂ0`ÏWª¢G,÷/ÌÓ¤ =D–€Q¤ýÝIŒ1ËËKðß/\ɹ·ßýóä•ä•ä•䕞ŸyeÑnì}^yívãOÿÇ3×ïL6é§ž>¹£'x6o_ðZª¬ev¿c ŠƒþÈrmmm«¢eÙh‰¼²Ävöü-úŸW¶ñ¡ªŸWªÜG,•R´'h‘%`Øè 8*UØ2þjݧ.//ÿ»?øNá{ýã»òŸþçÙ·ÞϽâ#¯$¯T"òʰ#¼2ówmDóÊï;VX¼²%Î+k0¢à¥8­èò8•ïîeDëë«_}6g®wÞ}§t´D^Y©úÿ|eª|À6J^™ûˆå‚;Ùöo˜q.¿KqÏØg´’#ˆÈ€lKK‹¿ûÚk!sž{ûý×߸põúv%å¹ùϼáï`åä•AûŸ¼2ÿç²»È+É+‹vcóÊó—æ®TJzúäôÔg=ìôågžÉyUv'¯,³.ñrFfúEc³(íä^é/6ÆK™¿3ýBŽ1SøÐõ@ä•ëëë|÷ˆå¼¿ê+Pˆáwð:|øÐï¾öÚŸýùŸÎù7î~÷Í·ceáÕ—O?yl™TBynþ3oøUÉôGyò”ÌõW’W’WfþÞ‘WR^yþÒü…Ÿ,„·óO?u‚¼>_þò3?}ÿ}ß«W®\™››››‘¼R«­QcòWì}Î&»Â;VÖn]cå·µÜH¹¸Õ6k5Ñ8Ûé¼™­µJ»Jg|öÌ!ìåžw>—*ø\©A~Ú9$:Î+UQ—ð¹ÙYštBw^B€]çbÒÚøŸQEÑÖ}o³ÙŒšÍx¶Ï>Œ§?ýôÓxúŸÿß?+¥>þø“ÿõýï‡oÌo¬,ÏÜ#Þ6&’é'&žˆ§ÇöŒíÙ³gkzllllkñºX­Rjl{žÖ»ÄÓÎxÊnxA'Èž^(¥676âéGbúáÇ?ýéû9©¥RêÛßþvQ.4$ÏWÚ¢ò×PØ-:½´‘eûw§×²oei½+ôF–þ·h-þ¡ä ä]Ê+s±?º´Ô*›!ÚØ­éºh$[3'm¬˜m¯l¢k¢énõÈlKiH‘%`ÈïI’»ÞTdEòž$žþì³ÏâéVd©”ú§>úë¿þ›>þ¸ÔVme—ß<p…H^I^™9yeÈH^™ù»6yåµÛk·Æ¯ÝnöMMM>sêéÖÍm|ß[¯×ÝÈ2É 1ÜBËa%ddY¯×ã»ñZ*²wéòûé¤Üi÷ëò@)µ¹¹O?zô(™~øP)õƒóçïܹë{‹ÙÙÙ^xÁŸ {^¹ýZá"oƒæÙ ´‘¥"ËvòJ¥r"K¹ÂìÈ2÷-´Ö¥BØVdY‘¼r}}ýÒ¥‹9K]^šh4dd™jcÝÈ2iWÇId¹÷ñÇãéšXܸ‘eMD–¾Æ“†tYF垤“Ȳuó¿¿ÿg·nßno ÿå7O)¥ž\™òØ‚÷Ê‘¼’¼’¼²ôH^™ù»6ÌyåùK[•Ñ.\šo¯A^Z<²´xdç½.‘åh^¨¢Èòö;?<!ç]|©åHä•­ÏiCféhKº^d3(ëlÍ¢ýŸJÙ‚ßÓ²á Ê%¯lŒ¯]n5€D–hµ,õ;ÿê·?¹uëÿüÅÙ6–=÷öÏ•RçØ‰ÀH÷E5vGuLN>±xøð¾}3ì „;°ÿþý 9Z®­­½óÎ;©ÔrTòÊV“çÖdܱ†Üú•¾¾Õî y“Uù£Ù¤+`*[<üÊ[çV‡qåOÄlÀ²v”ò×┼R)ÕÊ+1b8%:xðOþè?9á­ü·kõå¼²l88LyeÎ=…kHmÕΧ,(¯ò‹ÇÜ66î îmcÇ÷î•kˆ§Ã랎áZë¶”†tñ”%í8xðÀýû?|öÌWØPYS““§ž>yúÔSq^ ´gaaá›/}=žÔ³–™x¾²Ä6ôàaÆž³m,1<ÏW¶F §¹@·PË€ö}õÙ3§OŸºxñÒÏ~þ öTÇäääÒâáé©)vºeaaaa~þî/™3ÏÚÚÚ»ï¼{üÄñ¹¹¹¯}^)‹Zæ‹ §ëõ+•¿L¤ Î7ó¶*,t¶ÊTÕ¬H^yãÆ>¸Q¸ìüì, ºˆÈ€N>}êôéSwïÞýÙÏ~‘'èµ#‡OMNNMMC7@tÙ7¿ñÒÛ?úqajùÞ»ï=÷üs©Ôr„ž¯ìüñÉnoC‚¿.mR*¯,Ze%òÊ‹/>x°^¸ìò⢬›tŽZ–€a#Om¾éT-ËÍÍÍxZ®z(êZnllll϶±±±¹!¦7“ºWŸ~úÙû?ûùÝ»wÉ. Ÿ–—gf¦÷Í$£×Œ¨ 9–<«‘ªe)ŠN&…ÒÆDaJ¥ÔÞ½Imµ½ã¢æš¨¿VsëWÊšk²¶š¬e)Ëԣ[”]ëú%Aêû¾Z–¾K‚ææææölo½ý£»wï¾ûìììó/<¿ýŽ…ÛÚæg)±ŠŠä•aXvqª“Wª¢ÑÕ»¼Ùç•!Á[V–—ñÌ66.:)‹QæÔ²Ü+¦sjYÆí*µ,‡‘%`˜ïOv%²Œ§?úø£{÷Ö~yïÞ½{kü\ ë–h¥§§§Z¥*µÖZ"e^VYä’¨šðÔR)µººººš\Ž^^ÙÁ'-· }Ê+Kî·*ç•eÃJE^‰^Ÿú¹Õ ™^`©ydÇð¤—÷£T°¸cøæf¼ÈæÆ†\üóÏE¿ò‡Ÿ‹iQ«ÙŒ;—É.ê©îêþé€È’ó;€A½AÑžoË>ÔÉ?µÖ™ÓÊí‚-§÷ìÙOû:†×ë5¹Èã²Îšè¨ø˜Ûi1îÿhŒ‘Ó²£¢ÜB_FEDÛÇK‚Ôl›ò2@L7›ÍäÔ¿¹Ïöó_üâï/_)µU³³³³³³ÇOœ ¯,óI ¶¼2øÇ™^áúúúúúzÈ€àRc||aa>Î+eƒé-¾!ªgŒÕÇÆD½Ž½žŽá=ö˜¯•ÃCÚUÕAÄS–€ÑâÍ/<ÙGEñ?e$ýÔÉ“3ûö½÷Þ_…¿õÚÚÚÚÚÚêêªRêøñãJ©Ìb—ä•áÈ+ƒœ[Ö×µ¾­×?º¼$cJùËÿv(7gt§œÍÔÄâž¿ñhÑ_+åûc† ‘%mš›}í_ÿλキ¶v¿ì²­à²õß–Vˆ©‚úKtœôõ~ŽTµë?Û‡Í.ý»¿£Ú(SŽ.-6 ~ñÑD–täùçž»²ºººú®GÆ—@¥4ÆÇççfÉ+Ñ7D–€Q¤µSÍÙWÇ×ÉŦ{¦$³5£â1s:®e)W«2߆£­ÓÎ÷»X˲ׯt¦ëNi61]óô…”µÕŒ1r«øQìQç\ÄçYcŒÖµZmûûO?õÔWŽýÍßþíýû÷Ù“2+ËËFÃwÍœê î´±ÙÃk¾Kníë.ÚUE»:ˆ,Ã|³‘Jñâ—¬µÞK.Ïå—\\+e’{QcL3žÇSJÍf3ÚŽ,›M'²´bñ8ÖTD–F¾=Œ,Ý[h9„˜NE–âºî‹,Åhòûι1ñDû‡†Ðã ¡×—©½òWL9b1ÆÆ‡;’O­V{î7smmmõêU‚K ‡…ùùý óÛ­Yöu²·a¬×?Õê;Û^åb 5v™ö´«!-‘%]3;;ûÜìì'·n]¿q£+Õ]1?7·ažà»…È€.Û·ofß¾gõ«õõõõë7n°C0(ããFca~Ž]ÝEd E9µ,å´ì$žzI‰b:Ú$ß‹’΃¾žÚQ³ÙlÆÁ›N-KYòrÓÓß<Êî{n­êÊ—P¶Ú׋JÉ´1ZA«;“ÆylO2í«³V¯Õku§o£\$ó4‘ª_ÉsIhÙ·ofß¾™#‡}xóæƒ¿~ðë_³OPYós³­¼’ U@d Å[ßT-Kg6_-KO1²Té4_L)—m6›QHdY+Y’WÚv;$²ÔÆiC"KcŒohˆ 2mb:§ÎZH-KTäHsFÛóTÄ–§þôe€ç­µþÒ—¾Ôš^»ÿþýûaÿ wÆÖôD£111Á>A•Y°;iM4›ÍCÆß¢¤³E³):^DòÙÏìÎt¼€ÄƒÞPD–ø¯êdoÏlZë(*îµ-Õj5Ñ1<ŠïI"k媢Zöý‰µÎ[¤ŠYÀ0µÈI#ìÜf‹ÎàNF#‹ û:†×Çœ^ÞqOÃZ­wB4nÇp9-{&ÊÓ„ìœH@@‡ˆ,Ã}·ëÔ¯ ¹Ì¬’³µ”˜ðÕ²trCÏ-kEv;‚Ì©e5}‘¥3m˹C²   t©ÙÎÍWsMù#K9®Ž¬eiŒI"KcŒ'²LÕV“›T¶~%if. r^J]hÏ™4¤¤µaúæf3~j²Ùl&¹Œ"ù—ËM9 _$§£Ì q. 8×ÃÓÞj•Ý4ÉCÔW¿R»Õ$e+‹QŽí‘g\³2]/XÔvÊ_Šï›T½`þ4b »@uY¨"KB-KÀ¨,b%¥Š”ٸƙM†½±Öúê^Õ²>ÉtEñ"r:µ*9<¨õ®ò²d¨PCÐV;ßWÚM,à).,§sÆÒ1¢d[¼*ã–l ™VÔ¯º#0õóµñKZ›íó¬1F—ô®JuÍfRƺÙlÆ#òÉï+¥š²~¥¸$h¦úW¾Ë. FòHhWek¦‹Þ¸´¥¬kiÜ6¶VÅ(÷d_ô¦ëgM§ñ5×´±#ˆ§,T‘%€ !²P!Ô²Œ¢T™Yò)§NæK9µ,eEªz½.¿o=•§ä?åâN-KÏæQ¿Àp·ÕÎKr&QËÒWˆP~_GÓžÅsV•³…ÔV‘£QÖ¯4qͿֹ[]Qªè¤XU\›Rk™hçj•R&W²æ`P-Kçz „‘ÿÊ|Òñ}_X‰K€µà`€½à`€½à`€½à`€½à`€½à`€½ŒPz|ß§ªÄq*y@>„ Bƒ ö‚ƒ ö‚ƒ öâ U¶R«4PÉȃ¼  HBÖhƒ ö‚ƒ ö‚ƒ öÒ<˜Õ| ’¹”¥Få'p!Ùò Èò4 d5…1˜`/8˜`/8˜`/8˜`/5[ɧÀÒÚùÅIY Öé„•‰Ÿ ,K4 ¯€| …·œö7¼H4 ©ÏYÂzic0À^p0À^p0À^œF&(É󥪩²®@$¤ü´)'vªœ˜È+ Þ A2ka &Ø &Ø &Ø‹¥y0MJUM–ù êŠ,-`NT’Eþ” ’WÒ¼´˜È+ y ÙùHÛ*–$ š}-S}DyŠSߥƒ ö‚ƒ ö‚ƒ ö2È<˜åeÚJÜmyyyii9õ'FÏ[zpK€<´çAI( Ë{¢I¡’6‘Yya*ÈÓTy=ýü3ë#ò Ð`¦&'·OM"VÉGž0*Õ!ËËËKË Ûv_{^¤ •¯eyò`êcÂ4Ø8¨±æéÓÏ !––——€LìÝó bjjrûÔ”¹²•‚fX®³ØCN:-dãÀ@AÚ»ù¸|”ä`ž:ý¼ð}b.h*S““SS“é´L¡k%‰TÅ á`vÈÒÒòÒòrà]@IÞ_¿ AJáòzêôóKKËËËËü 3{÷<Øÿaò1hi0Ù˜ Ð2óv¿l‘ÂÁ¬Yˆ…ˆTL+“t %¯—PRø‡|X+ šÝˆ¹d-3²2q0 ¥j³Ø!KK˧N?Op0@ñÞóàŒ&þ4ÔTÇ@é Õ½¤T2vªxIòzêôóĨP¶|LNNNMnKéÅåÀù°3,jÜ%1@"Á¨ŽH4dû™rÿLÑVIaQÅ¡P]LtÀ|pFVî O•;¥ Aè!%Iõe»lyÅ»€*™œÜöõ'¾–ª×~ò>®[¥|ˆ†:˜Ä\©¢¡ “4¨D*çpNÌêB,t FʃYyÅ»€ZȇÀÁ,Z/PO<ñUͼrÌüÔÌÁ<~â?££6óõ'¾,Øg®¯s òJ˜ ~8˜•ICïnÄ\Ù˜œÜ¶çÁåh¨¯á`¦N;ÌÞÝ–––OœüÏüj¡ÜO|í« ’¬Ià’´[\ª N¥ A³IxI¦§UòÊÐK¨—|³ôa®ì`æ1èc•É1@Qìypff¦­iöSÇ8ò5šX\è ‚UAXT3çcÀ}û6¦§·ï˜žæç á¹S§×˜¥¥¥ìmïW¿ò»““ÛL̤· L9LÍ ÜÃì`f–×ééíÓÓÓBˆGÖ@‘Ì ²mÛÖ™v[#†í¼¡ƒ™'ðkžƒIÌPT4´mÛÖ¯}õ+y–›S:˜êSÅ¢¾¯ Ìb55óÔ¶}ûF;ŠRñlú=ÓþôƒòãGL›ä5Ã(´RñÖÒÒÒÒò©µPМ¯~åw#Ë”ã`–&Ä\&Z–-úÚW~w2”3ÌÜXí`fˆ¯Q€òÈ`enÛ¶õ‰¯~E¯µÓnyžÞ¾oßÃh+ä ÿN:J>"ÏÀp0Ë‘ƒb.óh(íc¹®‰‰ƒ™{Ì´RºoßÃÌe¨&M5¦¦kbâ`ÖM^ñ.`€±_¢|Ì‚ä Ø æÈ ¥z,wë™fnªp0Ë~ˆŽ „cÇO*÷Ö­[~÷_<®ÒQ¥S©Ž-åÃݨœ« P¯—›Yª¼â]€òá¨Ûy¹ V­Es6å…òȇÈdt%&r¸1@fRù˜²œex~¦Ò>³é*tPdZð§¨p)'6:˜æRŠŽÔE¹cQ(¦ÍòzàÀ“x—`§|à`f“ƒÞ}ˆ¹òc>· ”3ÌÌXç`J)cCj§ÜrŠƒi­¼<°Ÿ[¬•Ìlrлt1@QÎ-øôo}êÓŸþ-ÌÌØå`J)lÃp4M…â`Z(¯ ½€ÈÇñã'ûîö/ÿò¶m[fV9ÈQ„˜   9›œÜ†ƒ™²Ìlsþ·?ù÷ÄWõÅäñã–-›¿üØ—”©©Õ1dĵTl«i:ff¨=áhòŠ} ÍǾôÏ·nÝiÛò‘A2 e¢ùÈ  *iP½NÌP†&æ—ûç[·nM¥8Žã$¾¥ —äà Â%‘{» B!מ‹}üñ@½9x`ÿôôvý>««—/]Z¥®*ãÔéçõ;LOoÿã?úCä,—§¾ÿ4EÌ`-;¦§ÿøþ°¯œ}ï)ä,#­o}ë[–Héòò²>¾züñÇRËùØÇ˜žÞþÊ+¯jö9·¸¸uË–Ûo¿=ø¯áÌ´3,„ÙÌ S!,œQ®âÔéç_}õU½¼’ø,‘¥¥¥wß}W³Ï¥ÕÕÝ»v™ŒÁÌ3)Ïüð2ä£T!æ°BÎ.­îÞ½«²ÇäT‚1˜†,--÷•Òƒö#¥µ`ÇôôOê÷ùñO^¤¢*×Óژؗ`}Gb®®^þÉ‹/QQÄ\5—³U¢¡ 93[îK!Äÿùý™þ¨ÇìÎ;ïäjÔ… ÑÖ$5ûõ¯íu:žß÷ûn ß÷=ïÖŸïûBø¾ÊÍ­mß¿µåû¾ïÇž ª^—àsÈòäõðìÿȽ VÑwèÊêêêÄøøèè¨^>„É’!µù·T#I>"¶|d‰œ‹!dXÌ€˜ À*9ß8úþ÷ I¤:ž×Y#&d‘@híÈb”¸í!€H‘̰g±ÓÁÁ웟‹<,uä‘}ëŸ=¾ôòOWW/SQ”Wj ,¤ïЕ—^~™Z"æ°_Îô;¼üòO©¥T ØÁì;Á ) 䆊}ûÖ¼{ùò€! ö£1pùò•ÕËÈY ì`žê—Ÿ )¨5úgD¡È+@/}ó)ó Qh€œ=ûì¨%sªX‹\5»~iiùôóÏk¤”å€~iò_ÿú×÷|ðn? !e%‹'sq9±KwǪDfr.¡^tÏpÍY“}ÊXy€sçwj2ˆýú×ÿŸçû›&&|B L~½+’|8Òö­ôa)ó’–sÍ–Q°V΄žçMLLÜÊÉ,’…Ì—ÞÒ[²é´Ïq|!‚?C LâÜ<˜ú‡úÉ#PvLOkæ’_¾|åÒ¥Õ0eµ×éÈ©¬½à•èë½Ëýtÿnå¶Nˆe#Ñ(Wü_¯È+4ƒ>£øW/w$â2a"²‚Hò—’šË¢`³œ½üÓWºZ¦¦#ýiA5Aô,sZ—Øg`æÒÒòòò²FJ™Ë0$²ýÓW_¥Š D#¯L€z¡ñ×®\½zåÊUªˆ˜ À~ôsÉú Ñs0õáa ÀðD¡W®^¾r…*ª@^™*õâ‘}kFñóŒ˜  è'¥½‚œ™1’óøÌcMõ¹0µãè‘9ù¿‡ÏR' “N:­zwuõòøÆBÇqœ5eqÇ—óIû«²’i¶]×M<•,dªtcAÚÍÄÝ2È¥Éá™åU³Ú,òZ#I=:×UÕC³³%©ê};wÉÿ=»x®¨ dß¾‡?™øÖ•+WWW/OLŒ !×uä–SÚVʇ$ ŽYNd¹qÎ)9eÅD2|ß'æ¢?ûåL±zùòÄø¸¯ÈT)Ç2Nú$þžç…Û®ãxᩊ ^D%§È•ãa`5l €þ²­21_ýÙk÷äÃa´ê«lGyÉO–gI’eQt]WjúéF(c)žÂYZZnž¼.ÌÏ/Ì/ô¾žShb¡W@{¦Ýž™1?±«i¤]òýÓ{ßêï.ÐŒ[Qµo«—/oÜx—Âñ}G’ŒXH—,ЍO#ªL• V>N?ÿCb. ú¨…œ½òÊ«þÎ#²ƒéH2帒H9ŽìzÒI\9Æ‘GoH‚å áÊ–¢B. ´ d0¦&ĪÝÃÀØ„v»}ìä‰Tg8øäþ……H7í¸†üg’è%&´}âö­H=( ý0ÌWöÚGïÿµ”‡SM€¹0¿ØzçñƒTŠpHÌêÌ9̆DÚ%!ß?½÷­þî=ÓÓÓªðág¯½Žv CÌ¥ ¾ ûó´Ø@,\ÊFD7­¤.h†a^½vµ/ƒq05Ój÷0ðÐì¬ü³Œ9‰F±YÏ! óó©ú¸±3šEž‹ˆ™‚Š=:7×n·&ð]Ÿ ò¤‹¬•m•‰yõ*²]¢¼j2ïÔ÷wwìdV³’N<…þØ•«W7MLPKÍŽ¹röçÖÿÐì¬å¢c#¸¾ Ð$ô‹§ýìµ×?úÑû©% e9˜šÙš]͈¯Rù óóI/.ä<Þû9¸ÿ@†¶0$0¦(ôêµkNG8N8OÁÎã“ç5t³ÈÝèL éÐÔIÍ4‡WÔ,ƒ¼Ûä iøÝúÁÁ¾¨š`W®\Ýx×]rå YL”Õ,r•|uZ±–ëömŸ å#C³ß—SM¹²÷Ùæ„Í£b#¸¾ 04 ‘IA üÖšOÌ# ¬áŒN8øä~~YP1š!ð¯¿qVVŸŽ ]\”¬q¾6êþY `ùòòÕ[5!Òn·åyÜ óóB˜zˆÉc0Ry jÑO·W…¸yæ-€Ð¦3»ööÛÁ’²ÍŠ M-˜a&0Qü¾ÝªÌÉyÚ3mªº<4aÂÛo_§~’b®å†Å\ª@,Uþ☈ܷs×±ÇäÕFCÊõ|®]»6>N4¤¤jsy¹Û333•ÿ¨ØÙ<$# föxF;XõÐáÙ…ùù£Gæb5¼°°pðÉýL'ÈŒf"ùµ·ß¦~Êè$5éëÄž¦}ª$?ö‹ª/¬’×TÅ—9z¤§g•ijˡó̉)ÍÌ»k×xax`¶gf4¿µð­£GæzŸO܆AÍU)¶^VæåwÚ3íCÑ¡]<ê[뜫Çs\{SCÕæ’zf…³-æâ’‰ j†O¦Âì=B±]¢c'gæçî?©ö¬É×@ãh2£M‚Ù¨)äAÛF¡ig-Èû:<kÞ ¦0„¿'æZ¦dž"yt‚ÀÄ(­ÏÆÏJÑ9ßN%dõ§(Ôy fOŒdPE–™k·cQVÚ3¨ ù¯oïÈÖTÙN †fÈoÞSó›îß 7oÜèܼyëO'ÿ)RjÊ)cb Ѭ V/ ÏýS:ó‰á±=QL€z¡ :ŽF>n¼w£ûÑŒ›áßÍP;Œå£ãy៧ÃD–ò]ê: õIâ$˜í™™c'O;q¼÷-h@ehRy¼þÆÙ®xIêuóÆP±nD5í¦ü'Óé„&Y4}Ï|ø¾P¯(0ت«| æR3ŸfK…™Ë63Óž™ sµ*‰1ŠâÐáÙXTÙNÀœs‹çïÛ½‹z¨&æoF›|tnÎdrS|æ@“®ôx©í2ìÔ…ùùØó6Kæ5÷šÎ%Õ@c Æúrýú;TB$æb ¦&p›™9»xîà“ûcy¢ 24©0Aƒ-y0ë>Œ6[*Ly·öL;f˜ÌSŽ}Pª¾lØ$f„94;[lÏ8ñ³‚ÄÛuÉ7©2ŽÁ ‚Þ´âEՆ겱zsr•z£jN&†H¥©î“ðRÖ=veêääÐlÔÄ<ÒßÄŒy@~Ú1C0ÃÏð葹äe%æn5;‡Ïæÿi«¦I†­Jæ„Vù[6åªs¥t?Kþ[®Ø3¼«±× ù®¿ƒƒ‹¹. ¿zŽ<31Óæ‰ ,ªß© ÝïA²Œè¯²ïRj?¼Ê¼4µÆ4©0AÈ=ׯÖõ˜!f|ødt ¦0N…/F¾Ø¦« k=㜠ù¬………………£ssƒ ¥RÖíLfײ"ÕFæVYƒk;ÖcÓ/OËåœj-‹<Ç–z£ê/GxE‚OIìoiÎûÖ½¨ê¡ïWîv§jíïhVfF8}úù¡ú¾†a怹0¿ è!‘¢­îM©œXÂ`Ÿ<3*zG%6#AËVe­oråV½Ýn7cTì+§Ò©2jL+hή5¬ù†<æ*\Jb-óÁýúÞð&Íl¬ûmÒ½Wõƒ[÷-’ºÁ%r Ñ_eߥì~x•1¸=Ø ö-Oâª_çÉ}ûíë7ÞEHê<˜fiiRaj’`†¿Ø¾½Û¾ÅHÔ€ƒû˜ËÀÁ'÷&åÌüYÁ§4øg¶0?ßÎ]&¡HÁš§u oÃËztnî¾»rº±PÞº0?ðÉý†—#¼(A½•á›åîO·XŽ×é$g+‹ä+»¡Ø¼©LjËkM^&gy‰üä¾Ì q‰‡”$¯ûš›ï,æBêùGÃåoÐRµ9™µÆüØj„ [“{ßÎ]™û è3T\cÁ'¦ ‰$»Œ+ˆôFtŸÄ”aºÔÉIq+G˜Ý¿”y0aP\ﭾݪ ¿” {_e¼3¨B–ýUó]Êî‡Wƒ7ƒêkì葹 fïýÐ2ÖÀÐŒˆ¿zõêZLUÞÿˆvÉáûD7«’;«ã }¶å¾—öuC\EI`Ê¥xbI0{ã±´ußÁicYÒ6⨦š˜i× ºž6Ô±?x+ïF †AY¤5ì"晀`H|X´öz6[µ}YA›c2Æs íyb“Ûn·ÍΆ‰˜á\U£úË´4XJ„ñzªY~àuˆw2ÒÂèÏü#Jí‡Wƒ f«±T㊂iÙ¨ty³'}¤M…K‚©ú-i†UÆ~c}'­÷vmUsc{N=2wìdŠÓºÑÆ4,vbàÚ;ã Èî_,È,*6Ó8v?¢ñIÙ7ªêÎŒ9W—j~Dì"öõhê…*—Ù;ïümÓÈÓ,æ`&þºÛ4Mš§Ì²›8ÁM“Þ«xì)•bž×¬Hš7=„«jT\cÕØëP=L´Ï#%Ù~½ÏÆÛó……“ÄÍå}Óò YqôWàw)µ^q Þª¯1¤°^Tê`.«Åk@VéÄåªUöSoÌÄ“¤J…©O‚Ù;¾]•ÍáÐáY!fcmG¹ Z9ˆJ4ìDúüÙUÏf5›zÝÛ *E:Ȃڛž²o.žDÅÕD;ªš·¨žË¼Q/(³äô¯®öLûÐÚzôn}H•Ì%x1Q°k¹Ì ?±$Ôªl˜ƒ€¹0?oþë„ hÖúæáêí|7n·š‘C‡g 3-f¦·Tú4R‡϶gÚ±&ÝÚ^AjL³H…ÞûÑÄ\47µHQR"ÌV‡ë p;“½-­JªÂ__âê7ÙbÀ 9À诼ïRj?|€1xM©¸Æ°/kG‘fžÜ.C«ñû&ÁÌðÛÖÖ쉅ú&£mÏÌ;q\n2(™*‚RvÂÖM½ÃÑMMŠ=>2I´ß»*¢¾ îU\ý• k¾ì”Ž™«Ô5>l¹ß «+ÑPX³Μv¹†T1la¼ÔCí¸qãF°á¸Žëº©T¦ÕjÉû‡‡¸¾ß’v“Oëy^¸í8NwÇq >]>D£‰ªÝʖצ‡3ív[ßfö.¬la/¼§mYH1ÏøiYoKRꤹ Yðšô¡~2ØÓ?Ï«‘vDÚvßëÛhû¾ë{k2Ñr»’ẲšÈ§u:¾ »ã8nø–㈔RË{ ™(CJRíŸj••Þ' ªî}øÊÑ#±¹zYï.©‰þJý.¥öÃחêk,öqªÉ+UöƒØäæ›Aã.+‘ëvÖ¶[-¯•(X7$É! –ï‡ÿñ…ð×Þ’c¢ Ê•y0‹¤÷Áµ‰#û…È'1-ô6hì<†kiõ&·Nµ`È¡ÙÙc'Oè?¨7Ä ÄYؘ¦íëÄcfã•Uc»iîl±±Õ¢UæÚ{_™k[{fæØÉÇN/)jÝ*æñÐáÙ³‹ç*ÖT»”÷på•–T¿¯Â ©C³³e|z‚X0ó:>sßø»®´å½‚&ÕX»Ý>vâxß@³IµžAê!==Õ šµ²¤è¯ÔïRj?| 1x½¥pp5vhvöìâ¹Dÿ=úöP8˜eF/ê&I0Sý¼õ0ó´ËšÕ‰Ó‰ã†b“”ÄĮܽ M&!æ”gØ÷Ž(¬·}YòÚ{_¥­®’ª·whgên7š Ãvjí›XdRqÌœ1˜Q’䓾ÿÓ[ª4ß=Ö¦ÙÖ+hR™Ø C"%†A\6âÛ¬œµš³VEæß¥¼~ø@bðZ3¨k·ÛwIKX p0Kl+…â* fòÏÏì‚ùßj~œ™›QÉcIszíË`rDyíoæØ¸îË äF€æ‰¯Ü<ÊÛ?æ©ÆXìúlhb Rœ|˜Ž2ýXýÛ~Ö"'¶Neó<_!X]Y‹¾Ü?H’åËý%í£Õ·A2" èæ8º˜ø|otùç”lÈ'IÌDkþ¬>çSýÄ6f¶QÚÕlÌsYæ™ Ö÷l‰/Öý¢Tp£&,497§]а*ó%º‰âà“û‡üa ç…ë$øÑ×½Ämß—·ßsƒÃÔÔ¾çùŠtÔÄzMß󽋙 p&4I¡¨±úhGt ¯Û¶w¤·ZÑÝBap§»šzýÍZ:á[Õ¯l–{|eçã„Z²²ïRR?œœ˃ïû¢y ÁŠ Md‘Iû"ÛQ! ÿ«ÚŽ…aö„K8˜%ÿ ¼§L?Èùl{.ÌÏ=RÖgåè©÷XKG¬[sÍ|²UÂ:Eõ˶6ºÝ¢Uúš¨a÷P/ÀZ]¤kîÛ¹‹ù}©~G±Ql9ËŠMÕ “·S z%/íóÂ!·<¨1€zõ`æzSÙRÈ’¢¿Â¿KIýðZÄàà B3ÀÁ,˜C‡gcæçåÈÊdUØIztó!~½-²…újùã‘C³³©Fêõf (üÙfþEi¬­JnÔØé𳂡[á­ŠŽÞF#¼m‚׃‡6 °¼­ŽHçÜ `V3L¾yZP›¾ëü|ìQ…,5ú+é»”Ô¯E >„Á 4€²Ìaž”‹À\FL(YbqŽ¡óšg¶¡Rý•Àò¨æê;q\žÛÛ…B„OªƒŽT³‡gFgL8‰¯«¶ÇéΪp'œÓçûž¯œI¡ÚVi™j† ¼I³™óž6øzÃAl¶”D8ž$%å,rÇq×¶#’¡’ù]×UMÄ(EîhN¿CïRŸR‹BÚð]臃mtg‘Ë‚åGf‘‹È,rIã|UÒ­ô³Èe)QgP‘‹QA(ÄÌ4/:=– ³oÌð-U*Ì“D o¸«¨º˜Éupÿc'Ž7ƒc'Oönšs¢‚iæí™}ç©·#utn®ÁÓÌ£:*I¯'Çɱ¥ãû²kžÊ“^š¤fj“¼fµhÕML" €a ÇÁ”ìE/ÙvN·mw|ßM)ñíð¿ˆu£g5-]¯Oß“”'dw¢ IDATã ùAxÅÙ!jQHK¾ ýp°2Jʃ٠F|áxá‹*Áò|#³ÀBW.á`–aFDòzôŽˆÎyþ¥Úgii¹Q?¹hò”pÞwþ$˜P]ܳzÒÁ'÷S-Í»ÊÇNž8»xîìâ¹C³³‡fg5™ g—¥ÙA©Ž8Lßyªcå/--©Þòòá'þÅP½%Dø'bÚÌôcïøEId†Ãs~ô04Ý ÀL5—þ¼ˆH:b$"ú'+ˆr7E^l{®‰¹ž;uš[%Þ•Š1Ñt{]- ÁZÒæï2$ýp°™ f\°b*åü™ ™ % äÏ­òJlß¾}Hî¹XÃê¢aÌÞº91£Mdß“ôx©ó5©À¶ ñ؉ã1‰211{ _Aµ×}}'nÔà¹k`h;q<±ÿZ}%èE…©ÄŸ|á‹ÝWƒêÁÕ†Ûoˆ¯¤Û1—BP­Y™gšÄ–QMÞ.N¶é®ö[–~&–<À¯¬M³A zëœèŽ«†x즔HH×óßÄÝÒYq‰©œá‰¹ i6c£üÌ ²C³5ÈäS‹BFÕ—ÌýðšÆàC ÚCè7ö Å>&"eûhB!;qùµTЇºXýzpq/ÕÊéz{ê–m¢‰Ù7®HJ#Ppµ÷F;uÞkÛÚž™9vòDìê ¼Â  Õë®6)Üݰa"R š±®M%Œ4˜L0ä”Ô¦ÕE ˆÜ¨1kI5…\ñÙUµkQí½”…ßż^‹œ`p°¼5|]ôBÀÁ¬âtFÍ“`&î´ŒùÓ{ñ`?í¥Œ©T¶9]—o{5/Ú±áFí½ú–@^èE5g°aIZêK5­´%ZIAäFØÚ2ǘM·À@ÃÒ&…Ø5ꇃScæ]ô ·ßÎ  Âµæú5ÍŽ¿˜_ÈȈ¤ÖY]q[Ö²´eÇNžˆ…÷ÐWcïW(¼Úí”q Y{£ÚÙícñqè…9ƒ–ýH{Ó‰”ÒJÛé|õ¶Q$’¦ÆŠâi‰¹ŠëÐö¦8ç!1Ñ_!ýðZÄàVAÉ0#MC^3m¯¡$ƒiØLÇRa¦M‚© o¬z ÑÛ6Y8Ù!­‰Ùû _Ædª…y§¼dÅáÅÞ¨Öªàpʳf%ß÷<¯Óýëtÿ:‘ÿuÿb¹ÏŒvSåÔŒ¬öPd–Ê´'+ðÙ¥bCx“j0¾y³0@-H‡,,dþ¸ah$©±ülݲ9¢™ä#ò'ŸLδ,¯ü£HœYMÛž!A§``¾ô3éíœ÷¦‚ÆGå5˜–Çà6ö†¬Æ´‹šÞ’ŸÖôË3ú3Š} 3@+–ªó+×¾ªÇ`Ï ‘Þ•¬ aØÖ'dˆO?ú–Ò—0" Ö0µÛm;Ç—õV£Æ”Lœ}œ­öŽ™Kl»3_ÖÄ[ŸïP<_Æ Söº0¿pßÎ]iOh¸\Ñ=$âÕA¨FÏSFÃ_úÁ'÷›wªÔ‚¼_¿ßô…ÄBÞ·sWÝ=ScÂósþéC{©M3›0ú2ý*1æ¿ÐT¿åX§.g6ª’ 9诌ïR^?Üò¼áv³kŒþy6,ʃùÜ0Í 1œ¡O…0ö>ÙMKÓ-^˜Ÿ?zdîàþ…›°½qšµs7zWõч£‰ã)R5¦GÌ|r¿¦×’˜FZsYƒÖ<Èj20$à SÍtSÌMXÃå)c¯§ík.,,Ü·s—¹ïiíâWæ0 °pÚ»GÝC"æˆj´{éúŸùÑ#s÷íÜ•Vj¥};0‰MºaCTEÙ…´­gHïs•JÐQLlfÛívÚ% „ñë´MnžÁõ•²‚诲ïRR?ÜæÜÚp›BlÛºÒPùÌ©É!ꌖ™Ñ#Õ#ÇNžHhÜ ¬œÀD;¸ÿ@ZGÆDzÌÚ˜a«Ú»4¹Ê”lÏÌ$dÃ4ȰwutnNßø&›¤ûôÆ®áuÌÖš'Ž?UÝ9aá3|P57jPKŸÜß×J0ÏŽ”7@íV«>÷èÜ\p{ôµòd·œõë×#ÌÄü ëm|äf¿&º$-È_0¹¡ëm oOîÏ\µï7Rcý[3åóÆïÇc.Eæ®a#øí„â/¥Ýn'vAûÆ_}êg{.ÕûAš‘h½«ÅVVÈ ¢¿*¿Kýðêcðº3<5öƒ9²2RÍÇ„Óݧ¦”jzêÔéGö=<=Ô4váÙÙd­Méô;q¼×Ž ‚œDñÈùã?:7wtn.8m{&rò…ùù…ù…ÄóÛŸ<;ð%åÂ&fbk{èðlï ¾°rDÒìì´ƒo£ss¢è¦»ÝnÇÊÜ9òWH[þÁÞ¨Ab‡TB“©÷÷¸Õ±‡¥ÕÜêáå3ü²&£j¤ÙëÇF;N(±d)nË“d¥»íIÛŽï8¾n»kê|áË»©’°È¯Çö‰þ'y7Çq*®LùÓ§¦&/\XIÜí­¥¥ÓÓtzªïˆß·s—¦ñ)I˜ÊÓ‚´KlÒÃÖl8mJj,šç1žçÅZàNÇK”Ïëtwr„pºÛŽëôJI  ’âø*ɈŠ×Ú¶ãø*£‘ YÃ|ßž˜+ìWgîyšGU‡ÏÆ~¡÷:lrgL옷æïØü÷Ê YAôWýw)¼^q Þ„¾ÓpÔ˜æÜú±1ÏóBi“ß •Â÷„n;Ž/)ˆï%‡Hò©Ç Oå8N¢xéâ2RýGOˆuèðly¿¨´iï4ÝbQNšÎîiçL[«ZÌ„íõ%ƒç‰…?vò„&£YþÛCe’f†´=6“¯Ðë{Úy£š\ˆ¾„Ur›¹À†ÇÖqÑLf¯ÃÅÈÌö©)•¼.--ã`ÚÓ7imR DeZ!?vâøÑ#sC2Ž+ÍÓ¯-›7Q?I1×Ô… †!æÊøsk·N—û2\ûyš<—JÛ%Ö ƒ°§Dü.EõÃÚ4@_cšr·ßÎŒ4ȃ¹]ýHpHfº¥rÔ³Vg24gÏ¥š~Þ+åÅx5Jä—jiòc'Oä\ÙPïV÷F³Uubо×ÔpJN57j{¦ùœ&ß%qƨÑo<ëðv»}vñ\í2`öÑlf‘çà!õº $lG»x® 1¨2Ž*Rt{ªBcb­pÚ›áÐìì±ÇM¤ÑÐ$ ãÌUm‡â¬~£1á±ÇÓ¦q0ÿ.ÙÜêC‡gƒÛ#­ûPqG³Í^?ÆÌÁÔ<”Ý7iÍ‚ßuÎÕ¤}Ë©»Ci:¹16û’SñÖÒ’Ætc f"Û‡)æ2ìÔ…¿”Þ»÷ž»ƒ $8˜½ÒðüôÃþˆ˜«J¯ÇΧúB–ýÕ¢ÂTèÈÝ{ÏÝ8˜zFrÙÚ»Gã`>74‹’í™ì-®)€~Ý=ê§4Ëå:uzÇL¨ú)Ï$PÖ°wσ“˜«já U_Èò",K¨/úŒ($Á4ÁÔOMMfë¦@yîÔi4»ŠxuïÕ[KKËo‘yˆ÷†‰©Ib.+зºë×3ž£?s05!–`Á€aÒ챱ѱ±Ñµ‰o~ˆ¼!„ïwÿÄŽˆ½—x¸îA£YºA¡™‰ P»xob|\Õäç”°¤U“ $CSÚ½{$æ8úr›&ÆM"›DY1w”ªÛ¶Ÿ9˜Û§¦ôÃ0'Ў׭ ¸ibBT‘¸-¤ÎÚÖ?#Ô_ €ºÄ{ããe5ù ’©©)ý0Lb.€ Ð#¦øÒ_ézå×MïÜ~öÞ=º‹y Ã‚ŽŽ®¥– •W݈æ’@]´Cïmš§–ŒDA; “˜  lú æ@ÎL¤ƒ9559©~$¸´´Ì¼€ A'ÐìøÆ×ŸÈ|El ïüqªÈ8æÒ Ã$æ(ý`ANç4¸ƒýxý8æ5Ôý#DZÑÑl«Ûœ‰ÒŽxU÷ŒP0—¬×}¼×°+æÙ63Æ\{‰¹@ßùã3©FºÄ—±DÑõ­Ï;˜}C¬ãÇO"¨M A€Y}ç’cb€µñž^;îÞ1M-¥Œ¹tÃ0‰¹JB?Ÿ`lttS Òà¼ßüÆy.9Ô4Ï6ÌâÕÉ=ýLLf€…Ú¡®’yðþóõ¯˜  JúæÈ4s˜i}ë[ߪòó§ELMN¾öúëªCÞ}÷Ý¥¥¥}ì®@´gfä?*ªA§&·ÛŽ#ÇYÛväm×uÂC\·ûÔ­Õj%n»®î¦ÚBŒŒŒô=•ã8®\ªµÃåÆJ%¿.oÇP½¥9$£¼NM^¸°ò«_ýJuÔÒÒòôôö;3;j¡BˆÝ;ïQI†ªqVɇ¡dȇ¨ä£G¼rIFZ90™xîûþ¶m[_ã,1@˜Ø—¸óN!„ãö¯eQÅ8ýK>D>m\ûä’DõN¥}&¡P†IÆ óŽ;6èC, X°/aà!èä¶­·ÝvÛš\á`f YMÂ×;6lÐ<#B¼òÊ«BˆÓLÉ€hÇŽé©Ûn» ÓPbûÜ~ûí++õÿ@ÌP*}í˱ÑÑ©m[oµù8˜ÆÚg…ƒ)„¸ÿ#^^^þ‡~‚zÇw0T ö!èö©ÛׯJ²##¿ž¨â###áÎòv ½ò¶BÝGO•ÐQìU×…¦½¥v:˜wlغyógÏjŽ]ZZ&dûµclttÓÄx%­µíʹÕj…/ÊÛzÉßH” ÝC/éT&¾jìõÄoaNx¬J‚5‚>üᙘ˜Ä\™ék_Šp0GhDä %„üiL×u'8ÚÌÁòÑ)1²ÓÁtí¹Ì¶Ûú–––I2 `¹`÷ AGGß?66J]UÆää¶mkÏx5 ËÂ>`³vŒŽÞ½c;uU@Ìõ 1@)¼µ´db_Þ=½„ÎÙ°e ¦býú±ÉmÛôãDóÝlìï~奄‚=:úþ»§· !¹Býø®úYäÚÉý0Z8SáyÞ‡?ô¡•‹5„ï¾ûî©S§I‹ jGÔ¾tLf‘·ZÙg‘;ÙHF†Ä#EÉA0S±aÆÉÉmš„˜Ä\Ùíøñ“ï¾û®~·Û§Ö¯SI€á,riÛx f_í«ÃL‹LÏó6lØ`bb2ß  Ž‚Ú—3«lg“W!„‰‰IÈ UòÜ©ÓßûÞS}µC1¹uËm·­“E£o㌃ÙûzøÖ† ¶lÙ|öìω¹ áØñ§N=ßw·ÑÑ÷OŒoÔD8˜zÃÄÏ42Q/=Ï ·å×…7oÞ 6VV.þÅ_þ•É]²oßÃì{˜_ À xkiéÔ©Ó}‡Ï‚}ÏŽ²FI:ê˜ä“^·n]âv¸"PO†!e«Õ ÕZ~=vˆ¼-Ÿ6¦îáᆋE˜ÈsLCåCÊW!ÄŸç/.]Z5¹²<‰ ×̶‹¤NÖ,þ&Gq’dÜ&I†ÜÎÇ$CÚ–?•dÈÛ*Ɉ ™ÉvTÒàKÒàyž'ívãÆ‹/þÕý.1@NEë›%@šO‰&dÛÑu[R`2ÒWe cœÛÞ÷>9\ 5.&Xò©ä°h$j€ÊÁ‹j庡ƒllذaó¦M?ÿù¹¾g  ’j` ˜Ÿ F_FdÉàñ]ï² }·3,,«Pc²°l]Æ`ìÚ¹óâ¥Kÿð¿î{ÂW^y…€2"½ï~ï©S§ž7Ñ!ÄÝÓÛÇÆF£­«0[‹Ü ¯ƒdä ½d™ð£Ò°aÆmÛ¶ö‰IÌ W4“UéPDÜ”ÕÇí«2¦‚¥ƒ©Ú)´ÓÔúqb¨Æ`Š[O/ýõw¿gxëLOoß·ïaF‹TÃs§NŸ:uÚpçpò¸+I2c0Å Æ`Þ¸qCñ_ÿú»†#1QX(0Ò3w{フߢ‰Gƒi* ú1˜Á†ùHL ›¢õ æ` fº·ºûXë` !R™˜Ìq°G­E4÷%fì­A9˜"¥‰*ìôôvWHËs§N/--¥ÕŽšGLÓ`-­ƒ)„XZZþÞSß'æ(CÑÆ7Þ5¾ñ®hƒ™î­î>;˜ªí˜Ž†áÖÅ‹—æþvu5]”5=½}zzš@  (©B˜º4 A§E$먎ʯ˒ÜjµFZ’ƒ9’¬î*cT“åZ5·=í,òÌ´ò*„X^¾6d W!±+hxki)˜zœ*Ì ¦ÚÅ\Ku¼6ÔÊéo²›©{è¥ß'~*õQnÔWu &åe ƒƒ)ËÁÍ7.^ºôã¿x‰˜ @ eS´ J(Y¡¨™eÊ’|ÖäZÊ*cæ`ÊB3@e7SµŠ]LïTAæ­íï~ïûiM̘²ˆ+€¡H\ËÛ§z’—á`FÞ¬ƒyóæÍK—Vü“3+¬,²Bˆl^b°‡ƒiN~3Øøëï>u‰˜ †ž·Ö´,¨Ëޝ“#žPÔp0S½Õݧ¦âÒ¥ÕlCE bFGß?±1L^†ƒ©”ç;˜ÁFžÇ„j‡fæ¸ÜTâ`šJCJS±¼|áûÏ<Ë݇ñwMŒoŒ6ï8˜¦!’·.wÀÖ­[þí¿ù×[¶láÇ`¹`KÓ% <þå/}éKÿlóæMT Šۧ¤É0@¶lÙü¯ÿտܲy3UÑÑ÷ïØ>Ø—P8Á · —H\v@Ñét.^¼ôÒË/_¾|…‹`Aæ²XjjyÜ¥êñj䣈? ”*Êk)¨Iƒ9¢ƒòc°zO¬ë$ÿÌåÕó¼—^zù¥—Ê U21>¾ib|­Ln{$#låU}b‹¿%Q œŒ áW­f \üM:$2™ ÷̼Òàyá¶<³Óét¥A’ƒ÷Ö¶WWW_|ñåÕË—¹EŒmã¦ññX#ž$XÊ]r¼ÓŠJF85M©Øò;Fc0[ŠIÑSÉ%1YÉG˜MGË9s¤v÷Ä–-›·lù"Q€=ŒŽNŒodÜeÝùÄ'>þ‰O|ü'/¾ôÓŸ¾Bm@ù‘^×»ûb®-_üâÄ‹/½Œ"(ÚFÆ]VÀHMËý‰O|ü>úÒK?½råÊ•«W¹a|ã]c££±{ Ö|ücNß68ºy¤ÍOlÛ]×q]#Å‘e%TÇu»Eq¡Jæ¥ØŽ´¨ö €¡LD«zó¦M›7mzà£÷ã1y¸C MŒß¥î`Ë2[‹\$Ç8ÊmY]•ö™*Z¨Ä@.Ù#e9WòQí¦Ùî.5 -; „¸)å“–sKßxï=ùuo-ul[>ä½÷Þ{ý³Bˆ7Îþœß@~BPmÌ“¬âÑìÎò8‘}dSÚŽ.Ëp[òR ršêز ª•|T‡ÇÒTGJv¢ë ™tŒ#ÇÈ«âÚµ·¯½ýv§Ó¹~ýëï¼Ãobz±~llýú1…d˜$ûO–Œ3\üÍQ-þ62².QJT‹!Ä$CµzO|Q É M”!DK±‚V^“WòQ½.Ëá"oòÂnòë*9èt:^çÖ믽þúÍNçç??ÇmMeb|\166º~l,U¤r*5+ùD×Ò‘U¦«qVòQ…K‘í¨öEJ"¯)$m[±FqP3Wò1äú/ø7·Þ¾~ýúõwB Ö(zLÝåC cÈhØiœÒ(€uñg¸Žsý ÊøøÆññïIhï²ækÛž'-ƒëûž¼[â¶çÅ$Õï+µQÙERªã#þðoÞûÍ}»wÿ½|ùÊ;kO¶:9ŒÊyÉm»×·Í7nþ‘ èÏ­œ'„B–12$ßsã]wm¼ë®Xˆ†Lžç…!“çudíT‡XgS–áèë&2ŠŽ@½¹ë¸ë¶{nÉ‘—×»-Ô´Âc…fþaDEäP[ê`:Ž‘„¨³±ˆh"i7×`Ûw?ü×½¥¾p#‰l$M•çzôx“a©dÛÒ ?B'òç!©`ê‰ï8ŽMp–¼›ˆ-%sq£S¥CœäíÈi£9b\'!½¦È7§»—lpòêº]t]ùfpÄS€~Çéæ½‰]¾Øä~²u§ŸÂÄ“¨Ú+Y2"í’ë&“WRW•…Ùp[‘…ÙÎñ;†_*9íšãø EŽÌ÷—¯ ïøbM‘ýHÆQy2½ìZFå –h4a6½vx>¿?ÈÖV Urc³$ÎÊ<˜ÊœT®IÒ-9\rÕýs'U·¿Ñ‘‘ƒ©J³’A £Zâ«’d;*}•®¡ãûŽÌ(»)ŠíØœ…p3öõ|E†ÕXKßï«n_aa2ý lrÌÞrU0£v0s_Æ’š)·£RþלÊUn¹ e¦¶\ƨIèU;yu]Õw‘¥ÓMŒ-£Û‘)êï+Q€úª‰ªáÔ4Ú‘'U‘…àmTKÙvueB:•븲²¸9Ì>Ã…rF[9\¥‰®¤}¾äZúŽ£ò—uÒ ñ6\–UÛîû~hhʃE Œ PxõOâ,"¹/E¢x¶¢ÁŠº™J¹T›¤ò*vcUõ ´¯X‘RárG€µà`€½X´’OÚAªŽ”¹Æfcq\ƒmßq¥Ù aºK׉øº¾"÷e,¥¦<‹\^8U=A5ä˜ p`CƒœÜ^iFúG“¼¤žEž˜ûR7õ;²9mø_ÇÎãS4ÁŽzµ#oË3Êkv5sÈ«ëÊ3îT’Ú“':å,ryi½Ùž% RK ¦ Ø)%Â,•X²|äE®TŸh*1)‡‰ã¦ËƒÙ «¦žÖO,–Aµ«s‰Œ³È#ÿ KhyE'žó3€B!µ48ªY䪰(&RÁJ)9ö‰÷ÕÕK8‰›íÔ¸‘Ò.yÿd.±I›OÚ•2?ǼBÃl,ÊÂËzé¶Ú©Š—"I»TŸ¢úpß÷›½¼Ô.€Ñ†£ ‡h@Ð$5KÈ}Ùã`¶ú+}ôð®›©.•&'fÚDf_šjäU˜¤æiÉv¤Z^#qc+ñÆò„QÈjRW<°SJzwT4ΚöªëZºÝv>"ÏifÊ IDAT­–J2"¯»R.°Äm[WÁä9âXeaò§D›D#iˆ¦Hv¥ÖUö#M"/e©œÈƒ«VK^<¹ìªñ"†é’‘ (<rŒÒD&»™1ajäÁŒ –:¥¦Ê$5KÜœA¼*Ð8f‘€½à`€½à`€½ `%eNÅ>®:ñs˜‡À‘œ‘T)#É9T\×õÖÒ«tÜN¸íyž/¥]Q¥ãq[žtV_HXä×U)¨ sx)Ž(·A6?AØ›e™”3”Eó`†É+XÌn^˜‘u]Íén¯“¶åCZ-7̃溮+çD“¶Uê#¢Ûö¯ÞP¼Žx~yuo-‘™×iEäUÊv&_ÙNçf¸íE3V'ª_<ÕfTy˨[T‰OYB-¹¥•%C(V“¥$& ##ë¥DN+Ör#’!§Ñl©ä#Ú¢Êk u¿Tú4Êr&2_¼2íeŠåÚWåMkEõQޝTj"W©¢mNÇó:]™X{Ïó<ß“´›Ò!^räåy~rä¥i·‰¼ ¬8H¨7‹øRnbèÔ’K΃)‹—ëºaÞy×u[R¸$·Û#­‘ÄÓº­äÐ1ñŠ-jšø¥1ììì¥È<˜N$¥ßwŸÞ÷ÂMy&¿*ÿ‹£ž™I‰"ÖI¯w÷w½nîË–ëv³±t<Ï“³®tºÛ7»Û-¯“øqžç'ff±¼]Šú© ë ‰]êNÉG”Kì"œÄ]”y0åL[nÿ<˜Ž”™EDs_Þ¶NJj¦Î#'yi)ò|ÅòÂDJ%e®I|]dÊJ–!'šåò*Óñº2ê¹Ýg=y0»Û­‘n©¼N'Q*}ßïÊhL^Ñ5€Z ™AL9ŘœY2"Ñ<˜#r^0I&äG•J,ž:Y‘22²-eä ù¹S‰™Èê×u#1‹ÔØÊ_D^@¸n$¡çÚ[N§ãÉ…7HFÜj¹ž× å@N‘ìEò`Ê‚%GX‘ÈK޶|_yU %ÆA¢''¦“( ‰æÁl%ŠW$‘¥b©·MÜ<’œÄ¹¯PãœøiÝä/›^¼JºRŒÁ{ÁÁ{ÁÁ{I36›Ý$µ‡&c‹*Wš*3‹“³D3³Dv3(•×q#XÂ<˜­nÒ.!D§s³»ívótby0sdc!= ØŒ*[™P',“÷*)æH«»Kj–˜ûÒ0©Y,É‹ê­Òëy ò*!¼ŽÉ ­,‰t'´<Ïó»òªÊƒÙét·=éu“<˜Yt©¨J3ò´~rcK%–(r*1Ç4Ú-79/Ø:IJœ¨d¨rÉêã*R*Ëß½ùÈ™"YVdY |ùK9޳væXnh× Q½!}z,òòý„(LÑê¸ ™hy€U¡I¤Ìƒ)§NŽ&åW‰—×8ÁŠä€Iü9q³|*'ª†B–'¿ƒ ö‚ƒ ö‚ƒ ö2bi¹eâ!ÍØwÖÒ©¸®+'êŠ$8S¼.|!D7%XÛÍŽ'QÙ•Žð„ßí yaØé;NTû’CS7º6Ebhjh_²\€Um‘ aûß&KFzSÞŽÉDâÊB½6Z왌“Ï-ª y3‘‰ØW5ªräÕ’ö‘—èq'|ÖåxŽï…uåÊZ'Z®D™ˆE^‰’!4&‘qPºV…ƒ)Å;®#;˜ŽÒÁ4[¹4²†jR EiOãrÂ,r°L°Kó`F¦?(Þs_Æò`*“vEÒ¬t“s §;7Áq߉äó’énû±in¾üŸµÍè—ò•s:RÃ\ÈÑÂÒ8G§Nt›ËØé œE®ÜŽ%v‘¦E˜Ìéf3Ç›rñûË«pÝnnhו“±øª<˜ò<>ÇñÃC¤4Ó¾ãƒYä¾b¹&µ´Z*ÑJ[C œE™|§P"Y&"y0ÍR' )±êr¨ZE“ÜбýU91UÙ«iR¹#äd#ѹßÂ@&äYäRXäûÊ|#D^P@‰~ªŸE.eÊrÜPï\uÚ嶬qÑößÚ<˜ª ,2³¨êÔÓäÁ”ÕK¾¤³zRr.YÑ]ǬHÐQÜ£jSz ×wS…^„a`[xéM‚E¡ÒÈÄÐ4¶DCÚ<˜ªe”IÍb+Ò¤_ĦúH~€òæ¾t=Ov-åó¶Üäi¾çuCS¯›ZZŽ9ƒ¾WWi½®Öú15Oz(h¨‰©Wû€b%FäÊá(Ë„P4Άy05¹ÀS‰eH쨟™•Ôìg“Ý"—@²cK÷(Ó"K¯Ë2áI+öÈi‘7/%çgŽ/'`yeÈžLä@d®k&qP V´±Mí`FY øpÚ)}î¼ÿ Ì"{ÁÁ{±¿ˆ±‘¨ÝIÑd—‘© ò ż†X6¯;¯!2¡%M=Ï9‹<Ï\á3Jo`ûïo’û2zÒL³ÈÞ×…&ftŠDâ<>GJp–³šzõåUΉ)Ôi¦c9̺z'Oô¼èTÁäiƒñÌ—Ì"¨o#SÅ,rá(¦¿©’¨f‘;éS'×Z>Lf”˯æË*syÉUíûž<‹<̶,½®ërTy1‹€8È *e¹&ÞILÖìÆÂ¥|)Pì¿j#^õ´m½a™L³åÁŒˆY’‹h6Ïqœp©õ×ïWWDÒW'Ëù•!ú" «BÍXƒ¬QëÄ×åÄ.qqOr0ãá¨JÝ[-• ËêžœðE­è†bT’ØÛ#¯B®j¥HIÛr‚3yu»è"ŽIOuhŠ\4Te’Û('šx×ÄÁlEÒŠõÏ“7e@˜A&²ÏNaë‚*¿ˆ\ÕòENL¡v0…t <ß<Ð O%½.4 š9˜Š°Ï¨®*s+ªzä,Õã733&XªÄÍ&>”¥2ËSqrÂ,r°L°L°—*VòɱÅ$ÅŒœ%6«?v®pSÞMN^)//K´9óˆ”F3žœËOø‚Ñ/ëyä¾€z`˜ &9aY4…ŠP´¨®*Ëu,™"ZK‘/Fy¸:GL†¤fi…¬¾òÚŠÊkÇ 9NL#úè+eT¹­J3m$£`•²¤l- –ªœ˜jÉЬ``r*•|³”aùóg–Õêm½!’,Ûá1r¸ä¸®¯úâr죊¼"«´EôN§Ó-aËË#~†eSŒC!ÃÄÍÉŠc¦2­hU²fUhM¼“,£ê n°!O$œäîkÁÁ{ÁÁ{q|¿””&§Õì£zK“÷$ò_/9mŠœØ%’ûRú9KKüS¼\ÙXRYnO¨RT¯&7QäÿRå)6̃éœ6’äE‘‘Ss*“Ìe†uRA"{äU™¹L¡ƒ±Ž(²âÓ…êuä ¡êãD¥¡o#lضkr¹IŸ˜M2L$ $ù(OsýǤAn«;r¸kç“r_qF"e˜"™È Œã Ó¶Ú,qsrÀ¢Îƒ £LÂ%×à´"_âæ :k` &Ø &Ø &ØKYy0eüœIIR&íŠýWÎÀ"§GñÌ»¨>=m/]á¹ Àfмe°Ì$YäS s_*>:Cò²“¹TÓ~yõ£û'J­¡tú¥Õ XYR¶–NÏñ‰m¾ŒR2båHyª ߢbù(OŒÂ"Eºd?}äe˜ Yy‘ïJ …ŠLÜ,ï¯NÜÜ=•úÓ5yŸMDÇÎÜ—EæŽkÁÁ{ÁÁ{©oѧ›ÄSÞîÝ-Ü–÷pbû„™Y„ðåCXäüYò`ª^'ØÑÀí=¦ï©”©Ä„2©™£8•£Îƒ)oWœ˜²1W_–ל7I¢Ô µÔjRjxë@½ÔG%7šVÞD&Dîœ_Ãyub ²a2¸Ä«iyeɃiðˆ¼ ³¤ƒ4‰rÙ€ØNI­cöé8§â†;gniÕ>¾YæfÕò;"&ŠŸÂ0ä²m^šœÖ1p0…Ù¢@i; y»/vtJ’WCåM«ÂÌ4YX†A}4¶£FWú‡úP³¦ò‘¡µÌ³à[ 9H:‘4 Ò¸™i•Eé`*t-Ã2tÅŠTÅ¡³ÈÀ^p0À^Fšñ5by0E:å×hr.å§HÛixÅ0ÉÚ `ÛÛ÷õ sýLf‘g(Tsõ5ˆóõœ|‡@]Z“'­L¤M鋎d޼Œ.n4rˆ¼ ¡qÈ—öÄprúP5k–æÁL{H†¤]š}LVãI›À+=ÔQ¹d[¥ÎÌÐQH«èNmMÒêå5Ôf‘Nt`ø4Ƥ6̶<@É |Xy…oÅ÷!ò€ÅA"§ƒ™^°*Hâó™G+Ó‰…Òv«ÕêF¼±ÈVr-UfÄ5[ˆ`¸<Õ–Ñ,”—¨Úóó +++/]¢†È'?ù‰-[¶lݲÅÐÁ4y¼¤j¨…ñS(íÊÀÁÈ es0Ï/ž?ÿ #.,àî»ïþÜg½çƒì~f³T[ÆÐÁ¼paåâÅ‹ó T,€=lÙ²yë–­ŸúÔ'o5•=Mg߯³¦à`dŒ…Ò:˜çÎ-~ÿégÈn `!ŸýÌ£ŸûìgäèWà`6Kµeú:˜+++gÎ̯ÝÀb>õÉO~êSŸÄÁp02ÆBææ¹ÅŧŸ~fqïÀ^>x÷ÝŸýì£÷ÜsOø f“T[Fã`^¸pï F|ꓟü­µñ˜AÓÙ·qÆÁ¬)8˜¦ñOìõ˜Snw:ùõ§ŸyöÙgÿ†Z¨ÓÓÓ¿ýȾ;¦…#²ƒ)o»n誶…-y{d$mL`œSµc¨\Ky¶Ä™3gæþ–*¨_øüç6oÞ,·–ŽbÉ5}:5¢hœc‡ãfÚ&€i,”ÖÁ\\\üö‘£Ô'@íxä‘}¿ýÈ>ÌZ«v ½ƒ¹²²òùWT&@}Ù´iÓ¿ðỹƒ ` ¥r0¿ýí¹Å|)/wïÚµ{÷®ÚU^ã>ÔOº^B¿¢äê(úeV‹oð²_teÝÇ/æƒüêKžºð&ùÆKyr_<õƒŸäù™MOOæw~ûî»wÜŠlq0ë¦Ú14æ gÎÌÏçZ®gjjrjjŠkQƒ.äºb¥_³^8“ó _üÂçÃÁ˜8˜ À42t0Ïž=ûôÓÏf°/Ër÷î]»wï®g¬àñù¥zsœÜ/ÈÊ^°H ü«(õ™|¨ŸXO¢û…úYOè+v÷Óœ§“˜Ä'ôS~ët7›opó¤»ÒuÌqrióÜù‹çÞ\=÷æ¥so®fh×þÕ¿üïî¾{‡ÀÁ¬¡jÇP9˜ÿåÏ¿³²²’öCËrjjj;Æe™W †ö’ýè…DVCsó¦M_üâp0›&€®‹¦ÚŽÅB›7ƒÅÅóßžK7s<0.üËe­Ÿû²‚a_b_¦:áÐÛ—±óœ;éÜ›«OýàÅT7ôC{÷üÓ‡öެ['G¹a¬ÚB¬“‘ƒdyŸX\M`\¬™"+uïsÇ 3Çãò¡½{¹\>à’ !.\¸pae%­•9>>þÙG§»’Oôñ’Ü «¶c ™, ¤iœi· @×KKå`¦µ/ÿòcå—zã ªóc_R0ìKìËT'ľTŸç©¼xîÍUóQ™SS“ìÿ‹ƒY?Eã`¦µ/÷îÝÈË*¯pÉb\¸páG/œ11=11ñÀý÷oÚ4!p0›&€®£fî`¦²/+ô.ö%öeº±/±/›n_†›çÞ¼ôÔ^<÷æe“›{ûöíöÿ·Á6f],•ƒ¹¼¼ln_îÝ»‡A—_8à’©Hëc~öÑG7mšÀÁl8˜º¾š¡ƒyö³†öåî]»þàþç*¿Í€Ï}YHÁ°/±/SûÒè<·¶ŸúÁ‹Oýà%“[<41q0ëâª$:˜VV¾ó¿09|jjò¿ùæ7©íНpÉú’ÊÇüì£nÞ²³à`èºh¾ï‡oÅö¹ÙÍ}¹øí#ýíËÝ»v=þøc¥­Ò“Ê8¨êüØ—… ûû2Õ ±/ÓØ—áÆŸþÙÓ&ƒ1·mÛöÄW¿ÒjµB²Õj…Ao«Õ麓·Ýv[¸½NÞ–œMU¬ Œ‰„ U;F§Óƹ/§¦&÷îÝËœñ .pÉ2—í…μpÆ(?æ#ûÞ41‘Ø«gù‰”¦¡Vmk  Ï &€i,”è`šÛ—Õ½ؗؗévƾľbû2ØáÜ›«ú?Û÷NݶmÛ7¿þ¦ýª£Óé˜Û— ½¬æz—,gÙ.\XyáLÿÁ˜ããç‘Gã ` õ:˜†öåü»ß¯vè¥À¾Ä¾L·3ö%öåÐÛ—áæŸþÙ3çþ®Ï`̽{ö<´wO°ƒi­jÇXZZ2±/¿ùÍo0ô²š‹\²¢Êö_þü;©LLÌšâR™Á¾ÌñùØ—ý;é%žûû2ÝÍ6,ö¥â÷ïŸíúàfý-û£^X¾p¬Ø—û’KVAÙ¾ñõ'¾þÄúc¯]{ûÿ}î9®o­i}ë[ߢTݣضÌúOGÞyçÍyvïÚõþýŸlܸ±ââøüØ—… ûû2Õ ±/ ²/ö|zç¹7/_ÿů57îk¯½¾uë–±ÑÑ@:N§Ó Îì­áº®¼ʇã8á¶ Šáû"º@„ C5–G3îRµçyA=ÿùwþâW¿ú•æäSS“ÿößü÷wÜq½  .pÉ /Ûwl˜œœ|ýõ×5ûüã?þã]¸ë}ï{Ÿpœ°qv¤m¹¡–;ÿò¶áJ>¬êS8˜)zNa'æûO?3?¿ Ùy‰/ö%öeº±/±/±/×bϧï=÷æåw´&æ/ùË÷Þ+„/„ïyÏë±i7nµ<ßþZR`q3åhV ŒóÉ ÖÖ"?3?ÿÆohv#ñeI½&*KVeÙLLÌ¥ååòOîëë`p÷Q“¢qVÍÇÁ,f‘¤æÜââÓO?£Ùû²¸Bb_–VEؗؗؗÚkñû¿÷Åܤ¹ƒ/­®þí‚&ZÎÊÊŠþ‰#öe)=ìK.Ù Ê655Ùw:ù¹s‹\îš‚ƒ ½})„xüñǪïÝ øüØ—… ûû2Õ ±/K³/ƒÿõ51ü“Ÿ\¼´Š,ÚÌ­})„Ø»w/µTpû’K6¸²õ51¯¿óÎoœå¢×‘ªè®õv×&’,.ž_\<¯9-K÷THìËÒªûûû²ÿµèŽÄüÓ?{vñï®¨îæ……¿Ý²ù–ËéE×Á×±BŒHÛ®´-„k3 åÑ%±™‰Ì:4ô˜š_¸°rñâEÍ,ÝSjç ¸d)ÛÔÔäää¤fuò7ΞݹóÞÞÆ¶kœ aùpyŸXÉUoѶ§‚1˜}zK±|¾}dNs,öeA…ľ,­Š°/±/±/û_‹ØHÌ/hnè•‹—–—o¼wCæ¦L§þyjd¹AŽ3‹xÈ_þ•nýñ½{÷`_–Úƒ.Ù Êö¯?199©Ùá7ÎvnÞìܼÙQcÒPÓhW &@ ž~æYÍ»»wí¾,¢Ø—¥Uö%ö%öeÿk‘°ý?ýŸ×ÜÖ?yñ%ôÑBΜ™×¼;55ùóÇ‹í1ààpÉl*Û7¾®›K¾xþüõëïpÔ L€<£u0+_½ûû2ÍÎØ—ؗؗý¯Eòö®{6ib®®^ÆÄ´3ó:“Õ{ î1`_rÉì+›>!æ¹óç¹ ê&@rÏ)$œ3ò}í>ðï~¿â2øüØ—… ûû2Õ ±/a_Û¿ÿ{ºa˜—VWצ‘wÿnÞ¸Ùý‹L(Ož¨èç¦Ùr¬z½7cµïû/œ9£9í7¿ù º:Å^&*KfaÙ‚„˜ªwßyçÚÆ9ÒP‡±åò³ËKÀ4vÒ,A^ùüqìKìË4;c_b_b_ö¿ý·¿ô¹T·øêêå••‹7n(ó`æY#.J¸çÕKOMM’þ²Ø §¸dÖ–mσjÞ]<þV6LmNLœ˜v€ƒ `ÄÓÚ˜?þX•½»Ÿû²‚a_b_¦:!öå íK!ÄcŸÿ˜f.ù+¯þ ­´„•Ýúã{IY`§†KfwÙ¦¦&5&æß¿õ·DÀÁ0bqQ™+§Ú˜Ø—Ø—ivƾľľì-|ó*}ìóÊa˜W®^E+-a~˜9byà IDAT•ôH°/¹du(Ûž=}†arcÔL#4ý› `b_b_¦Ùûûû²ÿµðSUé®{6iîx†aZÂÅ‹Ê1˜ À,¬Ç€}É%«OÙ4Ã0¯¿ÃŠäµa„*zo±×åü5AŽÍäÀľľL³3ö%ö%öeÿkág¸¾_úÜßÿ^I|ûâÅKþÐ}áÝVKÚ^©O¸Ý’öB¸n÷pÇqPs¹ºbª}æŒr r`–Ý›.™eÓ®çó‹÷Þ{ïVë*µ´Ažâ[ ËÈHXÄV´´¹÷Mz8´áÙa &@40wïÞUMïnÀçǾ,¤`ؗؗ©Nˆ}iŸ})„xìsUÝ÷L$·ÍÌ)ìËBz Ø—\²º•M¿(ùßýýßs“ÔL€þœ?ÿ¦ê­Çÿr½»Ÿû²‚a_b_¦:!ö¥•öepò/©ML&’œ‹—.©Þzˆ)äù{ Ø—\²z–mJí`þâ¿ä>©8˜}ÐL!üËdÀľľL³3ö%ö%öeÿk‘˾Bìúà&ÄÑN´0'©Ÿ¼=ìK.Ym˦YÏç¿ÄÁ¬äÁzu ¯wßxÞ@;ؗؗivƾľľì-òÚ—Bˆ]÷L¨~¯¼ú³];ï½kt£­uÒö{ëÖɵçHÿ‘ó£ÉÛªt™äSÑ<˜+L!¯¼×\²º”mrrree%ñ­«×®ÝyÇn,I±ŸÜËÍ®œ³%¥Ñ4L^L{ž Æ`ôapSȱ/±/ÓìŒ}‰}‰}ÙÿZ`_/îüàúh!++J“)ä¹z Ø—\²ú—M3‘üÝwßåž±L€>œóÍôî|~ìËB †}‰}™ê„Ø—5±/…»îQN$ý³Hç ÐÌ"‡ì=ìK.Y#Êöàƒ3ܵ #e&ÁľľL³3ö%ö%öeÿkQ¤})„ØÅÌZ±wï*!cû’KÖˆ²é?ú—ŒÁ¬äÁˆwk|Ïó×ò`>ûìßT_–Ÿû²‚a_b_¦:!öe­ìK¡ƒÙétÞ{ï=!Äo~ó›ðÅÛn»-Ü~ïÆîÞŽã„éÒ'Læ8N˜‘Y!oËéÕ41yóRª©’†ÿÕL!‡üu\²ú–-üèÉÉm‰ Å»ïþêÆ-)a±ó]:B•¤xDjœå¯'7ÚN´mç.Í c02²{÷®2ºXewáŠø|ìËþ=åOŽ}‰}™îfþ,Þ¾ÔóöõëHä@¸È2>Åö°/¹d(›üÑ“êT˜`?8˜Ù½{wá]¬²»pE|>öeÿžr‰'ǾľLw³a_–h_ªó¹~ý$Ò6¶ã`¦ ìK.Y#ÊÆÜ$p0léÝ øüØ—… ûû2Õ ±/k<úÒßu©0¡¡=L.Y#ÊÆÜ0ȃ ôöz¶“ÃårË2àóc_R0ìKìËT'ľ¬³}©ÿ…t:ð_ù•Äí0]šçyá¶+edÖÖ0§T ëÁ÷ý`Û¸÷Ž€KVß²%~ôää¤ó‰û/_X¹÷ž{º³ë¹Rƒ6Î~´qö 5”&Àà{w>?öe!þľLuBìËæÚ—aL+G¶²k[¢'â`®À[såÀ¨ù êŠ + .©D.Yʦúè)uL_øšÆ9t-ýhã¬Ya ‡YäîÝ øüØ—… ûû2Õ ±/m_ÔµG‚ÿÂ%kDÙ¸“› &À {w>?öe!þľLuBìËfØ—>S–¡Y=L.Y#ÊÆÜ`˜EïîD¦‡”Ø Â¾Ä¾L³3ö%ö%öeÿkQ¡}©¥wyd»“?öe!þľLuBìËÚ—zµï‘`úpÉQ6îä!YäÚžPÁ]"ìKìË4;c_b_b_ö¿6Ú—úYärª5Ïï¦T“·5y0MR­5D‚Õ¯Çò½à^XÏÀ%«WÙŠúhß5Ô±ÆÙ[Ûî¦þˆ«Ž¯Ô¯ð-_æ º.Ö€Ï}YHÁ°/±/Sûr(G_Õ®ü ´¤Qª5€b{ Ü]\²F”­Ø–ÏæØ8ós+f‘TÔÅðù±/ )ö%öeªb_2yÀ ~ —¬eãN6p0ªèb øüØ—… ûû2Õ ±/±/,ì‘`úpÉQ6îä! ô.Ö€Ï}YHÁ°/±/SûûÀ ¦—¬eãNNȃ ôüâ»ÅÖÈùùe¿">û²ÿýQâɱ/±/ÓÝlØ—5°/}/ȃÙé/­üÐéH+ùt:^§ÓÝ^ÛÍó¢‹E(ÒhÞ#.™åe+í££+ñx~ØûRƒì{ž'§ËT4ÔPŒÁ(±‹5àóc_R0ìKìËT'ľ"û’`êÓ#Á[á’5¢lÜÉà &@Y]¬Ÿû²‚a_b_¦:!ö%ö%€…=L.Y#ÊÆ<äà`”ÒÅðù±/ )ö%öeªb_b_XØ#Áôá’5¢lÜÉ@L SXü‡ øüØ—… ûû2Õ ±/±/å#}OÄs_Ê91#ù1=Ï_ÛŽ¦]#÷%Ô£ÛÃrÉšm_úþ­¦{í¿ÝFXÊ/|_™˜8²ÕŒÄò;Ž£úޱ· €1˜÷|~ìËB †}‰}™ê„Ø—ÿ?{o%GqàëFfU/Õ‹¤^¤V¯’0t dƳI4ŒÃÅ H:0FH oïyûÙ#Ïx<ƒm|°eðóõc<ÆÈF¤ÖÆ K­.æEË C¯ên­ÝÕ[UÆû#«²"³r¯Üë÷¹®ÊŠŠŒŒ­"?EF@_À ô%Š,iCM"0˜€“C,Ÿã‡¾t$aЗЗ–"„¾,N}‰;jð ª(Š,iCMxŠplˆåsüQ×—ããÉdrœ§.«¡^q#«©•øTB‚À>÷*M !ñXÌll§ÜRäÔjÙYÒ—&F­^µ±œ[WmôEèKèKt;TQY$Ò†š X`0g†X>ÇE})*Ëñ䄆¸t*Û©WåEƒ_^©T*•Jë„//+-àìO µ~cciÒk!³/mŸ”BFN‰ëk«HFhB_†L_ŠYM©zue_ ”J+¯É–ZS\³õ51±¤ZñŽH }Pd‘H›ç§¦DÞÓÊ×ÁTyææ10˜€#ã–¾O&µ­¥³Ù}YÈqKg‡¾´\F¢Êÿ[_[U‘(­L”ØÎ:èK©µ¤/)ÖÄA‘À§ È"‘6Ôd &PèËçø£¢/ÇÇ“Ã#£&Ä¥ƒÙ}YÈqKg‡¾,´ŒFN‰'­¯­š[We5ë /M¤Öª¾ x#HY$Ò†š TÁ bù$ôåÉáÑááQϳú²ã–Î}éDeO:rjläÔXfJfE)ô%ô%vûF€" bÚP“0˜€ý!–Ïñ‡__Zw—úÒ…ò‚¾´ØW})!>Z^‘(YÐR«ô¥‰Ô:¯/³ë`RÕRÅiy5Bi½“¢u`íËâ‘@ú È"‘¶@e e~a¤Î™â±üèè±ÀÏͲ¾ ÈÇÇ“'GF“ãn¦ÐÕHÌÄ } }銾”HNÌýËõµ•òçÊ¡/ ¯(@³/q+ ¨EЗ(²H¤ 5èƒ qôèÄ-opõå÷ö'“ÖoÜ¡///èK+¦/%ÄçÊÛšk•C_šH-á1@ú È"‘6Ôd` &¸ÑcŸÛºÓöH_Ž'ßï°¥ ///èK+ƒª/¥ïö žb*‡¾4‘ZèKþ¤Š,iCMf€Á‚5z,ðs[wÚéËÌÔK;*€ÖÔÎ"„”'Êå* Ìeþ£Xr.÷öìéóâ‘sgÆ !-‹æåQ ÊÓÜ[J2KÕ)ÉOCd £Ìòvì—¨<úlY¦²#Tö­\ü²“Re¾«/´ÇDËQåyil†ÄÒ|jœ¦“ÊB*/+%„\ܾ=7a³ˆ]$JºQ„d×”RËEš;Îf?Õ4VLfyñ«ˆæÅ F¡r\ùEª˜Û¨<ÝèDêÔd:çqžšˆs1.¯Ø©Jjå•îÔ4!„L¤ÈDÚ²NNÌôœª¯­ªH”¨€¾ôS_RJÕïÕµ²ˆRÜÒ›ÏÛlM‹íÂQú(²¤-Ü5ÍÐC`0€ ‚ üÜÖvpõey¢,‘(«©›•?̵¤/ ¡³æTŠïgÕTª~±øô%É×—$—·º2úRErѺD¬.Ë]¥yÅ®¥/e¥S+>§„Љ4™H“S3œùö˜œ˜é<ÕÖ\S‘(…¾tW_Ê+> ¼E} B &”ÑcŸ›d7¼ÍÈsOޛޣ¦vVmÝì<ÅfS_2ÆHý‹Ð—Rj}iG_*/Ç®¾Ìfgæ,‰IÄHm)HÓSÓÜ„À™h”Ò7xº"QÒÖ\£úÒ)}‰çÊ#HY$Ò†š ,ƒ bôXàçæÙ ï…¾,O”ÕÖÎNT”Å}麾¤vŠúÒ-}ɆKÄHs¹@95ÚáÍ8»äÄLßàé¶æèKèKµ¤Š,iCMVÁTFT”R£~'OXàçæ٠ﺾL$Êjêf'å¬ÊæB_º¯/©•"†¾ôB_²kKhM<58ÅO œa;ÍJÌ9F½ô¥kúÒâ/Ö¾>(²"Ij2°,ü=ø¹ù@vû®/këf7µ4@_B_²1A_2y¢LIsYº©4]κ<šœ˜î<­r±ª_„¾ÄìKð¤Š,iCMö€Áü=ø¹ù@vû«/‰²æ–†šÚÙR8èË èKÝ[ èKŸõe¦íðBsiª&žÖnc™ƒâLLã} } >b€ôA‘E"m¨ÉÀ60˜€oC¸?7ÈnxwõemÝìæÖ†òD™úÒ}IÍÜZ@_ú¬/s•Ç…¦’ÃÆ›œ˜95®úú|Äéƒ"‹DÚP“A!À`þ á üÜ| »áíG~rdÔ0Žæ–†ÚºÙ21—÷èKæ‰ú’ ú2DúR|àé‡ÊfÊ9A¿ñŽœN&'¦ÕÃ@_B_‚€ }Pd‘Hj2(LÀ‡!\Ÿ›d7¼ýÈÿ»·?™œÐ£¹¥!QQ}4}i¶¬¡/IPô¥ô¢¹D’˜ZŽô žÍJLèKGô%nŤY‘A_:u-f ; &àõ°§ÀÏmÝ9{¤/ÇǓЗ$ôú’š(mèËéK1#15ÛèÈ©$ô%ô%—(A& È"6Ôdà0˜€§C¸?·uçì¾Ì, }ÉDN}Iu+ôeàô¥ø¦¹dZEb2ßKNLçÄ„¾„¾1@ú È"‘6Ôdà0˜€wC¸?·uç쑾$Òò—ЗLôáÒ—ú2ÌúRüos\.1ó4åÈ©drbúÒ!}‰ÛréBPdЗ À` á üÜÖݲwú2óü8ô%}8õe~B_†F_ŠÿkŠO•séüæ%½Mê–¯ZqШŸÐ—¦oñUQÕ`=µ"1 ”Qd‘Hj2pLÀ‹!\ŸÛº[öN_BÞïЉ£¶n6ô%¾Tf?ô%[bÎèKñP ŸÒžeI’“3*[ú@_ªVƒæ¤€¾DMƒ ¸>„+ðs[wËžê˓ã:q$eµu³¡/ƒ¯/IþwUnH /ƒ®/ ! .Ý›Ô)Ǿ¡³Ð—Z÷ÜЗÀϤŠ,iCMnƒ ¸;„+ðs[wËžêKBÈðð¨Îš[ /£¡/ÙT*}©‘K>èKB)¡$Á å\Z§œJª”5ô%ô%ðqÄéƒ"‹DÚP“KÄ‘€{C¸?·u·ìµ¾<©¯/[ôôe¢tj N‘|{‡¨/Ešb“ï¥*´œNÖ×V@_ÊXÑ—¸EÎŽP£Pd‘Hj2pÌÁÜÂø¹ù@vÃ;9¥z0‰²òD™ô]jç¤ 7޲)¹”6ò:ÁGN˾«ÒØ¡/¡/7m5 E…´¡&WÁ\Âø¹ù@vû®/ !5u³¥ïj=<p¡ãÉ<&Ÿà„r’Ö œ˜É5f•X /¡/' 5 E‰´¡&·Á\´ö>7Ènxgô%!d<™ÔúNy¢,‘(‡¾À‡.ˆi_5ü´V£NNÎ$'f / ЗèÇ@ÁÍ¿†(²H¤ 5x &à´;(ìs[wÅ>éËñd2©ùŒjmíló[÷ë‚äŠ-Á¥ó¦aæ$'¦ÕŽC_B_Oš+~Qd‘Hj2ðLÀQwPØç¶îŠýÑ—„}Yž(KT”èK<î‚Ô›|¦,ÀÈéüÉ¡/¡/'Í¿ƒ(²H¤ 5x &àœ;(ìs[wžéKBÈðˆæ"˜µµ³ ô%žßGª¶ß—RkÔ™×ɉèKã«€¾7VÔ"YÒ†š ¼pfWàç¶îŠýÔ—ú&*Ê /FD !5Ü”–›KNN«| úú¸×>ñ;ˆ"‹DÚP“ÇÀ`ÎÊ;ŸÛº+öY_êìB^S;ËŒ¾Ä°÷{¤L[¬!SZ |äô„ò ô¥)}‰> ØjŸ>(²H¤ 5xOY8' ì|nëNØÿÙ—ãÚ‹`&eæô%ƾ¸Ú#eÛ¥„r’š$qcy} } ÜkŸ>a˯pW´Õ 5øæ`NÉ;ŸÛºÄÃãzÛøT”åsÕô%†¿¸×#Éô%!4AÒZ.; ú’@_Û'¤Š,iCM~ƒ 8" ì|nëN8¸k_Š”' /FïÄèKBH9™1¼+Õˆ úÒLô›j Š, iCM>ƒ ¸( d7¼‹úR{LšH”*†¹Ð—øÐûÈõ%¥´œ¤µ¾œ˜¾´ª/q,´IÔY$Ò†š üë`n GÙ ïÃìËÌ<¯ìLèK|ë}òô¥~+Ÿ˜Ñˆ úúÜ&Q[–ç&¿n&˜ãkeB_‚bs0W‚#ì†÷M_BåÄœ¾ÄHWz-}IÉ:iþ¦úú8Ð&Q[Pd‘Hj20˜€óÁ‘@vÃû©/Ùa® }‰Ñ0.tAúR£±C_ÚÖ—èÁ€Qs„ôA‘E"m¨É à)rdçÎ]„»ž]¹bùŠË=u²Þ#}9øàøÃÉÉIt,½·©/µ8ô%µ’ „«ÈPP“Ah€ÁP ±–,==Lj{KaB_j~l¬/1:V¥»»»³³óÀóçÏw6ægŸ}ök_ûÚ{gLŽî­¸¾´¦#­†‡¾h€ ™,[ÐF¹Ý`î½@u:“,Ì£ 6ž0¹>f~0èKòÁDvîÜU¸»9æÞR˜Ð—šC_Dooïg?ûÙ?üáÎF»Â»m­€Ÿ7øzú’Òì-8ô%ô%p´áág-¨Å¡5ÎJ‰ ƒÉ ­#=#=¹U§È×d´ÔPÃ# •+T-==Ç|ºèK}}YŒ£Hš%NONNŽŒŒ¼ùæ›ßýîwkjjØ`/¼ðÂ+¯¼‚®XnæúúÒ éA_ZÒ—¸ùµ„¥ÈPб&£¥†LÑÇ»É5ïœD}©¦ô¥æ ŒçËÊÊêêê.½ôÒ{ï½·»»»®®Ž °iÓ&4^`½¡C_]ô%p¶ÙAŠ„¯ÈPб&£¥FaðŒ,LV®XîTT~ÌÁ„¾„¾´ÆÅ_üío›=rðàAd °ÙØ-ëK­Æ}i)[@ñ59HO2YBë8¥Tr²ãì'ÝòôÖþþþì[k¤!-EK© P*þé`ãQ“»@´¤(ƒ úózfÑêKÅ) /­¡X§²¿¿_|qòäÉ’’.KSS“Ö¥´©©I YRR2<wî\:}饗¾øÅ/~ô£}÷ÝwQÔZ8ô¥›úR3 HšJ?L nÝÚ%½Ú¶mª¾¡{‘:ííâ_‡t°§§ÇÒê–=ÇŽut´»ŸXèKKúƒJ¬|$„Ì›7Oz½nݺçŸ^z»}ûö¥K—æÇ°}ûvöíwÞY`’®»îº×^{Mõ£·ÞzëÆo|ã7âqåxrlllõêÕ¿ûÝïô#ºóÎ;_|ñÅÇ{¬¤¤D'äš5kž~úi­O=zÓM7½ùæ›eeeÅ^‡ õ%5ý]ýú÷ÄÅÞÔPÂć<¤8884ÔÕµóÊ+/onn*Úê} Âæ`.¿xô‘_<úȆ ßX¹r«/ !ü°¥¨<™ƒ }©rï«/1®”±mÛ6ömss³ôúæ›o®ªª’Þ*L¥Ä޹%sæÌQ<–n-})òöÛoçO¢L§ÓŸùÌg õ¥ÄO<±nÝ:ý›(})rìØ±_þò—¨B²f¨®/©‰›<èKK‚âk]¨¡â™gºòõ¥ÈàÐP×öƒƒCÅY /Aè€Á>vîÜeõ+î/… }©:6†¾4ËÛo¿ý½ï}=rã7J¯+**n¹åémOOÏ;ï¼£ˆáèÑ£===ÒÛU«V•——3%AÕŠÇxq®‹.ºhëÖ­§N:}úôc=–H$ØO»ººá~øá^x=²dÉ’_ÿú׃ƒƒÓÓÓCCC›6múð‡?ÌزeËÏ~ö3Ã,ºà‚ ¶mÛvþüù³gÏ>üðÃ<Ï매ˆ¾„¾î9TóÖÌ¢–òõ*Õwì‘öÛyjË3ƒƒƒl ­­-ŠóvmßÙû~ïôÔ4ó7%ýÍLÏäþfRÌßÌÌô´ø—š™II¤Si!-ýQJ¤?6å:ÕÉ›]} /AÁ2zzzvîzV'€Öú˜nNľÔ盂Ì(!D(îÁ%¥tjjêĉ¸ï¾û®ºêª³gÏJŸÆb±; ¿nÝ:ÙWž°SY¿~}ቜ7oÞÞ½{ÿîïþ®¦¦fΜ9_øÂ~ðƒ°Þzë-öí™3g¾ÿýï³GÖ¬Yóú믯_¿¾©©©¤¤¤±±ñŽ;îxíµ×V¯^Í»÷Þ{OŸ>­“’¦¦¦}ûöÝrË-UUU³fͺ뮻îºë.6ÀÑ£GÑ=f™ž¾¤º½ô¥íAåÒpj IDAT±ôÛÈ„°000øÓÿù¯Šù•W/[»æ¶5«W)<æ³Ï=ÿÚëÿ»xªô%)0˜BÆÎzúrÃ=wk­wÙãÖ4LèKÕ±1ô¥:ÒþÚ<Ï———766.[¶ìþûïgƒÝsÏ=^x!{äúë¯oll”Þæ?HÎY´hÑ5×\Sxj¿þõ¯³ËqB>ýéO³oGFFØ·¿ùÍoÎ;'½]¼xñ¯~õ«ÒÒRE´¥¥¥?þøâÅ‹¥#çÎÓßÕgÆ ì‘Ûo¿};::Šîúú¸éDPè¡a``pë6å¯äšÕ«:;—BÚÚZ×®¹M!1_ý?ûÜ:A_‚ðƒ LèoàÓÑÞÞÑÑ¡i0]™ƒYœú’MêKŒ1ÕY¾|ùý÷߯8‹ÅÖ¬Y#½=|øðÐPnjÉààà‘#G¤·ëÖ­ã8®ð”|âŸPa-*!$™L²owí’­ðp×]wií®SVV¦˜DÉnU”ÏÇ?þqÅ‘¶¶6öíää$jŽ¡¾”ݸB_B_ m …Tõe[[+{$_b?~‰écm‚¾¡@˜ÐßÀgåÊå„Åž?.,… }©:6†¾´Oiié·¾õ­®®.UñÇ>HN)e÷íÙ±c{[Rø.ä"‹-R©¨¨`ߦÓiöíŸþô'öíßþíßêD®øôÍ7ßÔ ÜÒ¢\¹lÖ¬Yð úR; ô%°Ð¢Ð½¸™·†k>j­})PšéO\…òé§·>óŒlM•ÖÖ–on¸[¡/EÖ®¹­óêeì‘ãÇOìÜõloo¿´æ$ô ù ™ÙW©™T*•–þØ•33IM‹ÿOÙ?u?\ú„L¡AŸ•+–KîÒ¥0M;ç“ÀèKB)¾´0ãùD"1oÞ¼%K–,_¾ü‡?üá»ï¾ûƒü «†ÿØÇ>vñÅKoÙ…/Ù×Ë–-S+W®^{¾¦i§à|`}Éì0}©w !‘N§“Éä|ðÖ[oíÚµë›ßüfkk«þ×Ùi˜¯¾úª¸ùÏ™3gvïÞ-wd‘|—ªÿpº%«¨¸›Ò91MG“^ýbþúú8Ðc#BÁÀÀ`¾¾÷í1ün[[«BbB\‘˜Ð—.^ AQƒ nàþõv)LÓNÁùÀ$`ú2Ïdþ£®/1Ú´ÁwÜ!™»™™™çž{ŽòÜsÏÍÌ̈KKKÛ|{ÉܹsÙ·ìJj7œì[ÅF= à+-ö ЗЗ à–)·níRèKiß3ˆS±,æ /¾ô§?½é`}BMvíÑŠL!ÀÌ>²#Þ-…iÊ%¸˜R_*éëK 9-³`Áv“qqÿqvò›nº©¶¶Ö¯ä]rÉ%ìÛ—_~Y'ð+¯¼Â¾ýèG?Šòu¨ËÑÑ—FÒúÒ8BÜ7GKBẖ±fÖvL§éO¶ö¥ ¤%R©t*Õ×Û·uk—âëùûö¢ºAùŸÞüÿžÿý’““âßÄäó7999%þMOMϰLOK¹1ÓéóÇ\_Z\ SüÓÉ{kbB_‚(ƒ ˜ÙÀGÖR˜Þ»wèË¢…}üùçŸ?{öìïÿ{éˆÎ>ŠiŒ‚ 8ž¶ë¯¿ž}û“ŸüdjjJ5äÔÔÔO~òöÈòåËQ¸Nt9ЗžéK5.²- R$$:t¤kûNöHkk‹ })‘/1‡‡Gv¿º59¨ˆFP\À`:æ7ðaa$_±bùŠËýù<úóÿðØ%¸˜DE_bài‡U«VI;•ýÓ?ýÓØØ˜ø¶®®îÆoÔú¢b‘ʉ‰ ÇÓ¶víZvÁÊ£G~îsŸ›žžV›žžþÜç>×ÓÓ#©««»ýöÛQ¸u;ЗyÝô%0ߊ EB¶®‡aˆûöØÖ—™²5·)–Åyæé­Ã'‡Q“vhE &€@ci–ŽŽö•+–ÿâÑGýù¬\±|å §wA_šÐ—{Ú¢¦¦†Õ”<òˆôzõêÕù;ÞHTWW³oüñÉÉÉ7ÞxãÀN¥­¹¹ùþáØ#›7o¾ì²Ë6mÚtüøñ™™™'Nüö·¿½üòË7oÞÌûÑ~TUU…ÂuªóÑÔ—úÒv„ìaèËè¶ü0…„gò¾4ÜvÜ<ª{ûìÞýÇ“'Oº9DM¶thÅHY ÈXÚÀ‡¥££Ck5Lo ‚k£ÛèèKŒ>m³nݺ®®®ü»ý]È/¼ðÂÑÑQéí—¿üå/ùË„;ï¼séÒ¥N¥íøãÿøúë¯KGÞzë-ý„}ñ‹_ü¾€buªó¾ôP_¢‹VûÁÏ’¯+ª-—PæŸ<Jú¶uíP|·'ÇU%ææ§žf¾üòîüÚúúzBÏó<Ÿ™&ð¼MaŒ9N!Ìú-qæ5'-û û]&دèd)ÇqЗ ª`&€àbuŸàw“€ëËìqóú#P›ÜtÓMsæÌQlooבŸþô§U¿÷Þ{¦­¢¢âø»Ý>_úÒ—~þóŸ£L]1–ô¥Ù®Úõ@_‚hµàS0[Õ0 ÞèK‘¶¶Öon¸[±,æîÝ{^}õ©™Tj&%mÒ“J¥S ì¦=ì–=TÚ¯'ËféÏž¨ƒ¾@p±±ïÃ*ד éK /ݦ¬¬lÕ*ånúó !_ýêW/¾øâüãÎLBH}}ý+¯¼òðÃÏ;W÷æ°mË–-ÿöoÿ¦>÷x㪦/'¸Æne¡L}‰,Òm•y¦/%ò÷öÝÛ½ÏÅQÐkªÈƒ¢ãEÅÞ>þ«\ L¢/§&g /½Ý‘œÂqÜwÜ¡ÿ•šššƒÞwß}—^ziEEEEEÅÂ… o¼ñÆþçv>«\ L¨/+*ɤÊNÖÓ“3J{«Ô—E2uéÞXÚ\äꫯ^´h‘á·ª««¿óï|ç;ß)$µæ¯¨´´ôÖ[o½õÖ[]Ê7xí›<µ‡ÇÕ=Ö¾„¾,òæ‚õ4cÇå]Tî ìñmÛ¶»·oÖ®¹­¯¯Ÿ]sdddëÖm×\ÓY[[‹Å(ÍLãyž½ö!ƒ4óšãs‹Zò']-ÇqÒz—ÇiÍ8c×Ä,¢_6Pô`&€ b{¿†U®&Áœ}YY‘0ô¥KLNN~ûÛßfNÀÅt“}©uNéKtch.(D²]c H•µ/ûûòõeçÕ˼ԗ"ª”ïÝÛýÁˆ‹_Ê—ÅÌü ÌB˜iAþ(ó§³*¦lYÌ"ÿe@ Û>Å÷𸹤 }‰©z{{§¦¦Î;×ÝÝýÉO~ò7Þ>ª®®^»v-²éK:É•šXïR£g€¾„¾ŒZsA!Á®® }¹fõªÎÎe¾¤GUb&eÖÁä—=®¹tfj—Ÿ•„ ÌÁà'ÑÚÀ§èô%Ñ[ “’ìj˜Ò[èK<ꄘ1µ'`B_B_àÑÖ—"ª”Øà˜öSGEú«} ¬€9˜|#Zø£¾$„VV$**Ê“ÉIÕ“&ÏN•”ÅKËcDC_?ó®ê®å„JáeÉdG¨ì[¹ø™OóưTî8TÍ·”õ1ÙT=ZÙÈ™ª Ñ3ç¥ù§¦ZÒ'ó %²„мb¨ ]Ø,RGæm*%¤ÓB|º¬d¦LKY OiŸW-ó;T‘ø|…”· *e3˜Êr)/Ÿ”—@UÕUä±¼¸Ô¬–,Êä¦<°¬„ue•]o®öÊÊYY©T®ŽäG’—`ª‘Éjúòxi­NÏPQÏKô%ô%.²-Z _ê ºAù_ŽýetttÙ²e® -Ãô%° æ`ðmàS¤úRü¿¹õ5:'MŠ›’kξ„¾ ¾4WèЗ¡Ñ—|©Æä}Y‘ˆC_ÚÖ—˜J€UžÙÚU$úR"ƒòS£§öï7±A9Ö¾´-úå(ƒ9˜ü!Bøµ¾$„ȧa*¿;3•žžL•”Åô%!!Ñ—& ô%[_©/)¡'Ô'`fB××”C_¢/ñ;¢ÉJžN«7(ÅÚ—ƒ‡’éËÎÎeW/‹|NÞ¾vu__?»,æ©ÑS/¿üj{ûEⲘ±x,Ϙ™XŒÇbbÎÅb±X:¦Z"%LáðìkžãùÜ45iíKŽÊ–ÁäyÎïÚåg¥ás0øCT6ð)v})’†©n.ΞŸ™LA_†G_:ô%[_©/+Êãå1e#3,wýJ} PC\øR¡/×®¹­ô¥ˆ¸,&{äÔ©S5z8¢¥á>zæb€DeèË â4LÀã禠/£ /)ô¥¼¾U_ž‰WMòe:åÛÖX©ldúú7ÉØb``p[×ÅÁü]n"O¾Ä$„äKLèK»Ñ¢g. `0xM´6ð±0¸2Æ…U_Š,lkÒ œšLŸžP‡¾$Ô—TÕª]ô%[ñ˜ÌóY_Nò¥gâÕ:í±~N™²‘é7vèKõK±«ú<³µkë¶íìqáËbÓ—"mm­ÿøÍo(7(?pp_÷~Ý!û#TèK`0xM„6ð±0¸2Æ…[_ŠÈ·ô¡Š—©)QbB_|ö%UDã†ú2ˆúòDiNã­(Õה˙~c‡¾To°¸aQ¬0è*ýeU>%„ ,”ªîÛ“¿³M±‘Ÿ###ܽgff&¥Kš!Åü P³È ™?§k—[•-·x€Áà)ÚÀÇÂàÊh}I™[_›}–œª~/+1¡/uÆÜ>ëKJ4ŒÍ¿a€¾ Ÿ¾$„ÔϾÌ{ } €¥a†èdoÕЗðMDCbvïÝ'¹JÕ?-(c”Y¹,s˜ 9™}%xUЗÀ`0xJT6ð±0¸2ÆED_Š,lkÖÑ^„Ô”}œúR5ŸýÕ—”µZå}ÉV<¦^„B_–U$â¹F¦ßØ¡/Õ,n˜Ðc``ð§ÿó_úríšÛ /Yn_»:oŸç÷ûS£§<¡B_‚ƒ À;¢²•Á¡Á0.RúR ¼@ZSã{©)á¼(1¡/•ù컾T+7šQЗ¡Ô—ÌóãЗŽèKÜ< d``P±ð%!díšÛÚÚZᛨîí³oßþ‘‘OF¨Ð— |À`ðˆâÛÀ§õ%!¤²"1·¾Fÿ{©)áüðäÌT:ÿЗþêK)1jå/Wuú’gçq3ú²­±*×Èô;ô¥zƒ…¾ÄÌÚ—”j<›LiZ¤¿©îÛ}©…ªÄÜ»·ûäÉ“ùë`Î0ZO” ̲˜ìCå¦VÁTëé \ú8 &(² |ŠT_ŠÌ­«É.ˆ©Ij*=62™ÊHÌ Ð—ÁЗªåŸ¿E9ôe ôå‰ÒZýÇE²Ë_B_B_`}LC‰\zåþ$_öÌ3Û<Ì~K\øúRÕ Ê÷íÛ¿oß¾”lÃÙú˜Œ7N³oØ…0 S)– 7ÕÆhQŠL^Pdøµ¾O´°­IWbfÂNL‰¯¡/ /µäÕ¨NЗ~êË ¾ôýòÆI¾Ì°ñ¶Í¯¬HÄ¡/]З¸‘€B¶mÛ®ºoo2GþÞ>§N>°ÿ ó#TlÝ &/(¦ | /3,lkÔ˜²ÈSÓ™¡‰‰ó3ЗЗ‚J&©=èKŸõå_z¼´öDi­™Æ }™WЗ8ÆÀÀàÏ~öo }ÙÙ¹ úÒ*·¯]ÝÙ¹Œ=2::ºwï>'G¨Ð— äÄ‘ܦ˜6ð¾”_ØÖø~ßñdrÒ0ò©ó©Ô”PV—qúÒ7}™ŸZèËÀéËÓñª3ñ*“ú2¯8 /09v LçJUÛÈÀÀ`W×ÅÅ…/ |“u®é¼º­µõÉÍ[¤#£££;vìZ¶limm ‹IÇSÌkžO«vM&võ±3êt²6¢:B0€ÛÍ>Зš‘,lk9=•M¶ ÇsgnÜë` „¾z`&‡)Ž |t†qЗVOJ !•‰…­ó´ÎŸ[7Çv$¶†ØÐ—ŽTâòI¡/ !¤¢<ÞÖX¹ø‚9Зzñx¦/!n@q000888(½íì\†Ù—~qûÚÕìÛwßý/G~¶ ÷:-ô%0s08Iqlà£3Œƒ¾´zR™t¨¬(¯¬(Ÿ[?g<9™LNŽ''““"±<þtàŠ* }©M}MyEy¼"×nЗЗ¸ÅÀÀ ûöšÎ«íþœèì\&mMþ_ïþ×…~ÈÞ°,xÃièK` &')¶ | /-Ç£zFw¼n„±rÏo;rèKèKk• úúú€èÓÖÖZ„ú²¯¯¿¯¯¿È‹Þ%!} ¬‚§È8@7ð‘ŒêŠ®Îý„¾´zRèK»W}i)rèKèKãžú§‡<"ŠEÙ„D7ÎÒßßݽíšÛÜ>Qp‹RŽp„s>Zw*)úê(ƒ À|ßÀGò•ù"ÕMƒ }iõ¤Ð—v¯úÒRäЗЗÆ=ô%(.L #v­CwÎå“›·ô÷t^½Ì±øäæ-Á”˜¥B:×U œæÓâkŽãx!G€#8 IDATóÐ-Ç ì‚§U_üT`¸d»kNvÀ³Úh}\޾:âÀ`(ï7ðééééé9FÔ|¥Zàcí~Ý/8ÆÊ=¿íÈ¡/¡/­U6èKèË"×—”ànD *ï) h%õ¥÷'mmm¹}íê`¥25)’ÐxA2’¼@)¯Þ7Êwo’7çZšÝ©½è£ &€Bq{ÉWöôÓQ¥š_?æ—Á„¾$FÒÁn$zCaèKb”ÕЗЗЗ†E} @éëëïÞ·ß{})Òß?ðÛ'Ÿ šÄ З `0„{ølÜø _œšCa¬ÜóÛŽúúÒZeƒ¾„¾„¾ÄÝ2(˜šÞ×ßßI–ù˜–¾¾þÍO=Í6sï½UÿÀô¿O”wwïw¿Ü)!„pœÓ±B_‚‚À^äìãê>NéKqþ¦oãý‚ÂX¹ç·9ô%ô¥µÊ} } }‰»e(Íýª¤©¹I é×ÌG‘îîý }écbžÜ¼Å¯ ÊçmiiN ûg*Ð܃¢÷£ÌŸ5 ú &ûø¾Žy=‘ú’I»‘è …¡/‰QVC_B_B_}™Q>Dš÷‹ÐÌL’§Ï<ãÉÍ[º÷í×hæÄ¯$ííÞçýyûúeE 4_Ofþd5‘;l·6.ƒ¾Žƒ À&ÞoàºÁa¬ÜóÛŽúúÒZeƒ¾„¾„¾´Ô­ÞáL®†75å$&«=C±oOpöGïîÞÿÛ'Ÿòþ¤Òëæ–f÷ÊÝÑX¡/3À`°‰Ûø¬\±Ü©¤zõ 9ô%1’v#Ñ C_Þx@_B_B_ô%ê]â•W^.½îïðrf__ÿ6>¬Ð—‰D¯Œ©­«Uœ]ÜÛdz(ÎÕÒÒâh±C_‚ ƒ Àîmàãžì}IŒ¤ƒÝHô†ÂЗÄ(«¡/¡/¡/ ‹úe›¥Ù•šššØi˜ìb”®¢Ø·Glæmm­••>fOKkK]]-{DÜÛDZÛ××ÏÊÜææfjQy•ÿè¸|!L¬} ‚ &˸ºDGG»úñöö•+–¯\±¼£½=8ã}‡ÂX¹ç·9ô%ô¥µÊ} } }i©L-­àòË?ƾ}r󷢨·Glæm Ú*«*ß Û<!!uuu-yo»½·O__¿"Ûµ!g÷e’d¤¾œ”mããdm‚¾G°Š7øˆ³8;ÚÛE•ÙÑ![XÓТ²ôô#+ÜËèKb$ìF¢7†¾$FY } } }iXЗ×êÆÆùó?!¾íïxró–µkns)Š…/ !‰¶mÁÉ©ŠŠŠ–––ÑÑщ‰ 6Ù­­-·¯]íøéòõåâ‹;Vêxx„LÖðrŸ_<úˆVôWáTàæväЗÄH:ØDo( }IŒ²úúúÒ° /0[«—ßôéGñŸÒ[—$f__÷¾ý×—Rª*+[ûúúY‰).‹é¬ÄÌ×—UUU³fÍr¦ÔÝéÌ /Kà)rÖp{3èO Îpßò.ô%ô¥¥¡/¡/5 úÒ°Ñ—¸ÑÀTM¾é¦O±oE‰é`"Ä…/úrÁ¶¶ ›qmm­ù{û8¸,æoŸ|*__^tхΔ:ô%0˜,„ |vîÜecgóœ{K`ážßväЗЗÖ*ô%ô%ô¥Abp; ¢‚ÙšÜÔØ˜/1´ñaGlÝ“›·äï´hÑ¢êêY1žç“ɤ_9Åq<‹1<ã.Z?EôÉÍ[ Ü£|o÷¾þè!…Ï­®®Z¼¸#‹ñ<'ýqÙ:3\æp¦GV&èKà&xŠ€Y¼ÙÀ§À4t´·{²ó¸“·¦­‡ÝÈ¡/¡/­U6èKèKèKèKPXõM¢Ä|î¹ß³7?õtkkKçÕËÚÚZm¤¡»{÷¾ýŠƒ•••óæÍ«¬ªÌŸÏÌk:¯B6VVV´-hëëícŠ“1;;—µµ¶ZÊ™½Ýûº»÷篮®rj¢ô%)0˜ÌâÍ>úè?ÃÞÑÞ¾aÃ7þÿóïƒpS`w }ILH KB_ZªlЗЗЗЗ (°ç›šo¸áo_|ñeö`ÿ€è1ÛZ[;;—™‰§¯¯¿¿ ß]B*++]°(\™YYYyñ%÷¾ß«˜"Úݽ¿›ì'„ˆÙ¢¥\ûúúûúûņÏKiX¹b¹OûöèK›‘è …¡/ ÇÜЗЗЗ†E} €jS*ˆùón]õ™?¿ýçáááááÛñTUW555UVVF¦e5Ìoh˜ßðÁ‰ÆÇ“cccV¿ÞØ8¿ªºjVõ,V/§Ü½ˆú¨ƒ À€ là£ÿü¸¸ü¥a$;]úÒV$zCaèKÃ17ô%ô%ô¥a@_ Ú”œá’%—B†O?þ—c1ÿÅêêjÑ]fj:±Ün˜ß N–<~ü!äĉ:«ªªª««ªª«¤I—>P-EK9¹­Kµ=@ØÁ`€ïøìܹKÿùqžawxp} }i)BèKèKÍÂ…¾4,Wõ%î‡A˜ nœ¹óæVUWµ·_D9vì/‚ P¾÷Þ{l˜Ù³gWϪ¦%„4·4BÒ’µŒ´]jlœOijjÔ* çZª—» Ùë’g„¾:À`ÐÃ÷ |Œg€æ=þrå ÏŸ"‡¾´‰Þô¥á˜úúúÒ° /PmJ,²5Ù×\ö+Ç®ÁHb±Ü"ŒqæuII‰øbÉ’KR©T:•îèh'„¤R)ÉT¦Ar=C:•Ê5W¦Çj"å^ÃqÏsÌk>÷šãóùâ•ûšÓü±âd§c¿Îåg5!¤¤¤”-šx<&MŒç)%„#±XŒ-5ö5ÏkÊSN¥Šèj OC_=xd|ßÀGhG{»ùgØw¹¥5¡/mE¢wg}i8憾„¾„¾4,èKT›(–âpkçqèKà˜ƒ @ß7ðÙ¸ñ!ƒýÇM,)²Â­0¡/mE¢7”‡¾4sC_B_B_ô%ªM Kq¸·öeˆ¢…¾Œ0˜Ôñ}ý؉îœî¹»§ç˜˜H7GŠÐ—¶"ÑÊC_޹¡/¡/¡/ ‹úÕ¦¸Ä¡8B–~èKà+0˜Ôñ}ýØõ—àìèèp{~hC(èK­¡¼ž—y¿w¨·oH:º{ïkÁÒö^Ã"ããÉññ óæÕ™/ÞpߦyûŶæÙ šçHoÿæª6žúÒ0«=Ó—¸7ïÈ,v_iŽã¤Å9Žã8õå2Ù…cÌkÊÇHL%Ç¥9ùY²¤…4­ÆUøï¤Tò„çxiK^¾x%»â¤Ö:˜l´D¾&§•Õ‹ZÆÅõ.)‹Cú ϼVd;Ñ(ŽüÍ… /ïÀ`PÁ÷ |6n|H?€ùçǃpañŽú2Ãî=G¡“•–W}éÄU÷ ží<+½Ýs¨—d´æìͳ´Ì¾„¾ÀâèCs·k³)ã'yDŽRŽª»­¯®Ò”»Ód¿’ŽÙÉG`•™À©~=8“#9¥Èq|n÷mWÇ¥Ž…gò*O`rj/5³ZŠ–"mÝ#ä‘c±X,¦ž*)åâ…ä^ó¼Fª8èK`0¨àï>…0˜rø»þÙ=ØÝ…!ô¥ÊÕû½C»÷Õz—¡¸ ƒ¾ô>ë‚rÕ}ƒçø_û¯½¢åo®lÉKô¥{ú7Ì P£èË"-y÷ªçôóãЗ @`0dðwŸÒ°aÃ7BqaqLYtúr÷ž#»÷v$³?råÙÊX¹7T¼ˆ“C#ÃÇGU¿»øc"4ó?æšUT“^ÑÌà›æFáÙ7Š,c“BIîûÒ«ÌérIWË:ªL"U_k…0ÅYd—š»¤ìw³  ±$7uzfêôŒ"=•••„w^É$™Ê®Œfme“Ïèö\”Èc®U^e_g®X~MÌ1_ab ìGq*ÚU¦Vñ‚*J`àÜäÀù)Õú¶´±’(‹ˆ²qŒ§’©[ĞýƒçÖßr±j{„¾TOô%ˆnèKNúh²ØG›™gÙg“åO‘sI‹M…ã8Ë8ò‚òîÍ1Ïn³ÏqëäbUJæ)rÖÊŸ(gäÏ=ENåÑÊž"—<ñ16%Zª³¥âøâ—З p`0â÷>úO¯÷×ßta}©rgõøovöö ÚÈÚùÍó[æÍož×Ð4W‹·7¢¨@3ŒJ7;KH;ÍŠÍá¡Ñ“ÇGÄÀï¼þ_‹ÿúC4ç<ÅW¬²ÌŠ®Ì+š #}@(•ŽgUeî´(!$]%È⤙8 ¡” ’øSØ>ñš²_'Y*°J”==¡9½J©ÀèJæR©”ZA ň=>IbÉ}/©a0éÇ;¯ âueò,ó"s0Sì1b0§–2Oв±)6|îŒ4/dö,LIf Ù(‹!WPìYâ¥Sd檆♃û(!¤¥ºTüokUYÆ e®N*a6%™Ä]•»|Ñf¦ ¡Gg¬¶”¾¡³üëu7_² yô%ô%(œÖ—*+$r„#¬•cÎÈîêCãì:iãhö÷Zr¿ÝòM~xÕæ–N ² Ì~%ÍšMÏaõ"'ßÉ'&[}RÝZÆÙ51™…,9Ž“ì¤Ì`Ê7Pb•¢b³ \ÑÈ\ª|!öŒ¬ÍT˜M1«åÎÚZ } œ!~oà…çÇ¡/uõåû½C¿þíN« þØU—Îonhl™K²¦Ï¼¾d\!!„Ö7ÖÔ6Î@_ })e FM?…¾TÑ—„eͳH[fõ%“„PÚRkIð„¥µ%„£ÓÂà„`¾mnÚþçk¯hÎ>Q} } ¢ Ø%Sêëv§:A_§€Áàó>úÏw´·‡àùqèKGõecKÃeKÿª±yžd÷ Ô—¬Ñ£”B_jéËŒ‘Ó/*èK¢¢/e‰)@_E™zUmÉU„JžJ<6Ù6÷ì<·þ–K /ÕàŒ¾¤°™ÀïÑj`ŠÃ«Á¥r§”:¾ð%¾Žƒ ÀÏ |vîÜ¥ÿü¸ÛÛ9=„‚¾Tް-éËÆ–†Ë—~¤±ek÷ /=Õ—T·vÉå#ô¥Ûú2[þ”PrUMìª9üÁÓéƒg3m°oèü]^wóÅy /Ò—ø;úðìKZt~)ú2LÙ}Y¬À`@±ãã>Æ‹oº¼}ÓC(èKåPؼ¾lji¸léG[2ÎúÒŸÙ—º¿åÉ8èKeb\Ó—R WÍᯚÍ8#:küPsßÐùMÛ®»y±Fƒ…¾„¾¡õe…vª|UÊÜ'ìo$Ox3»ˆ»Óˆßb1!ÛiBn/Å:˜ìkv÷iÝÌÌÏ¥ùaü€ã²Áó|,»®eŒI«LÆcñ8³hII‰ôº¬´Tz‹±k†rÒÆ>œbíKN¾iR68­ ‚¸ÜÚ—Êh5¾Âew[RD«¼rë[û@_Çá‘PÌø»þâ›íí®žÝé!ô¥òÎʼ¾\yë'WÜúIèKßõ%S|T÷–úÒ})%éªÙÜÿÝÊ5—w}Cç7mÇ´¤#ЗЗ ø|öe„S-&`º5MÒÄB_€Á€¢ÆÇ |6n|È`ÿñà/imø}©BSKÃÿu×- ’„¾ôU_R½|¤:}髾”ÎøwsÉ-õÆ 8+1¡/Ö—ë`?ЗÞrMçÕÒë|ƒéYqàáñÈÖ0`L(^|ÜÀGÿÔÄýÝϧ9Æ®\°|Ò èË•·~råªOJ.úRú:ñI_RJjTÓ /ƒ¡/ÅZÊè-õ´¹ÔÌLÌ›= ô¥fÜKÏ08EYЗ¦G­ â`L(^|ÜÀGÿÔnï~îð8Í™0Ħ\°|R×õ%!Ĥ¾ljm€¾ ”¾¤„|šwЗAÑ—b<-%´¥Žl%ƒÓz‹”õ ï<¿ ¹ÊZ}©÷Ò ²¾LDkMLBˆ´æ#!™Å)ó:Û`bLGÂ.dIåë`æ:™4»ö¥lMLAúAL§Óþ–Bn•I>·â$Ïó|,û:Ƴùîƒ)­X“Ï-¾•²K¶&&Çç'‰È׻䑾¡X“]û’çœÖ} Üm€È(N|ÜÀgãÆ‡ô„çùqèK•;«Çc /›ZæC_JýÀéKC5}$}I2Ó[jÓÍ%íú7;zzÏ[èa /5à^x>àòÃãEXaßy<\Ù} ²À`@1âã>Ñz~ÜÙ!bôåî=Gzûuâmj™¿rôef <}i¦‚@_N_Šoo©M5—ìÒ»çðÙúR3î¥çƒ èË`]2Euò.[ / &#~màÓÓÓcð躛s?ƒ=ª‚¾|¿wh÷ÞÃ:ñŠú’UfЗÒ×Iô%5¾±¾ ¨¾ÌHÌ9³ohŒY“@_B_‚¢õM–VÛy6ûÒw} B ÖÁ€¢ÃÇ |ôÍiG{»{s?}âÚ Z}IÑ×—„Ë—~ú’„@_jUV>B_N_Šo™=Óu¶dpFsšBßÐXïàùÍÕЗЗ ƒ ÷ »º¢ÖéØ51 !Ù5*)G8«/ÈÈüªq\Ñ:ãBnËtZ}Lgm´´%G8.»ò'Ïñ²51™DƘ×ñxN¿Äcq6ßb±˜”Õ<ÏIÙÂæ;»%Ïi–&§‘EÇJÅ9sÙÈÎÜè3Lúýx¨ÁL(:üÚÀgçÎ]úÏ»ºwÇw6‡Z_î9ÜÛ7¤W¾Xû2;а¾Ì¹1½ûièË êK1Ì-³¦›ãz›]ì9rúú„`0è hhî•{˜â…¾^ƒ Å…_ø®¼¹áž»£òüx1êKBèî½Gôªôev |}Iuoš¡/®/Å·7WOé´Ç¾¡±=‡«•.ô¥VŠ{aà¹Æ‚¾ ]¶_-…¾Þƒ E„øèOütõÑõé#¤/÷èé˦–èK&}i¸9ôe õ¥øõ›«&uŠqÏ‘y% }©úx>˜òÚ—úÒÅ tã©;kjB_ïÁ:˜PDøµÏÆéذáÑyÚ r}I1˜€¹ê“ЗÑ—¹J})eEõ%!´9ž¾¢|úðd©V1î9|üÚ+³e }©úø ±B”6Ù™²Os‹(r„“š'Çqòu0Ù&Ìkµ1*⎔ç”j]'_=R¶%óšçÙ×|fyJŽpÏe?RDÅ~E+d§“!çöµ;RÃ| ƒ ƒ Å‚_øèŸ—¸iN½ÇÚ }¹ÇàùqèË>Õħ†„¸jcßsäÄ‚•2êI /¡/›ƒ‰@V*.³î$%y )j%X{½Eªµ÷ ϸ\´ó¢ÍËžBP©]”¨! ó‡ãxJ3+Ê.çØõ.Ú2—o¯=£†¢×† †¾A"Ž_øèkÓŽöv—&~ööÄÄmv!C2ô%!¤·OÓ`Z}‰¦ €Ã}•– W”L옪VmÈ}™9˜:=‰»úR¶±zD µ”Áè>Y[Sª+N6ˆ`ò&Yšs«‘`YHš?}\q\þ­­œÙ]žµ^ûÇqh–&~d‘6”{Á]x¢…¾VÁSäq|ÙÀgãÆ‡ öÂò—¶†RÒ—Äàò¿²ôð8ÀáÛMš›QØÌÏ4ñ3Z yÏ‘Ú=‰g³/¯™Äx£/©e}ÀojÂr‚`µTèKBÑ–;ô%¾Œ:0˜e|ÙÀGÖ'qí¹õàŠõ±Zèõ¥þ#äÍó,®} pôÆP¾²gsÆ`ZêIüÒ—Änb ЗÄ} НB_Êá8Âq*'æØ—!›ÌË®ª(oÐìu+¸Ì­,ªÒ‰C_b4}`0 ²øµþ¬O÷ž[æ-€‰[ñB†d¾éK¢ûùÇ®ºÔÆÖ=Goe]›Ðjh{Ž|`ÔMA_ ¸_N·ÑëKÍ-d,†Wj9Ž“ËŸÂ_ò÷Ña>R‘˜*y ê&3ÛùHšù å9åù5¨F} ‚ &D_6ðÙ¸ñ!ýÅôüx”õ%!ôý¾A­“Íon€¾Àÿþ'·/9!„6q3¦o)ýÚºÇvb|Ñ—˜• ì¶O̾ [¶8t.u÷˜}‰¡d±€| šø²ž·x+^ÈÌg}Iéí;®u¾Æ–¹ÄоÄm¸r—ÈèKBI3?3”.Q »çð‰k/oPkõÆúÒÚ†à¦g_ªu ‚‰nÊÊÎãLÿ”ú®Â»/J‰êî=ŠÈ©†Pìä£Ý™{Î9K‰É¥€ /µBä»dw†›Ð—ЗEæ`@4ñ~Ÿžžƒ“º³ìf0‡©&î„ ’ù¯/uøë«>L,êK =pþ.Q®/ ¡MÜ´ÅÃâìKçô¥Ó—ØjšÐ—ê=Vä»dw†›Ð—莋 ÌÁ€âË>ú­w´·»´ìf‡©&î„ ’B_îÞsDÿ.È¢¾Äøn}I)m"šO‘÷_«l‰Ð—ЗÀñvq}ifª¬âDÙµ/Å!gæ}rL¹ IDAT!íͧË{¡šÕ”R7ö5‚¾As0 jø²ÏλôŸwcÖg0‡©&î„ ’zö¥ÈüæyVõ%faàNo”Ó—™·ô˜¾T_ã2Xú°Ô"1û²HË=LE} æ`@Ôð~Cgºáž»‹ãùqèKBihškU_âÞ z£<}IéåÜØZe¾÷¯Ãh,òÔw×Ñ\ÎÑÚÚ—f¬"5Ô—f:gjâ¤Ô` _©ƒË]‰‹V 2-ˤšJ)¤‚@ٯȾά}ÉWlRìýÚ‘6:sÕDRJ ×ô;²>&ôe‘öÄ®yF̾D­-N0"…/øè/éÒIƒ) ,Ü![’…C_²#`èK…VP%———×ÔÔ´µµ]qÅŸýìgwíÚåAž¼÷Þ{+V¬0“Zô«aí‘òô¥Õ.ˆ:2qÒ¡‡Ç¨/0Û¡/QîNwîa‰ú8 &D ï7ðÙ¸ñ!ý6|£(d¥;dËC²ÀéËÝ{_ÓO„E}Y¼cÑt:=55uæÌ™þþþ#GŽüú׿^¹råµ×^;88èÒ'''ï»ï¾%K–<ûì³è3#Ý'åéKjØÔ /-éKÜDmúÒ²HãPîR§}‰ž·¨Á€èàý>úS>‰;­SX¸C¶<$ ÍìKBÈG®\B¬ëKÌQÐÝÝÝÙÙyâÄ Çc~öÙg—,Yrÿý÷ONN"Ÿ£Ý/©éK=…¹çÈ&zWõ¥úڗЗ ÔíiC¹;7Ü„¾DÏ[ì`Lˆ¾làƒçÇ‹U_ê%Ɔ¾0"Í£··÷³Ÿýìþðg£ÕzrDðþYM_^AÎ!Õæï½µÖ¾d×g”…§‚Zÿ¢X3×ú‘g&úU [÷PbU_ʺ4õ( Ítw³˜Eé´ÆB–é´ô:-¤ÓBæm*Že?âc±´ÆWR©”êI9Ų’¾®‰©sR­Å:¥×ZH<Î~…f$ àš˜Ð—ÅÚÿº+ô%‹s0 *x¿þóãííEðü8ô¥z´Öõeq Ji–t:=99922òæ›o~÷»ß­©©aƒ½ð ¯¼ò :7`·sÊ›}ibº³Õ­{LtYŠ×†aB£/0Ù”¶Pd‹c¿ï.ôЗĹnú>ÔÀ`@ð~Ÿ;wé??îÆš›A±0ÄŠŽ¾dB@_š‡ñ|YYY]]Ý¥—^zï½÷vww×ÕÕ±6mÚ„þ ؽ3UÕ—Æÿ¡ÖØ¡/5®7ÂÀ¸)!m!È– _ ô%¾ÒÈYÀã |ŒŸXwaÍÍ X bEM_f|€E}‰¥ÈÅ_üío›=rðàAd °ß/åëKj²±«/õ"1£8‰­0>éKŒd‘¶dK/ú’@_¬ƒ ¡Çû |ôŸXïhowcÍÍ@k›C¬ðêK„ÙÑ—SJ¬X±âî»sk>ô÷÷‹/Nž<ÙÜÜ,-~רØ888¨ºè¥´¹¹ùøñ㙡^<>444wî\ÕÀùËÉiñòË/ÿçþçž={Nœ81{öìK.¹díÚµŸÿüçKJJô¯hjjª««ë¹çž{ýõ×ûûû“Éd"‘hmm½ì²Ë–/_~Ë-·”––j}7?y½½½>úè /¼ÐÓÓ3>>>gΜ¿þë¿^»víºuëtâ)V¢ª/ÍL£¶»ö%!™Ö¯ Då¯_§Ò‰¤Þ°ç15]Ô°×ÒÜÚˆ5•j³ÇíîK\õR¶ö¥ ¹&f:»Æ¥¤×iæ¸øVzÍ3¯eëHxLK¯)¥<ÏçG%.v)å¹bíK€Y±Ð—ÅiŽ\Ó—®lÃ}  &„ï7ðÙ¸ñ!ƒýÇ£¿ü¥êX ú2s úÃJ‰––öíÄÄ„øbÞ¼y7ÜpÃóÏ?/¾=~üøÁƒ—.]šÃ$}IùÔ§>5wîÜB’tþüùÏþó[·n•ŽœxÝu×q·mÛ¶¯|å+Òãí„®®.…Á|øá‡úrÉ’%ÿøÿxýõ×Ï;wddäå—_~ðÁßzë-)À–-[:;;¿úÕ¯ê§ä‚ .øñ|à 7‚ðØcÝsÏ=ìc¶]]]0˜¹¢UÕ—ÔJc—O‘–^¿Û{î/ïŸU­Ef^Ë“!{h\±ê¤nÏcwO!û‘¨¸€ÿœÖÒîƒCÇ !ìôdv–1;Y8‹Åb1æu<÷:ËݶÅbìW¤×ªëEªàÍSäÍöCº@J˜ÎåZ†ã8ñ2ó»,­51=nmAî ¢}ê¾¾þ¶¶Vtù&»,7Š9cë0¶M`L+oà´çÇ­¤üqkc!èKmsbE_ÒbÊD)žž>}úôûï¿ÿûßÿþÇ?þñøø8+)î¸ã6üºuë$ƒIéêêúÖ·¾ÅèêêbßšY_ÒyóæíÝ»Wzžý _øÂ¹sçîºë.)+" !gΜùþ÷¿ÏY³fÍã?.í´ÓØØxÇw¬ZµêÎ;ï|ê©§¤`÷Þ{ïúõëkjj´RÒÔÔ´oß¾††ñí]wÝ588øÐC¹Y9zô(~˜¾DM_Rj¾±kÝûýåý³Ïï@&‹(¢„h0!äüë.\ e0Ù ÔZSª¬¦Ãø¾&&ô¥÷§ž?þ‰'Ä×}ýþLꦹãœúDå7YaÄã |zzz æ{º°ã¹S‹W ʳçE¢/‰ }YlcL. ÏóåååË–-»ÿþûY}I¹çž{[Ó\ýõÒ[Å’—Š#‹-ºæšk Oí׿þuv9NBȧ?ýiöíÈÈûö7¿ù͹s礷‹/þÕ¯~•¿Qxiiéã?ÎÎ!=wîëgóÙ°aƒ¤/En¿ývöíèè(~äw¬yú’šoìöç'P,Í úÒµSSE¯ÓØ8?¤y›é†5Æ=TõÚµ»x3úD L%oࣺŽövÇw< MÐ? }©{K¯¡/m°|ùòûï¿_q0‹­Y³Fz{øðá¡¡!éíààà‘#G¤·ëÖ­sä™ÊO|âŠ#¬E%„$“Iöí®]²éáwÝu—Öî:eeeì\NB»UQ>ÿøÇGÛ ONN¢æ0­UU_RsúãŸ:¤-\Ùä ÄÚ—ú˜€ÿŸ½wŽ£¼ïÿŸgf%Y’o’­Ëê²¶H@ˆo²1F6¤P6) Ä.MÒïï|IzšÒ–þš“ö$9Mz’?rN{Ò´M¨mH€ CCl$_°Ml 5rˆƒµ’,ù~·n;ÏïÙyfv®»3³3;ï×QÈììÌ33Ï\ö™—Ÿçó zœÀ§§g›õøqÏn–¥K°š=}É,ÛèK´5U*++¿ô¥/=÷Üs†âïÁäëšÏÛóüóÏóç½ø,ä2 ,ÐÍ©©©á?f2þãÁƒù·Ýv›Eáºoß~ûm‹…ÛÚÚtsfΜ‰—«÷Vú’ûÌÌž*’m· ò‘2™ÉÉÉ)þ#c‚ÄÁS&tÝÓø‰@o0è˨U ðð4ÙVÆEx¢G |l‡«?ú·íùøñróÖóËJ_ÊÎĵ¾ŒssS„ªªª3f444,X°`õêÕ7nlo7êµtéÒë®»N øÈç®áƒ`®\¹R7½`t–h³ˆä£TΧSÏG÷í‰'\í ŸäߊÙ' §/³»‘MoFÉ×wWPÆH’455%ðq0M‚‡*Ë0m|LI’ 3ù”*&ôeäª%ÔOkŸ¢Ë§ÃóôVЗ ¤À`@Ä8µ-õ¼¿§”ø˜QHV/}É‘;}·öf‘Güàƒ~ùË_–§_ýõsçÎÍš5ëìÙ³Û·oW–ñ$‡O¶±˜Ð7­§WTTLMMVÖ%çÓ,UêáH\dÙÿêõ%³º‘MŸЗxó/¿}+r£üꎊ køfÚ*³=XV@õÚüü1»vfa¿žÐ— ÔÀ`@”8Ïc}×zGýÿ¶³³ãÑNÓž¶ûŠÖ¬õüòÓ—ê^¸Õ—htºàø‡ø¹æ&''_|ñÅM›6½øâ‹“““ò•••ëׯ/Õî544 (‡‡‡¯¾új³…5)­u‰zܽ°Ã÷|¾dæ·›éÍ} €á,öÍ›Uœ/(Wç”jƒ®SèK¢D |¬£mòéèìì4Ó—¶û¦¯·ôÖ,o}iÓ_À­¾D»ÓóæÍ㓌ËùÇù,äwß}w}}}©vïúë¯ç?¾úê« ¿öÚküÇÅ‹ãüzöªi¨/ô%ßUЗ €†bÑÿè } ܃ ‘!à>!?®Ôƒõ¾…¸U}I Ñ—hyº…Ïçó‹_üâܹs/½ô’2Ç"‡®£$IžïÛÇ>ö1þã÷¾÷½ññqÃ%ÇÇÇ¿÷½ïñsÖ®EZ0ïÞ6 õ¥áͦ䘕ŸLNÝC#LbŒ‰1)—Í œ399yùÒåK—³—¯\ãþ&ÆÇÕ?]¶9“O&“‘2&I,—ØG¹Xó“üx}KA_F¬ZÂþˆŽ}±Ð— `0 2™ÀÇzŒvgG‡¯ãÇ-€¾tÐz+™¾Ì}]€¾DëÓ÷ß¿’©üâÅ‹ÿ÷ñâEùãœ9sîºë.³u©o®\¹âù¾mܸ‘XyèСOúÓºÅ&&&>ýéO÷÷÷+sæÌ™³iÓ&œ\ïžCFú’Y?ÐûÛYì[ª%ä׊…¾…ƒ Ñ È>==Û¬Çh{kK]}i×z+¥¾T–q¯/ÑuG]]¯)ðƒ(ÓëׯÏÏx£0cÆ þãüã±±±ìÞ½Û«}kmmýüç?ÏÏÙºukWWדO>yìØ±ÉÉÉ‘‘‘Í›7/[¶lëÖ­übßþö·§OŸŽ“ëá³ÈH_2ógô%¶?²1Ý7f„«Õ%)û—‘$þOR¿‘4…›w°e¡©|uÏ6crûÏÑTB&£ù3#×^b,Ã!wOæªÔAym «ù‹ØV.§{ø &D€ øØoËëtçαÍÞÓÙÑQre`5¿üõ¥æ=Gýd§/Ñ-~ 9_ÖYȯ¹æþã¾ð…êêê%K–üë¿þ«‡ûöo|céÒ¥üœwß}÷¡‡jii©¬¬L&“<ðÀ;ï¼Ã/ðçþçŸýìgqZ=}»&Fú’9¸§¥bž”×}} |o&†ôJ€¾á"@ |¬·ÕÙÑámºsç<öØw­{†–pl»e“,^ú’0R€¾d#î¹ûî»gÏž­›ÙÑÑqÓM7Y¬õñÜpþ‘#G<Ü·ššš_þò—|º!k>÷¹ÏýÛ¿ýΩ×/žFú’ÙÞÓ\ïK¦ë%#ÁfWLNN^¾|ùÊå+Ù¿+W®Œ)cããÊߤÂÄÄÔä¤úÇÄT»˜I’Ò_l ô%ÿ)ƒ¾±ÂN |l-a©a‘ú²¿ÿ°Ï;}©,Zˆ¾DC´ªªªî¿ÿ~ÝL똄¿üË¿¼îºëòç{k0 !sçÎ}íµ×üñ††‹ÅR©ÔÓO?ý/ÿò/‚€F©×%C}ɬïiÛGîU›[ú„þ”A_‚¸‘@@È ,µ*%^wöôpLjZ=|ØWƒ }™¿8ôe<øàƒ?üá•”ÒxÀz•ººº={ö<þøãÏ<óÌïÿ{BHccãõ×_o‘ü§ðVf"ñÈ#|á _èééyñÅ÷íÛ700pñâÅêêêöööeË–}âŸøÄ'>¡K.<{,êK'=ž¡/n èKÇ’$Æ=#˜íêºù’$>|2u~FÊ(‹eäôa€JiþqÉA(¹É(Oþ`¥Œdøp¥” š¤üwʤ Pe)jÛpÜbÔdZà¦-Nõ¿­\N÷&L5A&ð±V¥ÞvötŽ“äã¥R«–M²òÖ—Ì®yéN_–}{Ô§TòËÜ|óÍ ,°]kÆŒ_ùÊW¾ò•¯³·Î¨²²ò¾ûî»ï¾û|ª7¼Ì˜Õ‹‰¾,¸ºPÏ >wôe¸ûoøŒP]C_¯€Á€ðdÛ$9%?îP_òjõžuk­+-&ô¥;}‰8˜066öå/™ŸcÛÄé•¿ }iº —q‹±;º›o[Ѩ< &&åžD!d*#e2’\HvšðÓY&'ÕNI““e>ßA)sSÿÜÓ<.œteÌîùæ¤pãé½ï^ÙûîÃ=¸áÃ×Bªªª”9ÕÓª•éÚÚZeº¢¢¢¢²BÎõJN$|åÊJµ(eyBˆ¨À`ÖåŠPêôÊ1ú¹á§ø?ru1NMM)‘"DAEÑpI¥_cDà–IäŽE¾Bäi&I„R¹»Y6 ¦¿&Зa;èˆ?‰}åMiTêúxB@x ,Ohǻ՗ájmÇQ_f}‰[} ƒé£GŽŸ?¾¯¯ïÿø8 |5cÆŒ7¢Šw§èËa±Úê•Øâ¶6X†Ÿ–Lʱ}’˜˜DN_´ÃùËx¯/AÞ7ЗálPárrôè c]C_oAL)%ð±íçèíXuçØv -ÕÀvGMµ2×—¦Gwjä #óU¡LØèK4L1þ|³¯þâ/þ"?59ˆ1F½/N˜ÌU]sLË1x¬¹Õ—.¬b.¿taïðЗ Ð{úÒ2x%Ó4Œûçj‚Zòω™–¬íy­LOe¦øb•’§¦2f[ )÷+”æ÷PžVªHГ;¨Imwi!#¨ÓÊWÚP›¢ FÈä»` ‚ÈE5ý²™²<Õ®£NRê¤ùX@ŸOèKà90˜RKàcÝÓ³³£Ãñê.¿¸äã¥SÖ¯ÖNÚuÑЗkV-ÞÞ{ ¡S#gµ/;Žô%Ú¦Eò¡}H7¢Ä}É ùÇ7ÙÉ¡/AtoèK²ÇÏ_+Þyç·J\…E‹:®_¯!-MDÀƒ a$°>==Û¬E¡‡ªÔ9~èËžm/ܳÎ×c‰{ïKíþºÓ—hõ×^û /ðaõ0Ö—Òk§%»ÇôeY<¼ÌÁjzeVøÁJ“2ÆŒ%35jFR/9M×¼\úi–CÙnY}é={ö¼É_bü´¶¶´¶¶–ß!nñm¾Þõ¿¥?}iÙMêð>´xô%%~ÎÓ—„°cÆ3•¬&†wx úÒÍ£Ì~g /A´]Fb_òÓq0‡†††vï~Óí¦÷îÝ'O,_Öµté>|$ì2“Q£Lf2RiÏßíW3-å=“$¶÷ͽ§OŸ.x+###ÿ󋗒ɿŋ555fç\ðJM|L5 &¡”ÒÜ^Q~1Jù,ÎJ§f]U B±©ž¡/¯À`@¸,mžœà£L:Ô—!ËÞK}IÈšUK¶÷°h ºÒ—hNàù[šrË)úÒâNã &¯/Í—<¦ }@_‚PÝ%ЗŽ$ò„YöžŸýìÙ¡¡á"7´wßþ½ûö/^¼hÉ’EÙ{LR7ÁLIÊ„§r4õ“ûpäÈ‘?üáÛÕ»o^IH§ÓéA³eŽ>ÖÔÔ¸páÂææ&þÀ͆~SJ•þΔR‘dÔð zXŸÐ—Ào`0 \“ÀÇv¤¶‡==½:v}Yä;¿wúÒš÷½zaÊ¥¾D›ÞÕ´úrõ\)þ±fóäÉLQí4¥T²F)¥Tâ¦ÉˆKÕõ˜@óÕIvC¹woFu;CÕ²åjgª¡ã4ÓŒŸO—ÑÁF)¥ÄÜ#è,¨¡} ¥TIC,(Ó‚ ðöÁÉ´Nkè>X™³+Ȩ»Y¿”Œ1Å@QJ%~¹&Þ¥º9Éh¹&¦ËýaœÖ!З^°{÷›|°Ë|º»WÎïëÛe8ÿÀƒ###‹/J&›£øÐ={öìÑ£çÎËÿª½½-ÕÞÞÞÞ–Jµkªˆ¬ÔUKßÎ]º+atôø¯~õÊÂ…Y²d±wXìb_B_–0˜"KàÂñã¶}BK²WÛDE´ë¢«/Ù¼öæ£éË7V§úÍJ¼WÓõ¾dl¸Â4檮zóáYd퓤rþ‡Ü¥ObVèeå%ÙŒRÎN23†‚`` aŒÑìG&1B•o˜±a4Ñ—vj’Ý›¼2-iô%%ÚuiÖÒRjÖJ ‚òßì~¨Æfª¦’·–:ƒ)ŠÜ´Æ`ŠÚÓDµ§Ì`¾Ã—|^Z2&“1e%‰QÊoÅ K/Ë‹ƒ©äb¹ë³˜ $ЗÅó³Ÿ=744”?_–t«ºo¶XWþ¶·oçÀ€¾âÈÈèK/½¼xñ"çºCÂÙ³gùœ< Ý7¯43¹ú%»WÊÿíëÛ5N ¤ùoß~ûÑÑãwÞyG8/0èK 0˜Kàcí HóíÉ.yž|<°W"ÚuÖ—„ù)SƒùþÁ£W/l‡¾ ¤O&¾$Œ«¨uô˜rûR{ÛJž$ï–<â˜ÝQ8ØÉîù‰§V¤ïèK×±/åßqyzppè™gžË_·»{¥µ¸Ô±ªûfÒMÒ½};uóÀƒÇŽüÑÝ–½!%IT.I¾ßn ÏcŒåvkxh8Në+ı»Ì¯Én²2¿fFGG_zée¹Z¸ûÑ<î¨ (£ÈJ•½•ÿ=Dù¾´ P³ ƒÿGþ ê¬U’ëíÌrÂB0 |zz¶Y»B¯ª;úÒ¼™^}I™—j&}ÆEœ=wis®/Ñ´ÀóÇ“N_î¯i0[4•œæ @ÉäÑçæI} Jÿ»]xÏÍö-TM>ö%Ëý^=ûìÏu«¸u—šçOª}Sj½¡­{ùåWn»íVBˆ$©]€ùé’×”Ä$BHÿ{ý.\Ô}¹aýýºã…ÕLoßN~Ð}®Z>ÊwÊæëCàƒ?st±L§·Ã}ýC_–0˜ ‚IàcßÍӻꞸLHõ¥C‰àf~„ô%!d~ªÉ¬3£çNœ«kšåP_J i1ðüÙ£êKÂȰyL5Ÿ™Âì ÃäÑÓê|ªß&%L rôIF)²°£|¹"ßåG¤\XIuì¶vä±<¨\Ùh~˜KÝ`p;ƒÉÌ &#J¾^ãØ—šÑè¢`>Š\ˆyÀJ‘K,Š‚"#DQT¾Å„˜Hp‹qÓü|~9UÇÛsS*‚Y¼î«]Ìøîf´(Àø_f–EÄb¹ûÒh¾:Í¢ê2"áY õåÆ Ÿ*RÕ‘œ­Û¼å)^bž8qâ©§ž¾õÖÎ;7”OYFŒôe{{ÛÆ Ÿòj+«ºoNµ·oÙú4_-¯¾úúí·ßæI{Ô§fnH®èËòBA0 |¬»yvvtx5PÝ!“GZ!8Ÿ-})O¯é^´f\1U IDAT½ï a!gFÏÏnœéT_¢‰ €/ת¾¬9Vij0WuÕË7eþWÚØ—L‰BI)U\ž ¨ÓYÏEk©èHy"+ykÉ?nTÇÇcjï!m6s[}É?ŽŒ¾bÆó™¾p’·Q}0MA45˜²ˆÔšJÓiÎZЉœL$ ÎTšN‹bþ9ËMš:L«AÄ'˜/I+üŸšh^E3ÍÙМe¦;‘Ä<ìˆO}Y<º¼=íím›6®÷°üM× ¤y[Gyíµ×׬YÓÐ07d§Œ\8áý÷¯›_ðÈq R©ö>¥“˜££Ç›š]ì®×ô%U%'˜>¶ƒµîêèP_†4{OáíºòÑ—DHn²3GÞIC_PÊ'Òš0 }™Bî ö¥£¡ÙÌIìKæfC¶ËX,ÌŒßäæ3û#uò ÀýͦA&w’Uþ8žy湡¡aeIÏõeö‰”j߸áSíímüÌíÛ·oß¾}*GÉã`J»xáb¾¾Ü°þ~Ïõ%_-üœ_ÿúµÉÉ)ùOG†CÊþŸ$I?ŸI’òÇM24sCrýC_–+0˜Pb‚Iàc;X;ø®ŽÐ—&ͬÈèKBÈ|ÁÔ~ä´c}‰†&¿½)ú’²¿Ö´«Nª¥ÚÑƵ¾4[ú³@x @_fJ©öM×ë$æ‰'ßxã’ÌÞ¾Êtmm­$±Ñ‘Ñ#GŽè+>ð¥mµè$æk¯mŸšÊheF¯/¥,Ê„ 3××4sCrýC_–10˜Pb‚Iàc­ ½Š³éb,ó¡—d—DŸ¿‡›ožVXŸ3'7­O-ø¨ÊçM¶Èol–ÉG§¶•˜˜*œÉÇKøÓçvÓƒƒCCCCÊÇîî•Ø:bóÔ©Ó§N½Y__WªªÓ¹KâuÞ'uò­o«­úC‡Þ»îºk]¶H=¸xŠ¿§ /AaÀ`@) OØÆC_š4³"©/åùóÚ›Ž¦G ¿;úîðìÆ³gØéKö?ïþŒe{H19v¦Ä¸ä œn`’’n6Ûy“)³•eøÎŸ¹Þhò×?‹åÞÈsËó{¨l7÷ßl]elª”\ÆÍÎ3õ kúŸªy*rèÓòª¥_«”t̨X­šaʾçÎc®®4ó彿’¿’©™V9Z­;›u³!sû‡³}ÙW7,<>~wøSÁ”³Äaíȵ\âáümqW„ZUDàw‰h¾âÊaÊG}ájìVµùýWRŒRëÊ¡q_1¦=½y›æzM²¼Lâ¹J#ºý1(+‡ØtÀd«»ê¬ov)]§HÛÎNž‡¶O¡PèK¢èY Øôààÿ1€˜ ùIl!§OŸ Éyô{ä¸!ÝÝ+ûúvÉÓï½gf0Cí¡/AÁ`9”ŒøØfËñ*Móý±Ö—$ð¡6œ\Í/}IY³j±E½|ðî°­¾ä"ôe)õ%Qšùu}}9\QkžÃ‡q0ÝêK³1ÝЗ„Ú³nZ“È… ¨ÄLäSû”©ÆÃÜ>a $ú’ää+cccãÊßÄÄäÄ„üßÉÉÉ åojjr*“Qþ2’úG˜¤þi¯]ÜLþ+'Ù~‚¼þ¡/c &”†`øXÙììèðd+ÎÙIöžˆ¾¸š_Nú’2?Õ<¯½É¬jοðÁ;Ãú2 ú’q ÍøŒç©7èKB«/_¨[`qó®îª·¸Ù¡/=ñÎ "ãY Ût ;`*æö)!íím¥Ò—Ê(ÓïÿîýüßéÐ^·Ð— H0ŠJC |zz¶Ywxôd”ºCêËhf¾”Y³jñO¶üÒ¬‚~{lVãô µÐ—a×—Üé0=çЗQЗ„YŒgD€©Æ¾Tî³ÇT6.&¡„AãcÊ11å™rLÆ.rb.®¥(ib\Vä‚e2ÆX…`ôtän¦½¶Yþ[º;}ImÚ4~eZø“”Og6mñ«Äwªb\L9§qa¿‘” ‚ ª¥” neÿä‹,‘Ó—„] òÖÞ¦ë{ûv*¨ƒ«7¦·‡A¾4$•jO§ w×is¶ä×?ôe¬@L($ð±íã°.„¾4jf•‰¾$„ÌO5ç’’óÎk¿ƒ¾ ¿¾´=ñšº¾$áÕ—ÛꘌÏ.³º«Þ¬O"3í·hø(3É6îà•ÒU7OfÖƒ²tú€¨x¯6]Â^‡2«ºoÒæ×[ô¥Í¯sX/èKà 0˜PHàc½ ¯²œ;Ý»dèï‡ 'WóËU_Êܲj‘ue½ûúûЗQЗÎÌô% ¯¾®¨±Ö—›înq«/ w'ø /A¬ˆ¢¾dŒÈñCU“rXÌ’ÔÛ†õ÷‡P_NMMMŒOŒOä?OLLLNNN)<>j†û“$Æ$IóK²ëú2†À`@ÐÀÇÖ™-§¬“»|å­/e§ðÐÆ;,êåü‰Kï¾þ{èËëK›óO /# /_¨¿ÊâæM%§Ík™¦»yÜœdçïJ /I¸ô%â`‚°{–²T<©Tûÿ÷wãëvC}Yò.¨!iІä"„¾Œ'ˆƒ @Û|ßAf˾´{]·m&FF_’\JŸ£éQ³Ú9âÒÿn?rÝ-ó¡/IXõ%w€f×2ôe„õ%!äµ-ù7¯öæ#²¾äƒ(*׬<)ðóEeBcBV} ŒFóŸŒI,!râ`2Ʋa&@·¹ƒ NÝc1Í*+D> %Sþ/?G†dÉǾ¬¬¬¬ª¬âæsq0µ¡3•i1‘àÏ“&\yL³—~¾œƒXž.&¦ PQ$I‰ƒILö±L%ô¥?lÚ¸~󖧨·öö¶¿{ô¯IÜ`ŒJhH/BèËØ‚>˜($ð Ïøq[—J‚í @sÏA3«lõ¥ÌÛþØ"/9!ä‰K‡¶ÿú2äú’Y]З$ºúrÓÝNõ¥É ­*ïŒ`’õ0&Ù?a˜Í#+$úMfÏųiãzÏs£ç×[üº^z+úx &G |¬ÇÙáÑaòñrjî9hf•¹¾”± ˆyáäå÷v…¾$¡Õ—ÌÁ‹ ôeè27ØêK~üxqúÒl›GœK}iö “¾Ä{4¥gqµiî¹Ì$&)ä20Æ) |BöL&3Á39995•ûËLe²yHÊî‘ȸ¿T&ôeÌÁ€àð;OOÏ6ë>žär‚C}Íì=n_üô%!l~ªé¡·[××…“—÷=ûÞù—¡/#¦/‰Z*ôe¨2ïŸÞd}ó¦’Ó”ñãÌQÔHb´L`úÒØB_¸°€7ÝÚªvÜN§qÊJŽÎ#·{ÛE4½/á@£ â`@@øÀÇ>¦9‚}Iâª/åÿ›Ÿj¾¥{Ꮎ·­+îw½éækë“×ÖC_†J_2k1}&}9\Q»¿¶Ñ:ó8ñL_C !”qŸ7¥Q“ø8Œš¨˜¹X™„1&PËã×£&Kä‚uÊ-N¹eò¦™Û<·!F%‚`ÅQ !„Rµ¿1S7­|Q³Ó‚(ð‹‰¢Ö6;;:Šß„Cl3¡‹3€F¯ƒfV¼ô¥<½fÕ££Y}dFÞ;}ñ䕿kë§Ï­†¾äW/‘¾dŠ¢³jýC_†C_î¯iÜ?½ÑÉÍë\_RJ¹KQÓ)’3Pº iä ’ú)1¦×gê"9!È¡ŒñÒ¼=¤œ¦4›ï\_ê¼§Ñ4ãNÝrƒ”R‡)ËG k©N‹¢b!QP¦En>ÑšJÑ?ƒÉ}AÍÎ%TW-ëô=åû²¥¥exxXžîíÛ¹)µ§¬T ¤ù!äíížÙdèK 0Š‚Àï>¶)¿ ³äãЗV>¼évë¬>2O^y¿wèØ{§ /Ièõ¥²cЗ¥Õ—ÃµÛf/p¨/7Ý4¸yŒž¶MÝãä—9xÂ/`|·ã£szøÏRæ¤ã*ˆ+å¥/³¿¹Ë—w)³ÒéÁ2‹†­¸Š½};ùmímŒ1ÉÆ$IbÙ?íbŒ‡p“¡¬LèK ƒ ¾ãwÛ”ß%̉™¾tÒÌŠ¯¾”q(1 !£ïy»çÈhÿèË’ëK+YÅôeiõ¥ì._¨[`2rœäëËy-Õú’Ÿ†¾eçÂ|ÙtîáÓÚÒÒÒ’TfoÙú4NYIèíÛɇ"mkk“ Š¥”E¥ò§•:i©­O:`B_‚À(rðßëùœÀǺüÀ†lÛŠT`WÐp4…ã®/eÞtûöÞƒ;úÞqRÇûÏï?ÛÐ1«¡sôeþ«…ÿúÒÒÊ0îÿ /×—Ã5æ!/ îA÷±/]éKªû`íj- Õ é¦Üe™›¦ŽƒçO¯+qIMYºù#Ó©n×-FCSJ´Ã¥-¦•UCkZŒ¼æ¡›e±Eb>Š\R.EB(¥Ê†Š‘”z8 .¬4›^Öµ´gøEåãæ-OmÚ¸§,HtãÇ !mímžTC˜+úè€Áñ;mÐÉ`¤¡Ãäãqz{¾TëeM÷ÂyíMOl}ÅaMž8|îÄás5õU5sªævÌ„¾Ô?õ¥ELíËôePúr¸¢æX¢fm£«QOôe.ð%Õ?á£üÓ†j‹É…Á¤Ä0¦$§þÑ-,o(wp ’ºÔ—ÆëJ¹ø•êÆµš2ï]—R˳ó)¥ÄDÌÉÂNL|"“ B.á ¨ÒP T0[ÅdÚ¹Á4ýñ’$Æ úœé, fy˜ÍòÖ—„d2™L6;6"L§£.1#§/u]_¯¿þzOª!Ì• } òÁ½ž¯ |B2~Ü¡¾,£ì=¶Í,èK}Ãq~ªé¡ ä\bB.Ÿ¿|züäïÎW×WÖÌ©œVWY=§ªH}yñãòÌÚùЗùúRròº}i¢/‡IÕ0­"”µHã-ìJ1úr8Qs¬¢š0²¿¦¡€ÑUKëVwÕܼ®{_š-À |¬å:Bjg›èÙ@rÉè5ÖÄÔ³bvÄ”²×—2wßýñÿ÷)#-1£¥/7oyŠ¦Óƒßúöw7nøT*ÕŽSæùúrúôÚéÓ§3&>¢4Ó„i¹ÌhEU¦ݨ¡/0˜à~'ð±Ö£Evðtˆí0öÀq†£) }iÓp|xã}00úÄÖ_;¯`Ýç±Óc§'Š˜tZ¦P“i{¨åG¯ÍÖ/]¡—¯~uy|‚²{è| ï6Oêß˪³ÛòþÄlËÛÄ˽åGŽ?ô¥ƒ˜˜fëRã§ÓøcjSÌÉc™í€íQXhMWÏRó‹…Ó­‹j²1nV¬É`mçŠA?𜋉‰v_ž%:›N&“wÝuçÿüÏKüÌ-[ŸŽÄŒ¾HëR÷Bš››š“Í%hX™Ð—Àä"¿ð5OOÏ6ëñãÅ'rtŒ±O>}é²áÈH¶3æ¦yíîâúù|"Kºõ GTŠ ù®/SÉi›înñW_2F “•Zî/O—1‰Âý)Ë2uy¢ÌUgm”*pÓ¹ÍäVᦚ™$>ÍîLÞF5ÇB·.Ñî­Ñ†¸0;1yûiQw†añâ¢Gª‹M3–•˜ºù[¶>=0Æ)ó9ð¥~ðø5WC_:½RA™ƒ ¾àkÛðšÁŒÚ†¾„¾tÙpÔ¬ûðÆ=´á6K }ü ëC_š–²jiÝk[•‘ãÄ÷Áã&‡Á iÔ"Yoˆ9nž`J2©R© Ã'¶£ÑËëA¼jÄ`ÓœRN&“<°©©Ióó½eëÓ›·<…Sæ ùy{! ÌŸ>cºñÉ¡Æÿ(÷Ù3IÃZ™Ð—ÀŒ"ïñ;uïÎ`FmÛ&"A¥AIÃØÍ›-ô¥šÞg~ªéƒÑ}ïæÇ„¾ôøü\1´GTò ù«/W-­[ÝU¯+ú2@})¡ÁOâ¢/åNÁüŒÜ'A wÜqûË/ÿjtTýísnŸéËüÀ—µµ5µµµf‘(ÌBIPJ©@óçý‰ EeB_'À`€÷øšÀÇ6îdÞÐaòñø¼Ë¸QЗúeytàøŽïZîp §ú²LލäòK_¦’ÓRÉj%Û8ô%ô%ô‘¿ÁãÚœc*Q‘˜Q×— ,(ûÊ„¾Áñ5mÏǼ¡C}YÞÙ{ìlô¥ëuç·7Îoo\Ó}Ãö¾wަ»ÍWîÙ©ƒ¾,“#*ù†|Ñ—«ºêRÉêyÉi†%–H_ò‰huåPµœì2êLƘ܇ˆéc䦩\8É­bQxÞ|u‹ù•n¶"±*PWÕœ´->bc6f®4Ãi¢øi’á'¯dZÔ~òCR‹<̼—Q}ÈÆS_šsÇ· lßþ†2'l Ê¡/‰©‚KeB_çÀ`€ÇøšÀ' ãÇ¡/ílôe1ë²5Ý7ÈSÛûÞ%„äzerê /ËäˆJ¾!/õe*Yj©N%§Ík©Î¿_ŒoÞõeabÅt´]†9ÈŸ®9:we ½/c ô¥! kÖ¬æ%& M‚ò¨èKÃÀ—©TjúôÚ@Îa)+ú¸¼Ä×>ÖãǃI›c;†=‹æó¡/‰_@d•¹¦ûIJ½2¦MŸðç,A_–Ç•|CEéKÙWÊÓ«»æäÖdÖ÷ ôe‰ô%Þ™ãôë^¾úÒ*¢¢MüK™$0ãÅAljjþèG×¼þúv¾Ø’KÌHëËyóçÉ/•S «v1¡šQÔL+AhöôŠ‚ ‚æ´+«‚ùµ…s}'`0À3|MàÓÓ³Ízüx‘½;€äã…ªèK×úR7s~ªq~ªBȇo”˜'îÆ®p'Õn|törÐþH™Ë‹ÁmÚeãjÔ\7sÑqµ8©SÅ Ú«Ηäàª+o}ÉŠ.œ@_ßÝã×û’1w[nllüÔ§îÿõ¯_=uê”2sËÖ§ÛÛÛJ3*ú²·og_ß.~NMMMCcCmm­'ÕàUë–†öA_Æ U^á_{7Z\ïN'@_ÚL„¾,V_ZŠw§úÒÁÞB_B_ª/iöfÿ˜2G;Ÿ0u&ã¦ÕùŒr¥ÈÁ%å@’š…•ù\ÑFëê§ aT…ŸoŒÁF¸?ÍÎp½‚ui(fݲúu¥¾,lÅ›»WΙ3‡Ÿ#çö‰Ï)sÅæ-OåéËêyóSéK[·¡8GЗñ¼Á×>Ön´³££˜ÞÅ ô¥_ã`ãr /¡/¡/ .Ÿ }i¶@ð½/\<’‹ûËéÞ|®¹s‡÷ç²ÿu‡¾tÉÍÝ+;:;ø9KÌéK]ÞžššêÔ¼T$Z·¡8GxüÆŒ"oð/mçG¿Õ¡Ãäãq~Áq0ú’8p ¶U} }YpülèK³ /mϵí¹eôë?}I ÔôÜÍL¾ïSBÕé„:]QQA¹á†755¾±£W™X‚òHèKÃÀ— s!” X)hƒWVVV™T{‚;5‰D.Df"!*Ó¢(Š‚ºŠÈA~Œ8ÕÄÄDê:Ð<À¿>¶ýV‡õeŒ³÷@_³.ô¥á¹€¾„¾„¾4º2¡/A¿î1ì}éÝ•ÝÐÐнêfÝÌ-[ŸH—å)sŽqÞžy)Y_–ýõ} ŠŠÅ×>Öö0€ÄßЗ6­}ûùЗÄS°­:èKèË‚ ägC_š-}i{®¡/ãôëK}émõõõ7®¸±¾¾žŸ¹eëÓ½};Ëì”9ÇP_¦Rí555q¸þ¡/Añ`9‹ |{ì»Ö ø=~ÜvP¨Q|ñ1y£v°Œq9ЗЗЗÈφ¾4[úÒö\Knî5ñ_qèK˜3§~Μ÷ì~óôéÓÊ̾¾]io”GB_¾L¥brý‡G_âÁi`0 (üKàSòñãH>^t»ú’8p ¶U} }YpülèK³ /mÏ5ôeœ~Åcû’Rš‹„(‰q_¨“‚ pE>£T¡Lg2êK •/ÞrËêÞÞÞ“'O)_ɹ}¼’˜‘Õ—5óÌWNaìKQTYmì˪*5&fee¥R |LQ•UDQäc_Š¢ÀM‹ü9§üyÕµ } 0ŠŠÄ§>¶Ñ'‹‰­é踠/‹mA_NÁ¹qØ…¾´="èKèK7çúú2V¿âqÌ<ÄVVÜ´âêk®æçȳø°˜á×—é|}9wîE_†éâAïKv`0 püKàc=2½³££˜Øš¶Øvÿ$þ`â‹ÉµƒeŒË¾„¾„¾,¸@~6ô¥ÙЗ¶çú2N¿âЗ~rÍ5W/¿q?',2·O$ôå–­Oëôe*Õ>wîÜð]<З `9ˆ |zz¶Y Ä‚»v:<.'Ù{‚©ä|G쫺õ¨]}I8Ûªƒ¾„¾,¸@~6ô¥ÙЗ¶çZrQ8ˆ8З´‘êëë—߸lï›ûøï¶l}zã†O¥Rí:e oôe)”L(ŸøØŠQ_s;Ô—Ádï1¬ ëÊ!ÙØ£ºVË=ëÖú¹§Ð—ЗЗ…U¾„¾4¾2£¡/A™}iQ š_%P£nš®È˜˜;>& EBHCCÃ+–ÿîðûgΜQ–ܲõéîî•«ºoŽÄ)sHoßξ¾]üœššê††E_jª—‹})‚˜RE¾zù˜˜Újç\ŠÜê‚rFøMBªŒKÍ.‚_ÛЗ@ &‚ |¬¢ß¹¿Ã£/ !ýý‡ YëðáüSã§Á„¾„¾„¾,¬ºô%ô¥ñ• } $†úÒJ(ï²”II ·³¢‰µäUcŒ1AÎÍŸ3gÎìå³÷îÝÇKLW Êï/ÓŽÏK ‚Z?: Ì©FcI´“¯ê„•Áó‹å·Î3Ó””RjþUçúä8˜P>%ðyì±ïZ/àkôIÛ­ûíO£øâcòFí`ãr /¡/¡/ .Ÿ }i¶ô¥í¹†¾ŒÓ¯8ôe‰X¾|Y]]?GÎíSõf¦/CxñøT™Ð—À?Ð\ãSÛü9¾FŸDòqÏÚHЗЗöçúúúÒèÊŒ–¾dˆƒñ_nèK?Ðö—/_vúôé}ûö+sd‰¹ªûf³°˜!¯7ÓÀ—µ5!¼x /AALp‡ |J8~úÒ³6ô%ô¥ý¹€¾„¾„¾4º2¡/A¿ÜЗ&PBsüÌì˜â|*¨Y¨Ì‘#ÁM%Ä„(ÿ546,]º„ߺE‚òhêËÔôéÓ¹*¢ÊŸ˜àŸøXàöU Úöý$>^úKIs úúÒð\@_B_B_]™Ð— È_îØëK^[ñ{Äû,ÆEĤÚT/’Y&A`„)ËšÚ1nöܹs—-ëâ{b£å!×—ùy{!óæ¥jkkͳi¢Rй™‚¨Éäcû’›¯]FH$”Ø—šAÜÖ™ PÓNK~mC_KÐ\àSŸžžmÖ±àÀšNŽÈIöœzû6ô%ô¥ý¹€¾„¾„¾4º2¡/A¿Üè}éc«¨¬¯¯¿ãŽÛua1·l#fÉÈ IDAT}Z ‹òzۼ婼´ã5×_]mmmØÛ®a»¶¡/èƒ .ð#ý°ôBk:Ùt¨’çÕç:yH~¿a^rëz+]kúúÒð\@_B_B_]™Ö—xS®ä ÃZ°|ù²={Þ%ð±Äí_ȨëK·kuttÛ4„¾tâ\5A¡/múúÒ͹ƒ¾„¾€Ø—…Q__¿hñ"ÝÌHÌÍ[žÒéËêêêk¯íô\_2ƳP°¾  ï }éj£ÄN@@_B_B_š-}i{®¡/A ƒèX>¼¡Ù^ T 4ï~—×L˜KE– ˜21ïR…LF´Ý™+Vì}sorûæíQÒŽS.ú$¥TÔ#©¨¨PåKB®ªªR¦EQLðq0bbB*¹¢*++¸MTrgŠr{Bø½¢|L¾ÚÁüÚ(·Ø—ЗQ}0À ?øØÊ¸‚Óš¹]šäãì9){…¾„¾4<ЗЗЗFW&ô%ˆˆ}é!Ëo\¾àªüYby¸ràK¾œ7/%ëËО¤ð^ÛЗ P`0À ?øX—ÙÙÑQXZs["ª/{ì»õeIwúÒ‰ (¬ê /‰ƒ½…¾„¾„¾´=מëK 6ÄÐ’øv€¦Å^uÕ‚ÅÚ°˜r‚ò`Âbšäíi¯©©ñ£n=ª^èËØÝ˜1Lñ#OOÏ6ëñã…EÕ´Å6즉ƒŠÜm'iÇ¡/]} }izr¡/mOô%ô¥í}b ô¥×M©,³ëf/.EnŸÞ¾:}YSS퟾ ¦2K¹{З 8Lñ<í˜tŸL\D“C_B_B_:Ø[èKèKèKÛs } iDß’XÇÄT[Nà£(ŠŒ+Š*Ë›ÅÄ$ºH‹|±ÜôÔTb*“ijn^~cåûï¿æ´sëS?í¾yew÷J?j#?oOMMÍüó•=WŽ]àâ` ‚ ruRQ©©Ô²Tç' 1‘53¢ ðq0\ÌŠD‚+V-JD¾ Õ3B)¥¹8˜Îb_¾$З 4 &ãGk%êS/HèK_ßVìæC_B_B_\ ?úÒlèKÛs } i ÷¥ÿÔ××wuuÍž=›ŸÙ·s—a1­õe0­ÉR–äù©‡¾^ƒ ø‘ÀÇv·Ñ6é¹OÛ-'ú²³£C§/ûûûCÒÈ7z+&.u ô%ô¥íÞB_B_B_Úžk_õ%^ŒA\,‰OXX©K–.ÑILosû ¤¿õíïêóöÌŸî¼=ú2†7f Á,¦aÞžyóçÕúøÒŸögøN=ô%ðÄÁ=~$ð ~ü¸C}ªì=Nö™„uØ»é+:ô¥£&(ô¥íA_B_º9wЗЗ ˜þòµ$rDDÆ4-e*p7†zk"QPÓjS‰2I“åŠ2¼Èo"‘Èd2ÙЙS™)yúºë®mh˜»÷Í}Êb²ÄܸáS©T{a‡l¨/,X0}z­²óê ‚ *±/E%x¥ ¢¨î|UU•2]Q¡Æ¾œVU©Ô©(ª«ëâ`Š\•ò11b‚ÛM,QMìKÊϧ&'±/Ad@LÐãyëñã>ù8èËàßbŒÞŠˆ!4èKw…C_B_¿Ã@_B_ÚV©K}‰—äxƒÞ—~7™Š¢®®ni×Ýé(¸'ææ-OéôemmÍ 7|XÑ—a¬{ÈB_‚0ƒ e *_õå=ëÖúЗ®6Jìô%ô%ô¥ÙЗ¶çúôe%æìºÙü騲õéÞ¾®ÊÉÏÛS[[³`Á‚x^„З ´À`€Šç |ì ,(§¹5‘K>ììì¸gÝÚ@|¥Yû úúú²àùÙЗf @_ÚžkèKÈ?ôeh¨««ëêZZW§MPÞ·kó–§œ¬>0Î×— З%¿€ /A>ˆƒ *ž'ð±.°³££€œæÖ”«¾t2æ½³³S^FW«>·f /]m”Ø Ó¦OMŸTfìØõ^Èß,_Ñ.]ºréò˜Å ³ÃZu. dáÚÛTsU*Y¥”¹jé,èKèKÒç;ô%%Ô°ÝB‰…Q TÊuˆ qå ‚Ú§Šù(Š’í)¡ÊôŠ+öìÙsæÌYe±tzpó–§6m\o±ó†/¯¾úªÚZuä¸&ö%àRA0ˆ}™¼2¡Î|‚[FEQPb_ |HM¾~øiM\Kn©ö1‰qéïµ } |²xžÀÇV&znm3žû±Ñbp’vœ„/d§Í+:ô¥£&¨#}¹cç!BÈŽïEíqU}åªsY ÝÞŒŒŒ¨î¸÷7çINk¦’•©æJ›€¾,¾d¸gãøŠ¥¾Ô%{á+jõ™f-å^!·ŠD˜ÀÔÅDÑøŽ–· u+”3wT›¦kYבß9räÊYb®ê¾Ù0·¡¾¼æš«§Ï˜®=v>§§5“*zQÅ/ÎÈ"³·#ã#ãä7„’j®L%«V-™awB_:© èKPÐ]ŠÞ—áæª«¯ª««Û¿ÿ-eŽY‚òü‘ãÓ§×677ëôe/ á `0€øXËDÏé8Ô—áQЗóc«/wì¶©À=Ê }YªÖ¾I› úҾܱóPn¨x¤›¡Ð—áyÃ-7}ÉÓû› „Tsåª%Óµ£Ë¡/Tô%(è.…¾t€f9ÍÞ-ºáʺá˚Ȓ’qLLµ8£Tµ‘—ÝÐØpÛÇnýÍ[Μ9£|»eëÓíím›6®Ï×—3gÎøPGa̰ ¦ ×í• ˆÜÈq d)jzŠÊ³D&EéF‘ Ê|MìK¦7ª‚Ø— ÌÁø?~ÜV˜zÞå³È½µÕ—÷ܳ6ôúÒ¬Í}éB_îØyˆ0}S3Pe¡/F&6ÿâtª¹rÕâé©d%ô¥³G–§ú’AeÆæ!}©…¢h“ÊËwi‚ Ù¥ªÈc\ŠI»O¼¹“$~>w÷J|Q7Þ¸üÍ7÷ò3üÖ·õmæë¯¿næÌ™Œ1‰ç5S¼RT­eB¼’‹w)ÇÄÔÅÁTe(û’RÞ`2BMc_jj ±/Aùƒ ˆ;Þ&ð±Í]À€tk"”|¼¿¿¿§ç'ú2Té†\¶¹ /êK­»$З±5X^úRa`dbóK§SÍ›>^oyŸB_B_‚BïRèËŸn¥¯·|‰©CÖ—!ÙÛà[ З rÀ`⎷ |¬»svvt¸n³ó‘Ò—¶CÝ ô¥ÕÂå£/¦OÈ!/ýnš‡þ½oèKo™üæFW-®]µ¤Öè>…¾„¾…ޥЗ>ÝÂRo7Þ¸|tôøtógÍšÙÖÖ6cÆŒXþVzwmC_‚`ÁÄoøôôl³ö‰n}¨5¶ÃÕ‰7 ÞUèK‹ù±Ò—y]/ oš/Z~}¶ê˜bäÙÿ>uâØ)Ãu¯]rËÛ7͹`Êt2­Lå>2þ$òS[ÙêZ܆²31–MŒäí ¶íθÝÒžÆï7¿[L;S·˜p™‰—…ñ3“ãg&u•V[[K¹åæeÊfù­¾ò¹zâw“›“]E[ëŒ;F´‡È’î+~En»ygI»3܆ôµe¶óywwœ„‘ÁóãƒÆ ¯·›šk5GÇ_ŒÂ/M ]Îù@ê=pq`d<ÛúÒøˆ /,‰·öÉi«†:ÎÈÇ|E]üGõÇX)ˆiCe&ê,Iªè˜Û0wÙ²eûöíSæÔÕÍ^´x‘”‘¤Üˆt‰If§[3ŠAŽJòñxèK'1èK§ú2Ùִ즅ͭÙãЗáë}©}‹€¾ V_*{µb¶°bÛs–½yÞ¾§ÏÀÈäæ—ÎmºsV®XèK££+T_æ."J@¹}Yd½ñ*ÍF”SJ5Á+ù‘‚ Û[š[ET~ E1÷ƒH2™L&#qkçÉáSñd2™L&£ü°ª]Éôðø –|Q‚¨fòAÙ¢Èeø!„TpÉê4·ŒYìKª‹}©­3û2àë & vx›ÀǺG¤·>ú2´o®æC_毳î¾Ûå~—ú²¤úR#­.yèËÒèKeΊYdÅLòì 24nóxJÊs&ô¥ñÑ¡/Ñ *;»}¢z‹ë†Ø—~]`xf—ª7¦:º‚ŒAc·®d¸ í€lª\`ù#µ•YÆC¿ Õ=@øç·'¦†Ë¨öùA•Ie@zn#”«EfØ2ãƒrZ] ƒÐ¡/AÔ€Á”'^%ð `ü¸Ãäã%©FèËBè6¯ÙNZ^‘Ñ—GÓ',Ú®KW|„8<ާ¾è ÆZ…©ajXJÞë½Ç75×@_B_‚(Þßþ[²n’f)k(UU#%”ÉÿŸ½ %e=IR%  p‰|8©„\ѷߘiÓÎ,B¥.R§2ÍçÞ‘c_ª_é‹¢jÂE‹-ø ú &  ñ*õøqO´C}Y’ȒЗΚnñÕ—„£é“f5“tÑûÒ¤§À“7<Æ–'®aQÉÖF—™Ç~ˆƒl^òV:9L*œ>¾ÊG_蕾ă ”è¶ö¥ØÂË5³~J|ÊügÙæ˜Q_cÆÑĸäÐpËKLs{Jê³JÒ> tÀ‰öÿYþ.©ãЩý”i T¨mSÒu–V? \L¦ÅΨñ*fÕ®ï‘ÍW5¥Ð— ÒÀ`Ê ¯øX‡ÑììèpXŽ¡M>n»c2Зv-º2×—ÄrùÒq©/Ñ ÀŸ‡Rîu}™xeïTáB}'V-ªpð¨ñ5ö¥îY} €›FD±Å² ‹eÌìÓ}ÅÍäM¥düŸV‰€Œ*P_¿˜i³„Ú>Átë |Æ^†B)3.Y¿yšÝ=MàMG»èÇy}‰§w”Á(r@YáI[…W¼X„¾,OSàèµÜ“uK¯/‰¥ÁlnmPJ€¾ ”%¦ôRb-tÒå«ô%ô%Ó ÁãþÝ|>¥_ÓîycÿJwA_‚>˜€r“>Ö…/Ò‚Ûn‚x!I úÒ£],ô%±6˜mÙ4>õ%š“ø÷ҫ܃-ÂÄpÆx yïÁ‰U‹* Ó—š™ÙE “ƒ»<Θ±/]KXÉäñÉoˆ_F*þå\K´Ù{t™|˜õV—»ØѼ›ý(úúÒÇB /·À`ÊOøXR|`J‡ÉÇKP{З޴èâ¢/-ZKVÜ@\êK¼Kà×ãIù7ÆZè!µÎ^íŠé}é¾ô&uÏúÿø‚½‘ý(¶Àr-TiìK“‘ãzSÏOKÊ?0&IŒ¡Áá¡áa³ýÖ>|´ƒÂjÒòðu_éÿù`éÒ%$?⤲47[’¿¿”OT¬R[2Íͧš¢M¦ug‡rƒÕ™Å!¹iìú}ùrý£©YÀ`ÊOøØ¦Ö)²k¤C}¼"t¢/;;:î¹g-ô¥e‹.FúrÇÎ÷¬Û¬.õ%š•ød=r6±Vb:Š|`$CV˜ÜøÐ—ªP)îu€‚od?Šek˜R’ ãTI›üc IDAT¦Óƒ„=oî-zo½?Òýû÷+ÓÉds2™$„&“É––dÞ)ËLJ5¦Ÿ&Ð'¥TNìãOJÍÂRÁ$üæÐûÒÇ» & ð$OãÇC¨/ûûû{z^p¢/K2°=ü¯ö­»8õ¾”inmp«/ÑŸQr Ë€ éQÉ䯇¾TE‹¯Bwˆb‹ éxçÝ»÷   ±·qìØÈ±c#„PB~CrB³«kiÑooS/ )Õ} tÀ`ÊâøØöŽtØ‹ÓÛžÅQw‹“>¡ú²˜Ö]üô%!¤©e®[}É ðëá¤Ñ—ËèÅ}lº³G™^üåÇ»´Õ—Œñ%XäüQŠö3u³OÝcþè3>:‹'bn(-Ÿ@Y-DÒÓä½jW,&IŒ_E;­t¹âç›iuî,’Dƒ’ܾþ:}¹K—ƒCÁí­oÈBó­·~SŒÊôF_2B‹ö—З TÀ`"' |¬hgG‡“^œ„0ù8ô¥‡mv—m¦rÖ—„¸êF_¢} €?Bï}i±ÁãÞ—Œ9|ì8± !Ò—¸¾€ß·¯?ÅúûÒ\ˆ­ýg„ÁÁ¡]»ö z¾·a@Q™]]K›š›š›!‚Ò'27T\†Ÿ¦”P)ûQ T —)Pª }©~4¦RàªN3ŽÜb°¹«¶£˜/×?Ôe & òŸÀ§§g›µ^t˜Èt¡/ËúÃe›©LôåŽ]ïY× ô%¡P ÚP˜ B;í­¾46†Ð—XÜ»þhìK×r³ NïÞý¦n´¸o{KK{6÷ï‹ÒÔÔ´hÑÂd2©ÌEƒfcŒR*¹.“ Ê|«+†êBŽ*‰€¶14‹ŒMi^“ ¨€]‚¾ôªÑî²ÍTæ½/ !‹–_OÜëKè|{D©ú’YêËÞƒ“«U@_B_‚Ò߸e=x<ܵk·E¿Ër½ÑFGG_~ùW‹-Z¼x‘¯GíM!¥»À /0˜€ãIk—WdlJ‡ÉÇ®4'ú²$)ÑËýÍ£üõeV3¸×—šøó<ÊÞÂY}É–Ñ ûØ Ë§Šª/ùîSºås_IÓšx™Œ[žÏ>lœº‡9Ê´ãD_ºÒ”.ô¥naI’ÌžrËLÆ$e&£Lg¤LFÊ~œÊdÄÜW‚(fLV™šš2 yI©:ÐÔlº%o[úF„ƒÓdù,pT.uÛ]ÑåîîÚµ{׮ݞQD9xðàÁƒ =¦Gú±/¡/ËL@„)>mvb:!:Ô—AŠBÛñì%Ù«x¼yÄE_2Rˆ¾ŒC& w Šb"‘¨®®ž1cFSSÓõ×_ß}÷­]»–R¾9rä‹_üâ¶mÛl÷ò"Ú¥œ¾4º£ <®éIìR™›ÚFÉÁã+¤ú0@pˆ"‹%¥ï}988¸s§}עüâu×=“Rã¬>rN—×-ô%L@T)>¯ãÇm{wY~ûQ}ùþŸÿ›«®â,2@ÈÞ<â¥/IAú]üòéëëëîîÞ½{wss³·%¿ð _üâ9‚JŽƒaù™|ha#›Ø—ŽÂMB_à¶Qd±!¸Vú³g‡>±”éÕ7-#ŒÝ²bi®M!Fd‡X„¾ÔúÊ‚ô%c¬mvuÛìjÂeì&V'Ïß•>=xîÊà…qW•óꫯ566ÜvÛ­a¸~ hŒA_‚€ÁD’âøXÆ"“Ø„-ùxtõevâðaùäȧ¡šÐ—üÛ!úNŽ=úgög¿üå/½-vݺu¨Û89­¾ÌÞÚ¦£c_æ‚­²JÖúÙ“üÙÏžu¾·„ym-·¬\žjM挡—ú’Èë§/µñ…Õm­lÅZfÆv¼0>xaÂá?~âÕW_»ýöÛ}¿€<½ºô%(0˜€èQ|ŸžžmÖFÏIN3¢¨/;;:î¹gm÷÷ïy¸„&ô¥v‰ôe [ÊY$irròâÅ‹ÃÃÃ?ÿùÏ¿÷½ï9sFYìå—_~íµ×n½õV@Ašªlõ%)pð¸ÎzrþKµ!L£<¸ä]g*q[âò{jJׯ Õ—–’$ÓÚ”½¢Ì1·™ÊÇŒ”QÞJ‰_,“É( IäæË*Jù/o*J•U7ÕBfAS†¨QìÍŸóq&gÝÅ3¤ ÝM§ÝéË[VÞxËÊ圲ôA_²<ùX°¾”$ÆU°’b3oJÎ ÍÓÓÆ/Lì¹èäð?ñ«_½²páGšššJrA_‚¨ƒ ˆE&ð± ë wy¶±5IqÙÜîLOÏ NôeFÕ—#-¡ÐŒ§¾d–¯L…èËø6=A¨ªªªªªš3gÎG>ò‘ûî»oõêÕ§NRxòÉ'a0AQÏ(GúÒÉãË$ö¥faÛh:ܲpj} €ˆ"‹õáåc_êìU~ìKW½/Wß|ã-+o”Õ¤¿ú2g-Ó>ÓLçz”«åä­’mêÈVN_æv•´ÕV¶ÕTÜÔX³{äâžã—m+áøñ㯼òë[nYÝÐÐ@Aù÷Q¢¨4ø‡¨à:³[t¶UW1‰‰ÉľÑ1ŠOàc-@;;: Ö^“VQ¶;CB¯/­]³é±k…fGGGggÇ=ëÖz¿Ð—Æ B_Îu×]÷å/ù¯ÿZ}PìÙ³Õ Féô¥£ŽÔ†OOb_B_àà'·ÈbK}‰:×—óÚ[\ÿI’S“èKJ˜¶ï¤ú‚Q–³“$»dvÓÙÊ“@)ÁT_ò;sScm[mÅîÑKC—§l+dÇŽ7n¹eu£Óž˜è}YX)x†GL@Ä(2íê‚užC}ÌHíòЗ^qøðáÇûb0ݼÿ›··ÊH_æ–p«/%üû9Ǻuëxƒ™N§å‰ãÇ·¶¶NMe_’ÉäÐÐa,3ÆXkkë±cDz­½Dbxx¸¡¡ÁpaçH_}õÕýèGo¼ñÆÈÈȬY³®¿þú7~æ3Ÿ©¨¨°>¢ñññçž{îÅ_|ë­·ÒéôåË—«««ÛÛÛ»ººÖ®]{ï½÷VVVš­›¿{GýáøòË/÷÷÷_ºtiöìÙK–,Ù¸qãƒ>hQN|‰N_2æüñÅïæžÒûƒSï§§”Åùx–J-£if~¥q‹ÅÇ”¯›ù¦"Ø,3yf:x&0"šÅ>&ß}ÊþN©ªªR¦EQs}¬DQTR‹¢(&Ԭ QTG‘‹ê|Aþèc·)Ñ.A ÜÈñìtÞNò»Í÷êÒŸÆ<• Àþ'סb& æ°(g[¡\±fùÇu·€s}ùІûRm-AêK¹%B˜ÁÐoÕ?*cÉ5CË•'ª~k})?ÿZkŸœ?cðÒä³Gí•ïØñÆš®±NÎ0xœ@_ÆL@”(2íïb:HB_úq ^ÕÑÑØ+‰}#©œõ¥úžãN_âݘ£­­ÿxåÊy¢±±ñöÛoÿÅ/~!µ×s2Ý´dËÌæEî¶–Ê©÷å=ëÖvz!尘ػFR™èKf)L\ëK†6%/ !Êôƒ>ÈõóŸÿܰÝü‡~¸X?rë­¼¾äy÷Ýwïºë.¥g(Ïŋ׭[÷ùÏ^§/u ?üðÃ?üðää¤õnlذá+_ù ¯/y:t÷ÝwãÊ»µ9}ÉÜ>ÊLÆwм/ýl‚Y3ì;­=Ib¹?IÊdä¿;wÙ–ÿІûb¨/•ùŸl¯i­­«èĉ¿~å×ãccãcccWÆ._¹"ÿ]ŸŸŸTþ&'§”¿©Œú—‘2RîIÆ6~‹ÜlÆ£=éЗ ô & 2™ÀÇV2lô“|Ü¡¾ ¬7h‘tvvòû)÷Àíï?l››¨T¯$ö¤r×—Ü6¡/ çÙg5XZ[[•é?ù“?™>}úÅ‹Ùah?ÿùÏ¿ùÍoæ—ðüóÏ+Ó³gÏ^·®Ø”Vû÷ï·øö·¿ýíO<ñ™Ï|†Ÿ™Éd>ùÉO¾üòË7ñÄOŒoݺ•šgÈýéOj]ÈáÇÿó?ÿósŸû®"Í «Ñ—ÌÍ£Ì$ö%nY ¾!í/‹)ö§?{vppÈz™‡6Ü7¯½-¶úR.ùOÛª/O=74fQQ§Nêíí[µªÛë‡ï©í|•]ÿЗ1}0Ñ È>þ¾|ì±ï–“¾Ìçž{ÖÝsϺGý›ÿáþý‡?pÕ7Óÿ ˜Ð—ü6 ЗhYfùíoûOÿôOüœ»îºK™®©©¹÷Þ{ù'Û{ï½§+áСC|†ûï¿Ú´iܹaF'Œ1»ñÅúЇžyæ™Ó§OŸ9sæ?þã?ª««ùoŸ{î9Ýò?þ¸N_~øÃþÉO~2444111<<üä“OÞpà üO?ýô÷¿ÿ}Û*ºêª«ž}öÙ .œ;wîñÇ× ŒÍß“˜Ûú’m;½¾TÍê5dLNMÉùdL8˜î{"ðÀ?ñbÑëM‹Äýqs5ý!]ÃL:àILSžùNÆÈ®]{ /èKyOÚ¦Ñ{“6›O:uòäIÏ-ƒHƒ ˆÅ$ð±í™h@Ó¢dÛþ€éK'=£«/uôôl SOLèK훌{}ç`kŒ±ñññ‘‘‘Ý»wõ«_]±bŹsç”oEQ|àøåuÉó…nŽ“ø’¶466öööþéŸþi]]ÝìÙ³?ûÙÏþó?ÿ3¿À»ï¾Ë<{öì׿þu~Ά Þzë­‡z¨¥¥¥¢¢"™L>ðÀû÷ï_¿~=¿Ø?þã?ž9sÆbOZZZvîÜyï½÷NŸ>}æÌ™<òÈ#<Â/pèÐ!übjnY¾dŽe¦™ÇñJ¦LÈäÆöJ²µäþ˜3P·ž>í »‰yfpj˜·(æ(V žÁÁÁÝ{Þ´>èKE_Ê+¶Mîm¶Éƒ××·Óó&G«xxý{Q žce & ºÊâ[[ÿh=ü¼³£Ã6€¦!“P?±Ò—ýýý=ö]눨:|NãO}ɬ• q©/cèEhA¦M›–L&W®\ùµ¯}íÒ¥Kübû·«KMó±},™L*óCaòs,X°jÕªâ÷ö¯þê¯øpœ„üãüG]?‘ÿþïÿ>þ¼òñÚk¯ý¯ÿú¯üDá•••?þñ¯½öZeÎùóçŸxâ «çØ£êòlÚ´‰ÿxêÔ)üh­ÛÐèKæäß! ô%dÅÝ‹~Ýâ¾î®Î]rñ15fs÷nèKwúRžn­¢÷6ÙÄÄ|£·—ûG Ó^Õf=¬õým¹3ªÙsëß›¥‚ºþ¡/ & ƪ˜>¶õ¬ûoZìUH’ÇJ_öôl{ì;»í}égèKÓ&/ôeñ¬]»ök_ûšn¦(Š6lP>îÝ»wxxXù844´oß>åãƒ>è6Ö•!wÜq‡noQ !—/_æ?nÛ¦ùg§Gy¤ªªÊ°äªª*]'J%Ùº!kÖ¬ÑÍÑ¥AÕ£yx¯/1¤·w¢o7xP»ËßöºgÉ®]»‡¬ÆC_êKyÉÖJroƒ•“9yâäè訩ö±6Œ‘$‰éÚx6Q"Š´–_ÿЗ€ƒ ?Å$𱵟{½éËÎŽŽ2Зt½ øõÄA{ úÒT_j¥ •••_úÒ—ž{î9CñÇ$gŒñy{žþyþ¶ø,ä2 ,ÐÍ©©©á?f2þãÁƒù·Ýv›Eáºoß~ûm‹…ÛÚÚtsfΜÄ+}TÅ ÑëKËú¾Àû»0RúÒm±éôàîÝ{,€¾´Ð—òÇÖJÚjs÷®=Eÿ°R]·Ð—À[‹jŠLàcímW7-Ö.­yÁ%»ª™žžéË@qúJO϶bÜe°i|ÌÚ[±Ñ—¤}óf¦ UUU3fÌhhhX°`ÁêÕ«7nÜØÞÞn¶üÒ¥K¯»î:%àãsÿ?{ïfGUæÿ¾kÕÞHw§síî$}ïÞ»…¹*$ÜCH:Qe‹ŒÎ¨#àyæÏùyÎyž9Ïyâ (è88ê¨#â$Ý!( ˜ÎŒ½û’{w€@.!Iw­÷üQ·Uµë¾k_zï÷;L¬®ËªU«ªV­õÙïåÑGÍìÛrÌüãôØrPBP?Ç7‡S¹œN=_Ž­o½õV¤š¤ÓiúVôN2¾ôñ`òhß_|“(q¥éðáÜqãÝäÜx=SŠb¾ªŒ1¡¶½6)kJÈ$³mÌ’†‰Œk÷ ćHgíǫ洃F¡ ·aø3uä@3÷眙G0Æ€1½ÒgÌz^xáŸkélo#|é/µ5ŸZ¾ Ç<[2—Éf3q?„/IÕ#"˜$©¢UHŸ@ÎíUBòñ0>ìPø2$¨-? oU¾ô ™±ðeÍ5 4Ÿ¹îºë¾ñohËÏ<óÌ¡C‡ššš<¸uëVsŸDrø|Ã9bôçétzzz:^Sø—œL“HJp_†èmhBH"EíçgL¹hái[ŸÌÜN­S8{öîݳg¯O'|ý_^Eø2_jÆïg5àoÞñlÉ‘ÜHww·#›šüS¢ì ¡ªŠëÀ ¦ÃnܶI:3JÞæ…|a _’Š!ò"'‘H•«Bøf —c‡ðe‰€ðQ/û=Òõô—.×x«†ð¥¹gd|I£ÍˆºöÚkÍyÅÔÔÔÐÐ MMMi+ëêêi¾K©E‹ÉÊ‘:óµgÏùOG¢‡ˆWFî¢ø2x6(9{ö*ôÆ’HA/άrÃv(Zvtˆˆ/¼à?Nø2$¾À¥uxf½ßÍÉLÛåÓ „©eõ1“÷®¹åíƒÖdr÷¾$ID0I$R…ªÀ>Åð¤¢×®3Rj_Fz¹vÍ€Wºžb¦ñ 3Þª5|iV&¾¤Ø…QÕÙÙ)'×òËYÈW¯^=þürUïÄO”ÿ|ê©§|v~úé§å?O=õTº¿ wG2¾ÄPs?ôìÇèU%Íì¢4_›RâËûïÿÎèèXa¥Æ©îž={÷z'ðéh_ÚѶ„ðeH| ˆ ð¬zuIÚó^lß¾½è·5¡—ð%©x"/r‰T¡*$¿ÿx<º2ùxQÛ¤Fðe¤¨—ýÙìÚµýýýþ»¥æ~©jÇ—5Ðø’Œ0cèºë®{î¹ç´åÇ{ìСC›7o6·úäðaŒÉ!„k@´BtñÅË•¹÷Þ{o¸á׬DG½÷Þ{å5ts‹ÂlÌisèy/êo3‚9H%Ї‡›o1—b_¦R)3„Ã~YþSÞÀ8wX¯BE}>ÍÆ&!²DTóâ]~÷ÆÆÆGGøÒ—þº§§ÇüÐæî¯SŠb£(Š0bSsÆ8gòþZ!Úòž½{òK6‹½îêO£P P(‘¾ôÅ—ÚÂY³ÕßòD4ÛÇ·gÂEÃ$|IªV‘ &‰DªD’ÀgÆþ–’þÑ3½êSöäã!ñåw¬›¹ø2†éåwÞ®5ûðpÎãv¯)Óô¨ºñ%µB4|I3†>ó™Ï˜Lðý÷ßÿ»¿û»÷ß_ûsÁ‚«V­ò:БúæÃ?L¼n×\s°òþçn¼ñÆcÇœI Ž;vã7›k,Xð¹Ï}Žnn’’_¢w7ï¥w“DŠ;(r¹¾Ô–|ðãããÑKÅÀìÇÖòK/ýÑë¨e? „ñ¥Æ( _úãKDX’KR«aGFœŽäîåªåHnÊ‹è¡ò?ÿ„/IÞ"‚I"‘*Q±øûžûFÏŒW(>¾¼ë®{BâËbç@/ž¢F½¼óŽu2,¶—ö_[óæÍ“1å÷¿ÿ}sùꫯÎÏxcª±±Qþóá‡>räÈk¯½öâ‹/&U·¥K—~õ«_•×üüç??ýôÓò“ŸLNNNMMíÛ·ïg?ûÙgœñóŸÿ\Þíÿñèæ&Ü/Ùð%†î@¸—IƘ$Rà@ ÈåÊøRSTˆ’U™Á/åþá¥?üѧØe?3_"áË@|©xæñ~yðö¿õ–:=­ý7­ZÿùâK4þ³–e€)´êUÈóOø’ä+"˜$©I–ÏVéï{ÞŸÍÆ°È Ìi/ªf¤ „Ás3_bzxkÊ1k©q| _’wj<]wÝu®sQÿ,ä}}}òŸ7ß|óñÇÚi§=ðÀ Öíþá>ö±Ékþô§?]ýõK–,©««[¼xñµ×^ûúë¯Ë;|ñ‹_ü¾@·5ùÞI—ù¯š7¾D¾ŽÞV)h Pär¿ó]'¾Ôôàƒ?߮ԂªëÈÀ&ë‚/…É1 _àKXšR—(ªWË8p0áç Èyœ4“Dq0I$Re©>¤/†{uÙ“W=¾Œõ2¿÷ýK“Ƨñ¥ßx1¾$‚O«W¯ž;wîÁƒ¶ùL6›=çœs|ŽºüòË_zÉ%l DÍž={Ë–-W\qÅóÏ?fÿ›nºéþûï§{š|ïäÀ—ˆÞÝ“–ÝL5õ‰7\zvú‚Ó-3ˆcǬÉö´ªš¡ú¦UUUÑXª0Ãäâ´j-OM ³.Ò>FMm•–~ð°UPte~‰‰úRϤFˆðòpý˹z×¶?é/N9üëñÇo½ õ³Í’Óét*•6—wìØ™tŸ9b¯T¤8˜j:­ªúÝaŒq9ö¥ì’ûCžy‹-KñÀ† ºŠh};äå’¢šÇ>ÝÎ:®JŽ}éUÔCýàóŸ¿¡««KÿìZMêyöinÅÁL¥Ræ!Œ1Ƙv$W8g\ÛĹp%˜ˆÀœwöB¨€Èhø]X$áKw|©­?ó¸©ÿø@q½YÛ·oïééNì¹­I|ICÎ-²Á$‘H•Ƴb&ð L#ÍáË¢ªx¦—¥©Ú— _’#y<Íš5ë3ŸùŒc¥¿&|ík_;á„ò×'K0`áÂ…O?ýôúõë-Zä³[GGÇ/ùËï~÷»‰'"åãˉÔñAÝ—§¹¼Rxô"Ê&œèAѱŒAÝWÙð%‰äöœ·X|©éᇼcÇŽâ]¯kL­²zþqEºàK47¾ôÀ—ˆècƒ Hèû@ø’4óD6˜$©‚THÿ0‘1½‘(Ä2ê ¯0øÒË&±ò•ˆé¥t³Ê”ƇðežÞÞwðe)uÝu×=ôÐC柌±k¯½Öÿyóæ½ôÒKëׯä‘GÆÆÆ ¹¹ùÄOôIþ ™JÝvÛm7ß|ó† †††^~ùå]»v½ÿþûÇ|{{ûgœñÉO~ò“Ÿü¤#¹)¹> lÖ—“Êl¯îàÜ“™Ô³‡ÀóÁ—¾½JT| 3_R÷Uó/Y…áKM?üãÏþ†ÎÎŽpý„v>ý{­ÅQ7r³²K—È,2_êÎã@øÒ_jËgÔ}ùØ,×[²ÿÛ³ëë€+ÖÏ~)Å;SŠI…u{•”Â$Cl–³úREQ {µ£ _’J,"˜$©‚;O`¨Ê¨¨1dòñ"µÃððð† ƒaðåLL;òê¬û¾f D–?OíáËåçdž}q$ÿ4oï;hÖ4<¾¬/ò"]£™\Ó'>ñ‰îî`ÿ²ÆÆÆo~ó›ßüæ7 ©mø+ª««» ˆ0 IDATꪫ®ºêª"µE!ðíš,|i¾×!¬/“Æ—ÎÂcã˨Ì4•FÇ—‚ž°ZÁJˆ/ï¿ÿ;!ñ¥¦‡þñ 7\bFÒÞ‰IΙë;±ììÓu)2 |_‚À%|À`:t¨bžÿr•Bø²vE“D"UŠb'ðIÜ<$¾,’åc˜³ÃŒÅ—Éš^^BF࿲ê¬/²yDÆ—D âéÈ‘#ßøÆ7ä5˜¤Z,&¾Ô§Ü^ÝNÒø%’­ûŠJ+_úôof GË .–YºªªrìBsA¨ªÊ0`À@VQ^v^”ŠÂâÄŒŒ¼—G¼KùO4ð–~$cníœì·¿@ùB@Žƒi]”ÐY~ç»ŒŽŽE=Áü¯×^û¹ööv0‚Èš›ä›ë°¹3×sÎ3[!ô/»’)ŸB·céb¡çç1óö¸…¿òJ@—| €K¹gFò !˜„/IeL‰TŠÀ'÷ùç.wáËbÝâ¤M/å;¢9’ËOQ‰ÒøÔ(¾ &FÇ—4´ «;w¶¶¶=zôõ×_ÿû¿ÿû×^{ÍÜÔØØxÍ5×P‘l/«Œ/ö¦g‡ïv )|³Û²¾,¬ T ÎÅ3S÷㜤’ÞÔÒkÙæsZÆ'9øQÇÀ]“X’•›Z*ÖeqË–ÇcàKM?ýéÏ®¾úêöö6ÇO…\"˜ö-ÌÌÙÅ9çœËD[|ååWP 2«âœë´}I+Õø 2ÈŒ…|(IøÒ/µÎHyyú8×Ûzèࡦ¹MeþºÞ9¾$EL‰TŠÀÇÿÀþl6j$Ä@‡ô!5CªŠñe±M/ûûûµýµÛ=<<<<œ+ Á$|é¢Ük;2§v¾,’´ ³®ºå–[æÎKMD²½°¾ÄÉt½k·ÓÞìz,áKÿ P÷U³/–²ØË.[1:6É…\Ö/~ñ‹«¯þl[[›Wa,ôÞÁºÚ¹ãygž&;†3ÙîÒ@„/CáKÿgìÀõ õSǦÌ5Ç”cæ²’’j‘™.ÿ*È¿IØ~ŸP¸{—(ýì¡%§9 õyJfÔ]„ó*X”ó‘D"•_±ølذÑߦÏ't¦«Ê˜|¼ZñeYŽ÷÷÷¯]»¦L Žj_.?'ã;^Œ†/)™OáÊd2rÉ/}ºŽŸ9_\|¦Ÿ /B ÎòàKRM¾U¥Å—šnþêM½½=± ÿÅ/~¹{÷îDê991éõü£R\KaáKb:¡¤Æì_‚¾D<ƒNô«¤‚_’âŠl0I$Rù/O ãyT_ïÊÇ—Åó^/’Jõ²bhA¤ù|µY_jýÏ]½§´G×4Æ,LùÈGëëë©)HÎÙ›„/_ž½(Ô$°|É¤ðƒŒ¡¹Ì™évªm²–¹þ€ìšŠÀ€"2`ÀÐZ¦s©ŒUtþ•úˆ([$Ù¯ŽÉWj3AbŒy¹Rj¥1Ƙq<ã̬3ÃSpÆÍMÌ´rŠtŸ…œCà…aE'„ž<š 0ÙºNxxŽƒ=fò¢Ë—šxÅ»”¬µT1²õ šDˆ3krRä±(åH#&€¾lÁ}åË_úÎw¿·cÇŽx§ûå/ÿýÊ+¯\²t‰ñ20óQ…°Çä4–$/rι:sbb³´/nªÊ€ƒ¹–ÉGó+7bËhO;.Ô¥@„/Áª[¢Ÿ‚ *ˆð%©Á$‘Hå‡\>[}¢Xú#¿¨¾Þé€ zBó $§úÙg¾,jÔËŠEîËÕ‹/;ÛæïÜóŽg¢àKd†×ÙgŸ=66vèÐ!ÎùÂ… O9å”OúÓ×_}]]5É}gü;‘šíµã¹'ö$rHD!gIq,3¦‘4Xœ¾Ì™Y ãÌ 'Œqn­×m·Açf¨5¯õZe$"é¨9s.{ˆò¢\ º,£Æ4™~%˜3®ÿkb\[HAÎ 6*GdœÅºÏï“òÀ¡WÝž–ÇÏ âÅ-öŒFÐ4 ô@Ó)(Ç%aEà˜ðµ¢ÛÍÐð—“Akúâþê‡ÿü£Øó‘GY»v­1™ÔB\XÊŒ3«^9×qª¢(Ú#¤ÅÀdy‰’ÚZ›U¡r@!Á8BèÐÖSjË-fËE„/ÑÍpÞ©={ö´µ·•äQìKRˆ&‰D*7犕À'0Ze$Ú2ùx1Z *ñeí™^BmZ_úÌÑ×v÷~´ðe1ôâ‹/R#"Oãa²nvðζؗ®®ÙAa(þÚA]YØ} ¨2AzÚ–Ê‹a‚lº^©Úߤ¢¾¡¡U Äܰaƒ 1«„ˆBæº 9¡ ïöA3€ ‚È@Ç—&¹3ú"9ü%áKO|‰xûàet÷®˜žžš–â]N«Vîòé)kYÆÍÆ/Lúz.áHÙ(›;Í—Í7€ð%©ü¢8˜$©œŠ—À'Ð^2m ‰/‹Øª_–%êe…Íkj_"`GÛ|¯yçC‘ð%ÅÁ$‘ŠGáåúE^{éi|°€ÌãQðe^¸É™‹/I5ó&õ ¨/~á¯|Rº…{o˜Ø;Q„V2óaY_êPÏÌäˆw)¤”>‚ð¥Œ/+é'^r'UŠÈ“D"•M±ø$ë?Nø2)Õ¤é%Ô,¾€No‚yàwßyãм–9!ñ¥ì%G"‘F$€8‘ö4Àìh‰Œ/å0x¦Ï2è±/ußmÎuŸh[¯†Œq4ý»™¢XË`ÅÁdF€\ÃyŒ–(íãÞ 2ÿ.ÖÜÑë@×e­'³®øx‘snþk®1Báܸ"àŠ#Ú y£õ@¢èM’ÉëÁ#ú¤£Q\#Qƪ‚wøHÏ8˜BäŸ]‹t©}2‘ ¡µ¿æmÍÓ"aº~kÕ øÐØZZXfζ×Lòª«­ªª¶pãçoøá?ÿ(vrž 6 ´¶¶êO£y“1+^*W8cÖæœ™í,?ØfµÕéi (@®pƒQš/¿ñi)ËõAÐèXÐæl^“ø+äç]—¤ L‰T6ÅKàãï?5ÙN 7zTþ¼ør1¾šŒz™?ºª-|©iÙ9½Ï½8æºÛ7ÞÛÜ_Ò€“D*/1ðådg¢§sOòÀ—à™ÜÆ#"cÈ šÆ¥0”zú3’$“È 7CR14Íx—ÂÀ—,/s$k1¬/¹Ô•åÅÁ4töpÖz·™UgÒ‰p‚©8 ¦¢(&RE¢™ çŠA0Î pÕ´°¯EBÌú#ǾÄb=‰^F¸àŠOög^™|4pÙä¦5Ÿ=“vU•ð©±Å¾ôøÈÊSµÇÁ”iæ5×üå¿ýÛÏcCÌÁÁÁËV^¦ALDëwDÆ­x© *2ÐD…¿öêkB ç¶»£Ç»BUU®™RjГƒì@ËË%Ý !¬N,|iÁ;)ÛO-ã˲?´„/I•%"˜$©<Š—ÀgÆþ˜Ì‹{ºª,ÉÇC¾âå=/Æ­¬IÓKï‘VÍàKìl›ÿ¸Ìñÿ½·ë¤%¡ñ%:I¤âôLˆ0á/Û› q·I:Ð͑ܳkÁë]+ƒð$r£‡ ¼jô¾R)Æ‹™Ì“S Äܲy‹ 1íµ³,aå„?þgŸr"t|‰³€‘kIŠw‰¦ãy>¾”páK¯‡Èæ¿"z¶õÂJ åj íÚEáåxÚ _’$¾ìÏfï¼cáKŸõ3_jZvN¯WÅv¼¾7$¾¤„ä$Rñä“ÃÎ;Y{û²¾DŒ°žð%©öè‰yÍ5¹téÒØ‡oÙ¼err2‰+’õ¥ÍˆœRH¹È½ð%¾9fR£)—¤ŠÙ`’H¤R+^Ÿ@“ÉðÔ/dòñįºjð%™^º´j_àr÷P˜ú>Û_ßÛuÒb—4ú$‘Ф½éÙ>˜çžäÄ—LŽhiEtDDn½é)ÍÍn¤ßQ×€¨ªf.¤˜R…¹¬ fö<ÂÃ1ß´“ëëÑÕHÌ'ŹâÑšë…½ÅØ9àDu)®pw‹ºº:¨›U'¯1m¦êfÍ2/$]—N¥RZ?˜N§E‰z—9×a  E%|º]9©K˜¢|vs=ÄÑç ¡šËª:mî3­ªÒÑzÌS…0sË0εæRU-b¤˜šVUU€žÕÒÑ£G@‹fTáâE³7yƒBj×enššš6—™Ëˆk6lØ09Dnڴ颋.jnnU¨BÕϨ( WôS©TŠ1~lꘪªB0-•RÇŽ@&°3Ó‹# pœ1íOÀèÒ ‘+¥ ׳™Ô<¾ô4À |+ |P)¥ AuQÏCšÉ"L‰TjÅHàh28†Ä—Éj¬&|I¦—PUŽ/µÿéh›çU±š<øæ{!ð%@I¤¢| ºf`/¶ŠŽ !#uMËN|éµ3F8 ÷E»"Rµ¼5E{‹:üX»víâ%‹cóÔSO½ùæ›òûcËnZGúf”€:ðŸlŒ‰&…´Dé@‘o¶Y“øÒ÷yBË_eü§¹Ô$o±yÌÉ×éáKgÒçB"ü¾$MD0I$RI/?þ‹”.¼bñåw¬«p|9<<|×]÷„OÚ³vÍÀwÞ^I{ j_Àr›#¹³œš Ä—H €D*‚¢¤ /¾„à!*U :ð%©ïy±† ÅÅ—ú¸+9ˆé=@¿Êá/1_b>ÖtÊÚÆ—¾ƒ#—ˆ^îpR¾lŒôD%=*NøÍ#|Y›"/r©b‘V_…‡s¹ |îºëÿbÿÀ¢"ÁÐg c®˜86M\”p<úªVð%v¶Íëh›·kÏ׫>ôæû;þ´¯ó¤V|‰ˆCÿû§F¾R4è¬ èëÍSë†:1æ(ù¤ËÃt¬¶äHÂnjL_À˜^˜¥yˆßV¬IRÌSȸ-ÃT´ še‚•FlnMt\ª¿AóŽ1æXæÙš£bÝÇ×½1Ûq+›ç6Àígl‰bs0×È$Hšà™„HšLÿgŸ×Uˆ”~ì»YmïX#—c^·Õ®Rá–•°ûÙ¥­¶ú[ùu.ýé²Õ^[ÚYtÔ¤‰§t!è¨ ØKDœHÏ\ÐãÓ{w²(¾ Œ}Iø’Dø²Xõk×âNþÔSO­¸lÅ¢EÍqÖm*]ð¥þõfy—›_^³ƒ•i¦ (k_¢@à%ìj_’ˆ`’H$’‚ENà“ ÿxé“W¾¤¨—„/ÃLã—ŸÓó“_½ìÕN»þk_Ss}ãÂÙ^øRFx„/ˈ/1Ä´€ðåŒÀ—ä?~îIrìK0£'¢ êÙð¥=Á 2ˆÀ´¸vúzƘQ5†Üº|¡pcYÕò‘#>¦y5BÛÇ_Â10Bêo|‰’;šßúÌÔgYǹGØÊt: uuuæã+/ÏšUg>¶uuu©”¢…;¬««K¥"OÐ#X!cŒsΘ~]Q#T ·ýñåWþøò+eÿbÿÝßý= Åõø–Ç—-[¶hÑ"0â`j{*•bœ›šRU•1–õè±£&R4:a )*œã ä €é}3äÁd`} Ðà†ˆZxÍÚ×Hø’ð%É)ò"'‘H%a¹\Ä>þ×^^ç„/“E½Œ7‚«5| m󽓒¼þôáË€/ƒ§„/g¾Ü8¿{rVƒÏm4R{ƒ?OëË€e “(<ŒÉ§'IL _Bø’`ªRÏ=÷Ü[o½`ÙòŒHÑìiQØ{3!ù»8[Ô¯6ñ%úŠQŽW‰aäSáh&—¤ŠÙ`’H¤éî{"'ðñ·ÙìÏfC²@CNˆâŠ^#ø’L/ _†Ç—š<’’[ú¯gÆÿâ‚.—3_bÀóMø²²ñåD]½?¾¼æ"µ$ø"âK /—Kˆ/I¤ê…˜Ë–-kmmM _ÚI›–‹\X/‘á?n•c£~5Œ/C÷3^nÛpÅÖm#$˜HqFáK¤Þ›&‰D"ù+ ü¥›)å† ýñ™+ôt%qa²÷”_Vx¤HŠzIø2*¾ÔtÝUgøø’¿ûÖÿõÌö–w¾œøÒíñ |9Sð¥øËöfìh) _¢Ê”ŸAø’Dš¹ó‚ /hii =r&G{Œé›˜ô`R.rf•CøÒ¿ŸI<¾$Á$‘H•ÖWïì!ã+ ç6úâ°5kñt†‡‡ý ZHëÅø2)úÒn1qõE¦—„/cãK{J/ˆyø¿·î8áü—‹/1ÌWƒðåŒÅ—ð¹‹EH|ÉÉwÁXff *P˜Q+Æ™Õ8ˆZ ÀôX™¯z¼Kf¾ ù$mîŒa¼Ñõš¾R?©\ôë@ýrõe&½;Ì^ˆí* fœ»GåJ§Ò µ¬««Ó“™ËÚ³’N§Óé”Sñ¬é#ΰ0Æ7‡gŒ1i™>Ó¤hzæégÎ=÷Ü hO²Â•©©)UU9gòk¥ª Ž=ˆœ‰ÿc¦“¸à ®ub¸¾‰ƒüe¶È#2”m3k_z†Â$|Iø’&‰D"Mþ,òŽÛ]ìý±cøŒá%Æ—§«p|I¦—„/ Á—Úÿ\åéÿú«Wvíõ„˜ïí?ü?[w~dyáËJÆ—èó¨¾œÉøÒð = ™ÇÃv;n™Ç1бd| !ð%HøÒ¯pÄ0Ö—4&U¹~÷»ß}âÜO,\¸0arà?”ø³ò‰s¦wÂBH¿H ÏæŠnØf¾$|Iø’D“D"[þ |²ÙlÖ‰ïº';†#€å„'¡U/Éô’ðe"øR+|ù9=?yÄ'í,¾·ÿðŸŸÝÕ¿¼ðe…âKÿ0˜„/+5óø+þž†ÿxÙñe… “º‡ð%‰Týþw¿ÿĹŸhmm(Pøú5[V“|©aAB˜ä„Ða6Ò§„°>ƒ`ÐטßÍX ªPå?­e«‹sb;ù7"´—}ð+>BÚx¾$*ÊEN"‘Š«€>kò‰§?G ³²”ÉÇg:¾¤„ã„/Ä—ÐÙ6ïº+?æ¿ó{û¿üëáwß:Lø²âœÇÁmÚd›¾¬¼Ìãaðåç.ññ%FÅ—!º/—$Ò ˜û÷ï I]Ÿ_" èøRë+…•ó`QÏ<ž÷­©=|éìg’êv°L…¾$% ²Á$‘*´O,°Ø é©Ã_æ`úÏV“„/ÃWžL/ _&‹/µ?:Ûæ-;»û¹—¶û7×Èó{Z?2¿õ#ó_V¾DßGžðeEáˉôìWšý3Gמ“HÏÔ=vÛÌÜéq. –ÄÃ<1@aEž4¢OYŠåÎÓžÒÞiIë¥cýà,cÒJæÒÃ:ÃØ] çÌÓôˆqœs颙3“1ιþÜp…›Á49ç,zÄJƘ²”ŠwI*‚þû¿þû¼eç ¡¢°|¿‡@UUBU¸‘À…ʵ 2‚síCËC®u$B0àÈl_ nJßDD z2÷¬Z|‰è5†$|Iø’&‰D"%OÇr‡üq˜“<…¿ ‡‡‡‘\‰ñe‚Ñ6E½,Ö¬M|©ýÏòszvî9°kïAÿBöýù÷÷nýÈü†…Ǿ¬|é}‹ðe¥àËWêš^j o}Étð(Ýã3ü1£n7³€ ÆùhK1D4›¶ ‰6¢™!Ñ-sŽSº­·gÝq¤ëÉÛóñ¥ï²Wဠg^ÀQƒ’œ+æ-ãŒ#Cs«ùF+œs®hÅr®Ä!˜œ1¡L‘ ð9˜Q2 ZÕ¼yóÎ>ûlUU…ˆç™*ƒ:= Óªj2;‘RLÈÈ€ Ðc_2Î8p­gVg"ÛlÊßa¦5×ñŸ(³šñ%$/‘ð%‰&‰D"yiã`´>6æ|ÉãÚµ‰ Å~è ´ô¬X|I¦—„/‹/5]•–Õç ÿàõýýGFŸŸhéŸÛÜ?ðeàË~Â—åÆ—{5Ó˺ú0½Ò5©ÑÇå<ŠSyÇsו^' ôûvuï+ŠÔQG1\%‘ª_žyÖ™a™“ÓyÜ„ƒ’ó8F—Bú%…`Ìê“õå<|)^íøR&¹ybŒ¡ÇQW*ƒRðqà± ¢.3ÊO%„/ID0I$RÅkx8—ËxmÍOà3<œ árÞŸ¾L„'Î\|I¦—„/Kƒ/íó@`Û½1|ðáƒÍýs[ú›_–_¢ÿ„ƒðeYñåDzöˡ٥†/;ZââË0€/”gzˆÌã„/I¤Ž/Ñ_‚@!‘GnÐLÐSù˜‰}Œ3¸X_" «|‰ ‘¼„ˆ`¬2_’ˆ`’HEÅÅïìBž¢rjâó ›O ÷¬¿×G¬vëÏÔ²ÙìÚ5ò!®µ"|é/2½$|Yb|©­¿þª=ûâx^LLw½9|ðÍ჋²se›_–_j%2ÿL>„/Ë‚/£²Ë¸©{ì¹jCô fàJG˜¿t_òp=Œçz»Û;ó¸"¶‹Ãˆ=|È#´ŠÍA›39&¦Ùð̾>†90=þ%s½5@þߤxøòŒ3ÏB€*TFþyL®Ù婪 ˆB(úÇ QÕìrG¦Å¾D`Âìi2ÆŒ!c¶Ä>nøj_¢­&|Iø’D“D"Gþá/× ¬v`Þ}ÏzƒMç÷}×]÷øï2 PµâK2½$|Y|©ýÏòsz:Ûæýä‘m![ó­Ü»oåÞ=¿nö‚Y ³„/K‹/ƒf„/K‹/'Ò³'Òõ¯Ô/ŠÔ%%€/R~‰˜+Ó^ 3’ñ8’Þhþ Vþ ¶¡ØP géCifà¶‹È;‘önI17Ñ­”s9+ãR1·`š€‚1O:ȘÓòôäœk0H[o,j~˜“ÅËäcÅ…*5/b'Á4vP¥/«F;…A@#ö% Dæk¾âÜüŠZ †#tmãK!_¾$Á$‘HÅÕp.7èO0í |üýÍÁ-bf ª˜T*ð0ø²Òð™^¾,/¾Ôöï\:÷ºOŸö“_¿¾Y¿sìð;Çö¼wüüºÙ ꎛWwü‚ºñåûÛi;Ôw¥ _ºàK´ÏáK|9³&Ø,\‚G㇅àˉÔì‰ôlxeöÂ]Ò¹'‰óNÆäc_B°¯·½ãÒzÐe…rOLçqÄ\ãQÐÇTÅøòÔSO<’ð¥D…Ô-#œ2é³ÃÁI…nF/u*JøÒÑ©sŒKø’D“D"U±üñåíëns¬ñ÷7ϘY.|’&…J“™^¾¬|©ýogÛ¼o|í‚}ä5)Ay(}øÎ±ß9fþyÜüôñóëü ÍøYvZG¨ÇªyWa¶‚œÞYþÃ9¶ì³LOP»i'¸ÔJ*Wnf¿- ,r 6ô%ÕØVºmµ³<è ÊÇ:RõJ¦>Tù‡júƒºüñÁ‘cðÂÄ{¼Ë»z—’ó¯Æýúò®ï…°ßCf_p–ŒyÏ‚½-íu¶/Ønºs]^R8Á4‚ À­Í§Ot;…\«ð‰ÔìÉôl,lxÍEjG $Ž/ÃàË(!) _’H©øRÑtsæÏ2ùøRû‘ÌĈÌ_JŒÏ/­Ÿšª_&åE^,h`\äåBø²úE“TÕ\¡$½XŒ³R±ŸÎqx  R.7â›À'“Ífäýñ%xû›… çrT1|d* _’éeiº·7…ð¥_Œë¯ÊsÝïðÑcðâÄ{Ř½D,“Eh¼b7©Væ~ä+©¹q«¹*†ç8T¾t‹‰éU sÌh1¨aìWÇœW‡€Ò­‘¯ÔÃù#5ÅÑÃ>,6m´=CŒ…iE?Wâºì²B5UZžš²ºVU¡êm(„0âêéYÀÜäzäê;®Åˆ"Šæ²æìÌìûí·÷¿ßp 'TÔäCôBŽÓÉmåµ¼ÿþýû÷Ç®USSÓG?zŠÚ’1Á…fò‚sÝ¥AVé ¼qàÝEsf£PwD÷"×~ÒH¦N6…@ÎP å-`r@6(ÓL_""C¨|YV"Gø’T"‚I"•þc€%.¹4º6‚\ï·|öXmCl¹@ÿñÛ‚ ]îî{‚³÷xi3_’éeùÞk—Ái|;ÛæÆ3ÆLÍ •_ ¨q> IDATVj;W~“&~3¢ØÞŒç$:Zò-5¾Ôh’D&…}¥\ˆb’I•A‹HÚcb2Ÿ«“ã`ʺÂ\Z€…h:ùŠ˜Ï¸ÈÉ#%sYŽ\iþÁ+€Ë;Oä þ\7E*våe+dRyôÈsùð‡šËSSSÓSÓÚòôôô´ª/«ªê@eKhÎ]š åe³Vª*„œ³?ç>xÿ×›qÚi§UÔ^xXòÚ¬Ïí´×lC˜ž¶ÚmÚ¸û÷ïÿóŸÿ»JMMM§œªãK@‚›T"ïìêÚ¾}ºýðÆÁ÷6‡R¨JB £"€™Ñ=˜Œ¼ä:û’y¦–QK>nÆJ1Ñ$T;¾D(Â$|IªLÁ$‘f¼rD€Õ«ÊXÿñÕýYË|8—[¿Þw®XÍúù‡Ä—‚¹‡/Éô²ƒ<—µu™§]å©;÷xö¥»ö*Á]*ÙãPñ¬­µ­ò&-3¾4¢^æ[Ö—Îõ"°&bÄ^ÅÅ¿ý<ÓO*"V€TSßú ÕþýûŸþù؇755üÑ5‚ ¡ìnâK@À ¸ šÑº ÿ¡î{.“A9ª&Ô"¾þ6˜¬x*áKRÅŠ&‰4#¥QË\n$7b³aÚ«W¯‚’ÓÌ\.78´Ég‡5vLÜ™Íf òEø2_dzYÖ) á˨ó|ìl›{}Û©;÷,2Ç$|YÖÚVy“–_ž{žw²k+¾—¤ÚúÖW¨Ê/=,|)¥‹ýY–κÛ8³ð¥#EHØÑ$›¬ÐÍU/}åjFMø²:^g’ˆ`’hôPæN0Ìáæ>C›Ëy^ƒÁ1-š‰¸êò•É´‡ÏclpÐ_ÞvÛ­ò•ù_Åšüö‘?Õ`±?›- ¾,œ“&"2½,w§Dø22¾4—:—6]ÿéSvî9¸sïÁçþ°«DĪC‰ $|YAMZ|Ùތ͗mœðeÉñe¹£Ô‘ªfR Jƒ/À=dœ áK0Ñ¡ŒÆEpe‚`aG}ŸZ׈“u³Ký –êi'|IŠ'"˜$R¥‡6âЦÇâ•©ÓÌM€Æ1“¢™²ò­Aee2™l&£….‚Öš·¯»Í?ÿø]w¯Ï9ùx`~sM‚/Éô²ìïnˆo^Q‹øRÊTÞÔÙÖ´üìÎg_Ú¹sï¡„L2 _–µ¶UÞ¤eÀ—çž„ÍèﲜøR9i¹4JÑ!åHÖÏhû¸“D&§å±ÅÍdù…£yRDfV* ¾Â]¢yÚ×#ÓCXúß59*¥u±¶8˜L³D‹ýi8N—ˆ¼Jãœ[ËŠb.§S)÷kçŒOëË*çªt—â9Z¡!­aľ&Ç0e ìqEóž·R#’ë­7ß,6¾ UwÃR|‰¶ìáhùó° "2°(¤öŠ[‘1Í׿ªñ%˜œUïÚÚõõõEyPãÁ8oáKRlÁ$‘*Qƒ’e‚ÚôØfíßL__&Ó§ý›HÉ÷}ûŸ|¶:üÙ×ß{ŸÏÎÙl†ðex‘éeˆðeøR^¹üìNmáÙ—v@V™„/ËZÛ*oÒÒáËöfèhŽfèhö ‰å·¾ô²ÄíU×B¼úF;¾tß9(õy˜f ¶'%Uï~&àË­[·Æ><¾lš;÷ÝC.?1¾õÞa/|iK8nd`B Õ1Ζ›hnPHf…ÔtЬb|éÓÏÔ×ׇ}8åü]Ò®.èQ ‹ß¾$""˜$R¥hpp‹@-]52::2:ª-k³ÃÌM¾ö¡«W¯Êf3æŸþÙ{àöu~ùLJ‡s¹ ZW;ø’L/+av Bò —.Z~V.?«vî=´sï!ma×ÞwËÀJv"—5/5^©é¼“s>—.…W¾LÔj’”XÇQøò£§œõbçzLë5²rïyÈÍ„ã/d¾Ô¬›-ÚhÆ»`£™’m& ng]ørÛœæ(=óìR, mɪš9÷0•léLø’TÁ$Uó° Þ) ©X¤ –022’Ë –„ZzI£™—]¶Âãèû©dlddT3íôÒêU—›—ìïlëÖ}¼c_ß}Ͻþ—sÇíë¼£™‚/Éô²»´£3—ð¥¯@çҦΥMÑ›Ë Í`ÜöwTR”­V1î—ñÔT#6#„ ÿŠQ˜'òæk¢"Âû¶¶±ïµ|ïX”&%|Y9ø’¦Ä4O)›Ä—>#å!ÿï‰ýgö,q×rfÙÀÁ †)¹‡Ûh¦E ­ˆ™Õo}‰6˜‰>í¥8_’’L©œ\¬žF|dddd´r.\Ûdà üå¯Ýô˜Ÿæ­_»E¾ö{¿ä?žõôÎݳ>_ú{ û+ ¾¬KF2½¬Àž#ÄèŒðe||·¹€ð%áË(h¬\ø¢4iâKÆ<îsmF!‡¤dî;£w!,ìU€gìKùÀD¬'™å‘á±lbÒ`9‚uJR¤8˜rìK¬«3—9çŠïRQÕØMUU¡ZM­¦¦¥á(úÈå8˜ˆÂÜMp!2ÆR©”¢p×c•Tè™oÉyÊ,- !„Û õÆo‚/çÎë…/9çfÆ…+Œ1¨ÝA…sÁÅ‚ówïÚeŒ‡Ó#“" 0¦HcÆ"d֫ʑ钜ÒGs57o{-âËms[¼î`}CCrÏ^)—¤„D“D*…r##¹ÜH.7222RM×522:::æµ5Ó×—ÉXþãߺïÛ>Ee³ÿñ¢âË&…GØ,PdzY‘"|Iø’ðe”{Gø’ðe\ëK´Õú2°IEÀÕ‘õe­|Õ+ýæ¾ñÆO<ñDìÃçÎ{êi§š°8@S7å¼yó¼öý¯‰wÎìiµìQØì.uÞ'€1à`l2‰§±É°ÖdȤÔ=hËPýÖ—¾Ï!còͱÖËÌuÙïaýk áKRyE“D*–t[Ë ¿éÂuÞy#@GG÷®ã»vmß½{{É®ñŸîÿŽÏÖU«¬ØšC›ó·9Xí³õî{Öû×$›Í‚/ïº{}àneÇ—dzY™£3—„/‘áK—MJø’ð%©_õêÇ—§}ì4 Êa—Ìf[oP2ÝJæÍ›{è{@ê7ni<ÞxÑÜð¥aÈldò`¼3-j5–Çj_nkò ‚ÙÒÒ¢qI/RÉmÙ{Ü¥}3—ÃC¾á-Ð _’L)a •‚Z.»XÿWŠÓÞÞý صs|÷îí»‹L3Û¼ÅgëªËWš˜¹‘ÿl?«}üÇï¾g}.çמÙlöÎ;ÖÅ»Š/Éôr&Ïy_¾ DZ„/ _Fbm„/+_""ÍkãS^)J _}æÎ›çI0ß=ÜÒpœ•€<_‚á3oá?pÁ—f"òÚ´¾Dœ<ÎÓO¼¾! ˜1€`œ¸„/IÉŠ&)©/>VàY ¬•Ïáö`5¨%ò…t…«££§£³»££»³³Aƒ¶15b{Gw{G7|a÷îí»voß½{ûžÝ;| ŸžžöÚäúûÞèØØf_‚yÙe+TUÎùÐ_Ëh™ÐÍ@œrÐ%…/3Õ/ÉôRÓ_ÿÍ—û³Ùþþl%ÂY—„/ _¾ ¾×„/« _Výðžù¸£Ê±/=FÈœó”1ºSS)mXªrøu9&¦óÑ“ÛbDZû¡je¦ÓiEqŸáÜqÉòôÝ–¯KŠï)7ÂÄÄD!ørÞ¼y§Ÿqºtw¬²u×mã®™oœ¢(ÜØ/•JsÎ…Pà„Nؽk·Y”xôO“Nk[ Å·T4÷pÆ3 ~1äŒ) ÇÂdƵ3ý˜øN|)-T/¾œU?y¼'ÁlH.f¤y1‹Î/ _’L)¦´âCEN#ÞÑÙÓÙÑÓÑÙÓÙÙcŽ'Ð-ºâKsA[ÕÖÞÝÖÖ¿^øýÓ/¼ðt"{üq¿!Ô-·|Õ\¾ïÛ÷Žúùßöõ[= c.ç/àŽÛ«_’饩 6jÏÃp.·aã`e])áK—„/ _ßk—„/I•+¬øÈ7lŒ}øüùóÏ8ó GÞÎ"é?÷¾}Ê’y(›L¢ä ǵäL§u rrÚq¨a|`€™hòÐC]ŒuTyNMýsu‹&‰AƒƒC`°Ë⩳³Gû¯£³Ñ9‚„/ÑøzX£‚$4:6666îµµ¯¯7Ó×§-ŒŽúã˯ßú5¯MùÜúõßò¯‰ò…Ä—wÞ±®\öŒdzik {SlØ8¸aã`Q¯Úf ìó›3áK—„/ _ßk—µ‹/cägeÎF^sª|yö9g›6°ñ‡FfðD`™lfÄÃÈàOøèâ¹v|‰²/92&ù€k&  M´^Û<˜¨áEfÐÆªÄ—€°m~«×]h]Ü* P­õœsy+¯7d9çŒËÀ¹G" D€ áKR‘D“D –§Ô²«K— }Ë#øø“ûŠ˜zàïûl]yÙeæòý÷×gÏL¦/›Í¸n ‰/ãeï¹ë®{Â6– _’é¥C»pþ%pþ—‚–’À—Ž•¦¦¦¼69rÛ=ñäo}®hÅ¥—twuªÓÓð¾çù·þí-BûAX²wÓlߒ—ö4‹ *_’ée¾†‡îW L2c _¾ô¤‘îáK—AÜðe´&%|N^A0óŒ¼ÌeEQÜw“Fz\!‡}äâ`Úí¶8˜\s‘N)Š\Û¼7• ú˜–v†…öjX3 k\/±½½ýSW|ê7þ&êé,XpÎÇÏqý€H ŒŒ1skJQ2ónZq0…s®ÝD%¥0Æ,XðÎ;ïhw×qÇ_ßwè´Ås¹v‰FÒlóL‹©SêF—(_¿V93D¦ V;¾œ8®áUoÌ–Ö–’N½ð%‰&‰T©Òˆk첨'ºà‚Kà‚ .µÍ³cãKÃIÜLçç…/éÕÇÆÇŸüíS>;¬Xq©¶àïi·þí-^›Öß{Ÿ5²ÙLõY_’é¥W³„lÍ$³ÜÙ~_¾ C="Ý/—„/ƒ¸áËhMJøÒWŒyKÛ²Ü^“1fæiB»¡•x@U$‚)D>`³½áèž ^¨Šv¬’JqŃ`¦K2ó =à¶ÁLéq•À,!Phoo¿âÓW<úëGÃWdÑ¢Eçžwžì<.Shhà=Æ7ÏžJ¥Ì» ( Wt‚©¤Î¦câc,›ÍþáÈ{HôÆ}ß¡s:c€ 9þçš7:cLc¥¨›b2}šãŽ/«Þy·Íkñ¿§¥‚„/I)"˜¤š–æ!>\jy᥀pÁ+¬Ž½ôøm?ù¤¾¼é+_ÒFÇÆ¾ëëi~ùÊË2™>×M÷~ë>Ó×l6/üe%ãK2½ôä’ùhûKÙ~úû³¥m%—„/ _¾$|IøÒ—š' BŠ|Y5*FѺñ¶¶¶µk×nذ!Ì!‹-:ÿ‚ó§§Õ8Їå-é*ÆØÂE çÏŸo˜a:õÚ¾w;æÍ^ÒpœŽÁ0·Ó3øhžãF68‚KìKPÝ©{´C&kØ7»Ñóž67s®È?(ÜZæLŠƒ)ÇÄ䆬~ ­˜‡µ²–›>F _—¤ˆ&©588¤Y\õ,^x)\xá i _š´ÒünZcêèø²ÀÞ}||ûøöí^[{{zz{{µå-¾™Êûúz/¿|¥ë¦oÝ÷íbáË»×ç‚ðeYÈ ™^\ïÚ50<œ ßDš6l„ ™d®Y3P‚Y‹Œ"| QáKB*‰ã¾$|¢y _ÆÅ—4m&%Îo|¿ûK–. 15|¾ØØÊd3/½ø’×Öm‡gfñ.@݃!Xd-PÈÀ‘½§Fb_""ØÔ–ñiíxYÈcÁ²=þ„/I"‚YƒßV¬bKÔRÓÐÐ&ÚôXQ›·«»·»«·«»·»«é|)P®6B1ð¥w“=zTþÓûR[xè?ô¹ö‹/ºpzj žüíSþþã—­X¡ª*H?!2D`ldd40ð¨‰/}žG¨¦áá܆ƒaðåwÞ^â÷‘L/ÃÈ€˜ÑP¯~÷ “Ì5kÖ‘c¾$|†zDº_„/ _q7—њ”ðeAbR KŸqwx‘KQι‡¹éi®mrŒu÷CÈ;›, @åB¨ŒÐ®õL§RP<çŒ\¶§¹ß“1!5¯¢½£ýŠ+®xôQOwòæææ .¼@[V«X3“¸öupº~gTR Ü8Üò"O¥R W„éEΨ°hQóÂ…‹<(eÕdßûG_Ýwèô–Fm=Ôp%×cbA2ÝrŽëk¹úñåP[ŸÏÓ²¨¹9Á$|8j%Á$‘*G¥¡–Ýݽ]ݽÝݽÝݽ&>Œ„/ÊÀ11|iü!IGþô3[}¶^rñE==Ý0>¾Ý?PæÍ_½©¯¯7ýÈÈè}ÿt¿Ö­ûzÔjçî¾g}8Xb|I¦—QÕßßçýûšÚ¸qpx8wçë_¾$|Iø’ð%áK—^Ò¶ÞîOlÛM¦–EÉÐÓLyYz¸Átã^“«ª–(•ò&˜ét±ÌµâAðˆé)^!˜\&˜ÐÑÑqå•W>òÈ#ùe¶¶¶^ré%ÓÓÓù03‚ä[_rfÝT*eLt‚©ížJ¥87Ò%¥RŒëOÇGNøÈ^úƒ×ðê¾÷–6ÌZR_Çõç„C`Ì ‘ÅL4iæ"—ï¯Ó³:ñå¶ù­>þãÐÜÜLø’ð%‰&© 5¨QË¡MŦ–ú=}rp?|i7‚4ÉâKgÞž„ðe Á¼è¢ µ…}í4{{{Ýñåèè·ÿé;øò¶[û³Ñ²÷T,¾$ÓËB´víšµk×Ä0ÉŒ—ý‰ð%áK—„/ _¾ôªÀèäì,èÌ„Ãò—9cœqàœÓÇR’TQø²@¹ÆÄlmm]yùJ_†Nx>FÒ#Ët¬Ètj>fŠÂ ¥¥yÑ¢…o½µßsj6º g~[c׃ZjÓІ«8‚éEn×–=fuâËÉãë_]¸Øç†´´´h­Í¹íWRRL9>¦n9Ë@QEÑ™OJI¥ŒÐ™ çroÃGÀ¼N+ÌkUžw“ðeÍŠ&©¤%ä,>µìééíîîíéî3‡±AøØÑÆ ×è\ˆ/ yóÿü_ÿëég¶ºrÌ/~ñ¯´…ú¡!·Ü|“ëú0ø2[ø’L/˨bx‘çF'²}KòFD„/! "| AH"q—„/C4/áËD¬/ÅØ¤»_gÓœ9ôÙ­ •‡,]ºT†˜¾ ªlQª{þçÿêßñÙá•7ÞkkX`JÀŒ¹ŒÎ ÁB‡à†/µIRÕâËMí~sœúúú–Ö–bÓ½ÚȾ”©_­áKˆç?^Iø’D“4ƒU¼,`rÉÃù‘‘‘ÜÈH`¾—uÉ%µ\‰ÓÊ’àË];Çwíܾs×xG{÷¹ç]œ¾ôœêà իÙå±HgGû篿nÇÎ?þןjk.¸`¹ûÁäÇ€zº{º»óó,ÝÿÆÆÆüñåm_¿UÛ9¤cTH|yçëJÆÉô²ôm˜ß¤±OÝÝÝzDDø’ð%áK—„/k_ŽNÆI <襩´eïñ&•æn^™6PÒ‹rºk#}FˆFEIyÇÁLræ—¢xLs»`ªBÈ™|¬K˜žVÁˆ‰¹pÑB÷á=S™ª.˜ÐB…æ3ÓTJ1ë’JûLU5â`˜3EQZš›››íß¿Ÿ{K8<ýÛ^ÑÝ €!“&0ÌÄ”v|ÉäyV­âË… ήŸ]‘ø XAø’D“T…Ò¬,5‹ËPËK/]™g´h±?á6ÎH_>÷Üouv¹Ë2cìhïN_&§®ÎÎÿõüýÖgŸcŒuuuÀ~ô°?¾üò—þ&}H|©nwß³>— ~TJ‰/Éô²ðÎÅf—5AÞ Îsˆ6¸å•g¾$|Iø’ð%áË´¾ô!˜sç6iß[¤9nee1–õh™=Â`Œï”ûQ³’ O‘Šmkk;rôHQ*¤GkµÂQ1·GK{ü–-[öÜsÏxç¯Â&>˜ztû¡3ßVŸ2Œl‘ÉéGk,öe ¾¬¯¯oni望P^^ûžËŒséN™X™ô bO;áKRÁ$U¨‡Jà!~饺­eþÐå,˜ú¦®èll›B!èŒb_ª¯¯ïîé;µT¸¢p){D*µT?ænò>æï(Œ[wÍB™zö÷•ð%©œ"‚YÑ’FŸL°äü}´âÅNÈÓÓÓ×ÛÛ×ÓÓÛÛÛç. __Gرc|ÇÎq]FnÊ@|)ÿ¿thø8˜òÇJØ»³6(6ïr†¸sç®g¶>ëSòÅ_ÔÝÕ¥¹ö˜_Çññíßûþƒþ—þµ¿½ÅQg/ßvmý=ëï /³%x7Éô2¶ twiÞ‚ïxO'ÁÜòòÀe§G!&„/ _BRH‡ð%áËÍKø2騗ˆcUïENªdÎXyóÍE‹á[Ÿ~Æ·ßìzſtǟ¹ NÎ?n£ ÓÌj×Cm}ûf76f¤ð—Xªç’ð%©ì"‚I*Û÷O[Ö¨åЦNJzÞÞ^ZöÔó‡ ÉâKsýÖgž@„gŸ}2f‹™ç“ðež“x(|éß…«ÓÓ¶ߤ˜Dòïxò—@#˜ÏlÝêSlWWçE^`F8Ò~*ðÁø_ø×þö–L¦/|CU¾$ÓËØŠ—¥'°y /¤§»ÛËX:76pzhF@ø’ð%!ˆÄq_¾ Ѽ„/“Ç—›·yò…ÎŽö™û!örHw¬“ÉÇ+ö¥Ï˜Tic`™r¼HUUõ8˜©W×cëêf>“)|2$Õ½®Ë9«Bqê8\H¯•¢(ZÎ"D4›EÛßn `]b»€ºº:i½Â9G¡Ç¾äŠlTaŒ U/9NM«imyñâ%çY}û“?¾}dâðÔm³5| (»“k4­üÕ‚/'kØ6¿5 ¾ìêîªo¨ýTb¬'9F¤Ø—$"˜¤Z”fe9Td[KZjÿåÛ!&ˆ/å#`Çö±;Æ`ëÖ'¸ ý êô(‰/“þî'?ýÙ®]»}vøÂŸÏ_Y|™Íf×®(¾$ÓËJÜèÒF0×®)üãžÏd>¹±I—„/ _¾$|YSø|]È0Ì•ô1†ËÌt椙B…jF™_&f”g+G°µçU~°ÍH‹œ±æææeË—=÷ìsþïýP½ä½³æ×5/ 6çq)V¾Ü6¯õÕ‹Ã4ygWW}}3|àägÌïQï3¼cb:#–¢ ¸·qMÆUÊÇ’ð%É!"˜¤)—Ëår#Åö€+Vöööõöeòè]’øí£è;Fwìß¾clçŽñ$/&i|™l_¾s×.|ùW7Þ¿òû>ä_l___x|9œËi!SýwËf³wÞ±®Ò0™^BYzú³Ùþþ¬W!ýn9|òC„‘O(ÌÁ-¯ \ö±x…ð%áK—„/¹áËhMZR| cû<¿]f±y“y-› bV&œQ•\[ùi´½Aö F\L+÷”¶ÅžjFinnY~þù¿ó‰©éïÛ{X=k^jé,V­k+K IDATør⸆MKÃNmºº»ëëëmÙ{7#zqæÈäãS^fœ3«(ÆÍ›È8“…ù[X“õ%©rD³j¿C!Zr¤Ð™Zñ$ä¹ì²Ë`ÅŠ•.ÃÒ‚ñ¥ó@ãï§Ÿ~žyú‰Bjιb >øà}O¤_"ºçrÕô´*˜lˆG‘[ÔúÔýìß~áS`gGGG{»ªªòO…ßð¡ñq¿t(½½=·Ü|S×!DÌåFÖßû­ÀF. ¾$ÓËHJ$KfTÛßß?<<ìUÔÚµIÕÙ/æãÁ$|Iø’ð%áK—Վ/}\È)‡OUÎÄjgÚ˜ÔE/Z´èüó—oõ•¯iïñè䱥DZ3›”¥ur^r˜éørÛÜÖÉã&oÙh¾¬Ê'Šð%))Á¬ñÏqò`T㕹\ndd´¨•רåÊË.×G—&g”ë_¢œ„ÇØsûö±ÛG·oÛQ˜¡e:]&µ ՇƗN” rÚŸ L>µ4“Ö9¿F!ÿþ«_û×ü†ë¯Õbå çŸÿåá;üR–÷ööÜ|ÓWÑ+cýIË­¿÷¾À,¾$ÓË¢6W˜6v/­?›M_rñE¿ýíS^[s£Ù¾%„/ _†¸„/ _q7—њ´Ôø¶¼Úâõ9˜;·zfT?SŸõIÙLÈA!9c–­+€öS¼"e^vÎ{SJŒFÀ¢¥—ãÌ˃_.Å÷äR KÇ,¯FééM)Šj†ÑTU3Z¥IïÌ»cž%•J›ëëÒis(Îι"SQ³Á5Ó?EQõ©M*måTž2&8---ŸøÄÇÿû\ŸŽ<މ{LÀYsØY|FãËÉYõ“ÇÕo›×é‰(:¾Œú’ð%‰&©ePË‘‘"ÛZ®\©SKGŸV¾D‡i¥_>õÔã»+œZÖÕÕÅéƒ=ð%Bž·x\|U»÷ìÙ³w¯Ï×_÷9Çší;vøãK¸ù¦¯„ä¾uß·w+¾$ÓË"µU¤ô*6Á°§Ú@»§»{ÜÓ sÛºÞÅx…ð%áK—„/¹áËhMZ|éc€ mm>þ˜rl;ι6‚ux‘“yå¨xø²’‹u\53þeLO·ãxTc&5¶¹?sÞÜÜ|Þ²sŸîwáÏþ‡wñïªKê`iœÕ8“ðåä¬Ù»œ<®!Rƒ×××wvuÉ„]ö—­^EQ˹M¶†ñò(WÌ;‚Àœ11w9ÿI çqR¥‰&)¾J“§¯¯¯¯/sùÊUÎ+_æmµáËßþv hì2¶îá…÷úV‚>@‘Ç4KŠ/õÈ£>;,_v^Wg§¼fûŽ?ú—ûûÕ›¾²¡ñeæŽÛo+Þ“I¦—Ej¨¨­·aÃFÏ£ŒCÃ/¯7"0ZÙ%—\ôàC?t,Ç&sc“Yb¾ º€ðe$ŽCø’ðeˆæ%|YD| | 0ÛÛÚgŒ3³H9ÒçÜ<—†/¹F097gœf…¨êñ¥ë“æ‚/Í­ÆFLÇs9¦…Ø8ç\áÍÍÍŸ¾òŠçž}nÿþýáë6q &ŽÁßglI—Ö! .I‰¥© —“éÙ“u³A—ñî[}}}wO8p¤ƒ`¦d‚ií–’–Û²Ôí(Š"1e3º—ÃÂ:ÿap}þ£öN„/I‰‹&)š‡À`—ÅS___&“éíÍdŒ„{öìÙõ³aᢅáïK‚øѯw?zôˆœûRá¶ßîÌåmÛ^õ÷?眳§¦¦„tH ¾üÒ_±»«Ë¼›LŽpdßÑtï·î _µ¨ø’L/Ã4Q‚Yz‚Ïåq/Šô ôôxÌÜØäà㯠¬ø˜/DwEø’ð%áË`xDø2°y __ŽNÖoyÕ3¶Ýœ9sššª–`ZlÅ[ï3+q'Ëî±BºAÌÈ5és"º½Âa§Zr±rØw°üxãhÙð2ÛÍö0KÅâ,s½ªªfLBšÙ>#ŒËÃ~E35€T:RPKÎõÊ( gŒ ú]@+&©¢(fL®Xö˜ŽgfáÂ…ç|üœ\nä·ß™A3ò"•;»¾¾»»;Òcîaìµ’PFâW‰¹¥:Gr_Ô¼4v9{6x0b¿LñŽ~64¾t¤!2Æ$…jbbò/¿â³Ãg?sU{{›¼æg?ûyêîîéésöû¾}ÿèh™ñ%™^ú«Øãng,EY矿üIï|>ƒ¿ší]œím %¾$|‰ã¾$|¢y __Àæm~©9:ÛÛ4çZ¯ÔˆŒsn”Ï8çõáŒ1ÎX¾º4©>dU$£N¬xëKY ,øøÇ¼ýöÛ/¾ðRÍ> ³ëë›››£&í)!¾„Âé=áKRñD“d)—Ëår#ZZž¢žhÕå«ú2™L&ktWè5ÙtÅ—hE­´uEùørlltllTs/¤Â‘ÜÃ5jÙÜÜ,W¦áEH|™—ö'a| ‡|¶¶µµåãË]»wûÒÓÝý¥¿ùb˜S‡Ä—ëÖ}=›Íã¡%ÓK/KOÀy‹™ÃGŽƒ Òòùç/ߺõY¯£ß¶î¦Uþ£$—„/#q—„/C4/áËàË–±}žþžMsæhYÈcœqdV¼KsÎ9Ú—ÌÎVÌ; ƒY…Ȫâñ¥¯›±/1ÆRNEQL{LÎyÚÌlÎX]Z5—UUÕfRK–,YûÉ5ÿóß.vØJ{êëë[Z[-Km[PË”-•½‡+fÿJ¥Ri àÈ^€ué´4uM›÷TáÖV.–KÑKÁˆØ‹ˆ„/I."˜µ®aZær¹"SËU«aÕªÕyÃI_|iG“_>þøcðÄ[ ¬px÷ðúúúú†zMŽþWÆ—Q 0mý¬_b± ƒ/ b™6ùïpõg¯’ÿ, ¾ìÏÅú’L/=;’]:î‹g™ÅÄÇœ¿|ÇŽ;wîtÝšÛ·þMënZEø’ð%áK—ÁÜðe´&-¾ô÷€ŽŽ6šVÌtÕ(¾,RdN@ækMœÉf2ÙÌHnäíwÞ®<¿ò„Ec— ¥¹Ñû²t:©Ä"‚Yaeaï|¸¸ŠX:[ËU«Ac—ò„ÑV]ôšÑ„Ç—cc£ÞÒœÄc+’{xKk 4Ô7Ô7Ô›€2¾ô»k¾tz‹ÇÀ—àó¨=¼‘õ£œö“à)=irrÒëð+>õÉé©i½%¾k×.| _üâ_™azÀ´q“c_"â}ßþ§ÑÑÚl630°:¾”›ÝÇÚL/½Tš,=­½vM6ÿ#‡Cœ¿üáÿ«×ÖÜØ¾Üè¤Í—Ü}LIø’ð%áËðhŒð%á˲áKøÎ¦>ŸBÓœ9s›šh"SbÉ1"­±ó;ÀhX ã×ð!5ÍÇŽÉÙu˜ôšË¶É &ô£r{JOéÙæÖy¸ÂÍM)EAnY›a8W8c°–ß,9rc ˜V˜ž½Üÿª3ÙLÚýÖ[oo?xà`…ÌÈ,«¥¥¥¾¡>»Ô{£’e¾$ÍÁ¬•ÌÖrõªÕ°zõj[¯”4¾Ñ<ÄÇÆF ©m$÷ðÖÖVlmm ßç†Á—¾…`iðe-nm]yÙ¥ÿùŸ¯OîÛçØ´dñâ¥K–˜îÞ½çWüÚ¿´?}àGGǶ<þøØØxÀÐ'“¹}ÝmE‚tdzéìIJ˜¥'T³÷÷ßyg¿ãf•"wuuvvvz™aÀúï=¶î++³½‹ïs8Œð%Dh—Á`bß/—!Xá˪—÷õúþâÄÔò,#L†Ÿ#°(DÌ?µ‰4r‰ÿs{µX¸vsxJ™@[Êó —'/¶æ…žo¤D>yß#ÆÌ5<ÅÍ7CQMJ“3Æ™N0XÙ{¸`Wæˆ k;¹ýnÌ›7ïôÓçÀøøvuzz×®Ýå{œ“]Í-Í ±Áe‰¡"9“fˆˆ`V¹´À…ƒƒCE=K&“Éf³«âv|étúŽŽ/7oy |sµ ïÞÐÐÐØØØÐÐÐÐØhbéº5‘¯háøê¼·¶¶·µ½²íÕm¯¾f®\²xñ'%`´gÏÞG}Ô¿œ?}WW—ÿ>££cß}à{až½u·ÝZ NG¦—¶I‰jíÚ5k×®ññ+:s˜9˜ó! ý…ÿÏoþß>¥­ÿÞfb¾$|‰ã¾$|¢y _–_ú„¿€“Nÿ(B2±ˆ©oŠû°Å¡{/uOyjKø’Tˆˆ`Î`mÜ8»,ž²ÙL6“Íd³ÙlÆk[ ¾|ló&ؼ9CKçÞØØ8gΜÆ9É F·~9©2e|iE´4Á—ñ´jåJy”ó› &&&}öooo»áúkýËð¡ž:Ó×—¸õ%™^ÊMQö,=3EŸûË«öó_øCÌ{-ˆIø’ð%áËðhŒð%áËJÇ—'xÍG W$â哉ѾèƒäÏ`çmŒÈcl11åàžöXšòn¦;0äÒù7yŽŽ¯-º¥â\17Èc{&L¦åèaV5-§­ä>±/”€3—,swåÔ?æ/‰A„/I¥Ì¦áá\.—.~BžÕÙl¶?›EÌÃŽ¾]~|9222::222¢å­HîáKÛ–ÀÒ¥KåªÆ@‰„¿ ŸAÞþ³báK'óÚ8˜ŸáGV{{ÛçþòjÿBBâË[¿vK&“Iôµ"ÓK½**KOé'NŽe9”9àœs©ÃI¥Ó7\Ý?ü¿ÿŸÿYîýÞæÕ—ž2°âÔ(8Œð%Dh—Á`bß/—!XáËêÁ—£“õþ™Ç5ü'Λ7Wÿ()38]*J§Óf5ä_ÖåõétZ¡evVRŠ¢(ZþhEQxtÌSDË·*U± UeS¿ÚYÉÜ931ƯZ*emôš9 ÁUՌ稚11•”bÆÄ+[ÀTjÊZ?•’ŠÒƒ’çϪdËQ®¶µçg÷m¦””­9 ¼KLnŸöÖɱ/ëÒù·Ã(JŽ}ÉÁð÷ÓiøìCø’TÁœX!7<<\j9€kÌ7?A|¹é±!ÐÙeîáa -çÌ™3§i ´µ··]dâø2L±áGW âKôœIªÉÉI| øòûþ`|ûöÀs%Ž/7lÜXó¦—ä0^ ®»ös?ùéÏü÷zâ?scûVœším 1&#| Z€ðe0‚Ø÷‹ðeÖFø²zðåæm-[^m ìöÛ–.™3§ÁïH%M„/-–ày2îØ%‹}™ÄóOø’T.Á,W7*öåÆƒÅv×xå€N-°²@|¹iÓ<öئBjÉ=|Μ9MMMsšæ455Ʉч*F½kɆ¿ ¨FÁøÒ+P¾TUµ9°Èxln&¶ätŒÁ¾}olڼſé®þìg4+áqíþà‡aR Þú··dúú èn†4CÐì sµmz9ƒ²ôT²:;;Â@Ì‘ñ7îýÞ–Õ—ž2°âß1áKˆÐ„/ƒÁľ_„/=®ˆðeâËÑÉúÍÛZ=Ç`Ncc{ÛRšÎÌÔYØL×ò Ö–§^z–e»E”¦dèÓS6õeœÙ†úÆ®š!J±/Íõº{R³ àΛÊM2fìK—¤."˜µ(Z®Y3`ïÆÀ—##¹M£™‹Frïèè@ÀöövG}üñeqBU&Š/…C¾tÛÍQ=¿³øL. k„°ÍˆÞxãÍÍ?á_ÿ«?{U{{›Kã ?ú—‡ÃàË[n¾)“éKê-¨qÓK2ºL\W^qÅ#>¸§aŒyŠÝ3ïÍ |Iø’ðe¨Ú¾tm^—Ñðe˜¨—šæ46þʼn¡¯ÞL…M•„/™Ç³nÈQg‡ò˜œ ¤— 9?åÙ³%ÿ‘–9: ¦¹ÌìM‰F@Lκ]`0ØD—¥MÝÃ(ö%i†‹f­H j©SK—Ïx||944€›6m*°†áÝÛššæÎ«ZZŸg7@ ÉáKÌ}·Ïý{|/|éëcî¨yQž«@|yÅŸtÀe‡Âã˾¾dðe›^R–¿¹„GL&Íû»”N[Q‡zzº¯þìg~ñË<ÑÈø÷~ïñLOËÀŠJiÊðeˆÁƒÑ¾Œrï_¾$|iŽd¶5oyµ%äg¢©iÎ)'Ÿ¤¤Úå8˜ét:]gÅ»œ5k–yx]]kÌt:­(Šö#q*•bv—3šøxôÈ3Éú*¶àrä¬ã£&;Ü4Ù(cväš—âÈ`£ 5:0÷O¤¼,Êq0­Y•…n¥þ ‹u÷ó›V ’ɪ͡MbÇŠ¢˜e˜}؃W*\‘â`ñ1µº’­bËGÏó´ˆh%cò'û8¾$%+"˜Õ¬l6ÛߟÕpƒË+_"@.—ËŒ€á$[‘ÜÃ;;; inÓܹs!QFêËþ2_Ú…#â‡~(„êÕsËåV¾ÜòÄ“þ;,]º¤m©Ÿ›U|Ù×Û»r劤ðeÍš^Öf–žÒ«£½=$Äc>‘éiÉö¶ ¬8…ðeˆÁƒÑ¾Œrï_¾$| 0:Y?:Yž]@ûÒ¥ÝÝô¥#•€š$‡/ãÅœèMûÓf3jËNÃHÈŒsÓ„ÓæÞ©c„`-·2™`z%˘Ž!ºLs7³XQ¾ÅäôÈä#L99dZ"˜Œ[¿ž›?–0ƹL'åŒ=rnzΙ!ÿ›Þ4“ð%©D³Ú$SK¿W:"¾Ìår###Ú¿…T/’{xWWtvuæC|Y™á/¹‘EN[pÔß_Z·'!|éí/rû&ù§B}ðÛ§ŸyóÍ·| \²dñ§>¹VÎ(×a×®]¿ûý ø²··÷æÿŸ½7’ãºÎ<ï{‘•Ø—Ú µµ‚@$Va!’ TâHò¢¥-K2µ´Ü²-»Ïœ>}æÌôÌôœ9vkºÏ±µ-«}ZÒØE U€ …•i‚4j@° U@- iQ”€ÊwçȈx‘ù"2r¿¿VÓ̈—/¶ñ¾º÷»ÿöË¶Ý ,á ööö—`è%%Œg™æ¦¦¿øúŸýà‡?º9>®²þÐÕ[CWoõùç}O<ÐÙVÛÙZ£ðGò%É—)…!|¾H¾TÐÚH¾,Tùrøæ|]»TÌ7YußÊ%‹Ó3ŽÈ‚j’[ù2êR¦)pÕ*`é–›+ŠäK"O ³ˆ«–]]J·´‹|™0ŒöõõCº¿þGÔä£}ãÆþèSÞÖÖÖ¯|ù‹¡Ü#¾ŠbMè%UéÉ!Oïïùù^ES§ÿÈ?÷èh­él«íl­íl«ñ£F‘|Iò%É—)Ï5É—¥(_ê’%¼p±&Àx®×í!ù’ÈŽjbÆÊ9N¸|ýK1wÌ–¬î² ö¤c¯Þ Ôý-][b*6$_‘/õ X +*ýË2 ›»6H¾,5HÁ,Tö÷t@§™Ñérïú’/‡ mÕÒWzøŠÖ`h— ìé6PæÐþ2‰hš¦ÿ×ÜD×û¼åK¯=MC¾L ½D¿ï2®Ü¾=é-_ÀG÷ïwûJ]¾üò—žM¿·ƒ½}}ú…­xgz°!]†…=‹Šã·Í%¸ÍÌH´ýO»9þÓçæ÷w‡®Þºz»Þ0?Ñ5M'9 å ŸÏÏS¼a5è÷ó”:Ž¿ÆFzŸ=ñs”³u}ºÐøÔO!„ó˜NƒÁ)¦þ-ô§‰'õ…%-¸EÝ —ZÑüì‘£ú¯¸§)×Oy%£¿‹ÇõÐy­óÂÅêôK/~`õ*Çq^Ó´ˆ1_M;}Ù웉˜;.gáhš¦çv‚ùo³¬3Ís @ÊëèËL•÷Æxf´þ[o­Œr{Æ´ññ ˜ì‰ÉcR´D$‚Ò.8?‰Pó×…´œ0<™ó(PÌ"w?ÒNyµc¹LÊ/‘`Â5®™Óq·×HÎ9×LLÝûAwfç‰r³‘³î C#"—Ýí’’õ¾DR= R0 •ýû{0Õ½‹Nï‡hMkËÀ¨§‡————W”ƵKpR-=ôÄ`ö—™/QV-“ã1}•÷ÅSÊ—h?Ýnòe(ܾ=yìø ïuzº÷¹}•eù²·¯¿¯”B/©JOä·7ùF±¿sÇQUËHÂrKsÓŸ~íü“ŸŽŽ¥ÓI]Ó¤Aë8ä²Lä˜5«î_²d±Û8Ÿ¨`–Ëe‘HĪäã¡fZ ¦®Jè¾Cj1lD.Ésù2£Á¡ñw$×ñŠ1fú`&í2)–ÆñïÄÒd5¹J·½Š9)˜èv0Õ¦ˆè©›W¦ œ”%BC^LS* &É—D¾A fÖžtAÄ,¥ÿòeo_Iâé ž^^^^QQQ^Q.§‡'ìršò¥wfƒï"_êZ×…K°GV:n<3¡Wvù2á¨)É—ž?%D ÐùjÚtNMϼòËÓÞÞóäîºÚrâyü‡GÇÆ~òÜOSžˆÖÖ_ú⥌{õ>tƒƒ}}ý%zIUzòœ§÷w¿}ãF_ÿ!:AAsScsS#"4Õ$[Íb««áô-@#a[ÈòÈú’¼/³zƒÙ„Ì|~>b°{7ù³A3Ô2.ùJoooGÀööö…trbéÝ¥yóæÀܹs“{’Ž|éý»7ÇÞ† †ðœ¶|齇BùñÈ„ƒfjùr÷uµ5‘'êÑѱçž>å©ÿüç>ÛÖÖ–Ž™ø ½ììì|º`C/)a¼P¨_¶ìÙ/|îÕ‹—^½x‰ŽADÞ¢»^–—/¥CA„¦š¤lìYÊöŒ·@9Ù§žA<7‹1–f(wrš1&}œÔIHù£á¹Ò“/ƒ™_’|™¹ÃBäR0óšÎη¯èÕ­0ÝîçÞÞ>ГÄÃP-UÒÃ+****+ ££ÃC4T Àô;eÎþrÞ¼y𦕕•rÞzù²¡¡aÌ)ÉôæÍ·Ã•/Ó·RÅ3ÖÕÖÖÕÕ:~õ“Ÿ>?¦EûùÏ}vÅŠé\®ƒƒƒ}}Õ-zzº÷÷t¢åUéÉŽùMŒ±äüMJ ÒäŒr#¿iÓÆ ëÖ­½xñÒÅK¯Ñá%‚È+–.YÒÒÜ´xñâ„ñ\NíLÌ"·y_–Ëeræ¸Û2çŒ,r®iœÅ]9çä„™§dN¾Ì§vÔ›•¬/í«é_¢§Ä IDAT õ*…¨WøÑ•N=ç˜d¤ÉäßÒ¬¹‹ƒÁ%Êó©¸ö›0Æ™!n²DÓ(ÜžÞ—Æú¶ß÷éu{7ö;±Íô•Kò%A fÁs ·OOO§_éáÐÙÕ©8™9ù2MûË„çÍ›WVÿ˹£Xbþ¸â`D¾´ŠüÄŸ ’˜™Ù²é½÷Þ?qê•„Ïkª«l§ËõÙ?>>ž²å?üì¤#_ ½,°;ªô­[woõ½±±±[·o¿qùM: AY¦¥¹©|ÉÒ¥K—С 2¢š¤l5ï»ÇBèædÿB«Š!ywúý³D £OÉã™;,Dþ@ f><1¬7h&©zzxeUüÿUVVz(æ?$fßþR§¬¬¬¬¬LwºD—\ƒ Ù_¢ZL©›|i4(í‡|éù3±Ù˜àÂåR±þ¾WYY±më–S¯Ø 1w>ºC4+öp!Pèí?8>ž:Äì3ŸþdKs“b†~ò‡ý<¨Z#¥àB/©JOqS[[S[[óÀšÕ §–ÿË•:,AbÉ’ÅK—,ÑÿË”*‰Ÿªe¤U÷¯<.KùMX?GD ̩ïØsJ’·Ln“åâЃ|éÿwYƒMòe¦ ‘W‚YZ¨§‡w­ì€ªªªªªª””éç€B±¿Ôƒ.ËÊʼ•GÅî”/"XMôJŸòe(Oâêª*YÄÜùÈÇÕTäË–ææ;¶-oi Ö“Á¡¡ƒ +®_X¡—”0žW8zbB¢¦k¹|£ÊSyÎñÀš5æÇÇÇMdž[·nOMOÓ) ‚ðK[«•ÞÑÜÔö’#¦1_òØî6žë/ÒÆçš{y™ÝûÒjÖòÁÔ4&„0¼/Íÿ’ªš_äyòx>F'ù2c×Ë•À²9‘|IdR0‹_éá÷Ýw®\¹Òû†]¾T Àô5Z¥”/çÏŸo]¦Tò¿,ùR§ºªê™§{NžúåšÕ«jjª¾½9>ñú?ÿ³Š|ùo>ó©À}ðzÙ±¿§`ªÖP•ž\‘0m´Üšd'#Ó(Å,·ÌÖ²ýgÌÿ+7‹YAÐË–Õ-[V¿z;ÚåuÌÚ£ˆÂ-DÝ ˆÎ‡·j‚ ˆœ è)‡zeÓÏå×iMÓ"Íÿ#ư_ÑLcs»šiŽá𦠳’lfLÀd HùÌ€,”‘VCë]ÖåKù³¯ÆãF–Ò;‰dÔèØUfxH2Æs0ª«3ú´"šq¡™mò…F¹°æD1§>çkR?¹;‹9h3ÜÛöž÷eÀÓ˜ž<Îâå™56éŸ0fUòaÆ1Ôc6Ìiqúõ¤÷[’/‰BÌ"|°êVI¯ª®ª®®®ªªª®®e ÒïÀ‘Wö—z¶¸tŠ|©€pŒO[¾´Êù…:’?úÈöäklâÖ­¼”rÛ¦ÆÆÀòåÐÐð¡Ã‡‡‡G×ïéÞ×S¡—tIA‘¿³¬ô[ ­wù}™+ïKp—/³Ž0[?ÊìŽäK¢€ 3£>Ìèænò¥w¸åý÷ßÕÕÕUÕUveÙ_–••ÍŸ?_ŒR´\̸ý¥cb©Ói _¢õXS:/±XŒ£c1s™Ç¸¼š¼•¼À…@qëÖ­#/Myl›÷w?!÷í{è¼ ‡>tøÅ ¬³³£§»\/©JAADþÌâ2ÒjNæ˜êÁ¹ù#_9T˜?ç(Pïy_’|™±‹È[HÁ,Ü'«uW­Z…€«V­ÊLÈ—ùcij—É‚£‡ò¨Þ½4åËÔRu*ùí1ù2£Ã¹/ù2@ûÃ##/yI=ô²»{_O÷¾|¶“¢*=AAy6ÉÊ_ù’1†ˆÉ/·n³ @¾LãÍ9Wò%b8Ç7‡òe°ë4ýYÉ—DÁA fѲzõêU«ã’eJ Ò×pP@ö—eee ,ÐÍé|É—Y³¿L)šºÉ—hû?jòe&Çó[·n{ùDÊÕË—/¼xä…(®ÜÙÙÑݽ¯«3C/)a<ððJ°¿´&’a™¼ áØ”¦i1Ã-rï^Ù쬾<;;3–@Ó¬'òoïZ¶kewïšË"=Lz«#¢ÄÇyYŸ‘½/u9i@¶–e_c¹’샩E"öª>s¼7w®9òΉF…ôŽ*„к±`–íµœAÉãÊ÷•¼-J}Ó½W4×Õ½…@DÔ?Ñ·Êk%î ×4xõüysåñññ‰‰‰ŒTF`ÍšÕærMmMmmmòdŒqά]ñf 1 X‚|É€q»¦<`cÉa)²‘nÜ34¾K~Ë•=1I¾$ R0‹÷ù !GPú;rkF,X Ç]zDDæ­ýeòðmO:$_b¦‚0oON?q*åjÏ|t[[›ßƇGF^xñÈÈÈUÅõ»÷íÍg×KªÒCA‘—Ó'’/sÙ7³‘ ^E„W_}5Ÿ/–7Þ¸,ý#Þÿ5kÖÀ¬ñ5ap Òÿ;É—DB f®ž!Ý`˜âW2”žÏö—eee .4µË`ñ’Ù±¿´–U*’§/_z^1…ü%Ò_ó„sl爈ÇOœšœšJy`ŸyzC}½í”mÙ1ðöÅ#/yéŠç®£££{ßÞüt½¤ K‚ ‚ ˆ|žže¤Õ|›Ž=w®@…K·tûöíÛ·o¿ñƵµµuuµk×®M:n¹Œ¾ôÛÉ—Dþ@ f?m=¿+\ûK¿c"êÚ%$Q†n©~ÐTåKÏfÈ—‰Æ—“/OžúåôÌLÊÕÈ—G^úÅK¿8ª¸rGG{÷¾}yuoR•žBÇãÝNþJÓ4ë‹hÔq}-3Í+#š‹ÅôåX,‹YÖ™‘ˆeµ½k5uïÞ=sY«)ÝIʸÙEòPLouAààƒi÷•3¹´—Æö²ˆ5]’=15M3‰Ë‘ˆ9F£(-›>˜‘HDa:Ó™:#ÌL¨H¾ ¸!:Yoá61&Exöì¹óç/¨¿€eàèøiNNÞžœ¼ýÆo¬Z½úÃ:¸ÆÁ0¬ÔMxc"naÉ93M0Íq‰3&8Ë<”ãÀòÆθtŠÌ,<ËR3yV›r¯‹O¾D wã†ÌB~Úz æYôö—¦åeÊ.eÍþRýˆ¥.Dî)_ZÒdüjð”/1Ìáúô™³*òåÞ={|É—×®_?öòñë×ßV\ïÞ§öí}Jv°Î9”0NAQ *’/s°gÏ;wî|I]ho^¾üæåË«W¯^½zu £$å<ýˉäK"ß 3ž›ä+µ¡¡èí/ËËËuËKyå lnì/ý^$R,eùRá8?F’–OŸ=—R¾¬««]·vmݲZõ=öòñc/W\¹££}ïÞ§:;ò(ô’ªôAAÐ4,#­fl’pá´×®‘w?!¶ºS££cgΞ+Í+îòåË—/_ÞµkWݲ:*•î eH¾,|HÁ,àGn:ãWØ_F£Ñòòr ʰ¿”,Süt¢XD¾DŸß‘éé™ÁáaùrïS{Ô¯ÉëׯŸ8yJ=ôò©=OvwïË“[‘‚. ‚ ‚ m.UÑ— s+cåbò“ŸŽ–ªv)sôèÑšššÇŸx¶8¿¢)`ä•IDÁÜþV›Ë¤ìƒÉ¸äqÌ­±]‹8{brÎÍG—|09çœs”ü.Í16RV&Û" Æ„ gyb¦aÆGd\5 ]¼ Y<ÊT÷âfŠˆ Üã"̤±„]ˆ£ïŒ=÷ÓçCÙ‹m?hLkPŸó˜“ëÌÊÿµæ@ƇˆÖr’E—ô•õ_cJÿçéë“iîÅíÛ·øƒ>öØ®êêj®8ºO%cˆLc"n” ¸|Ð5ÝïRiÔ1Ñ>:ƽ/³]ò)ßxI¾$òR0‹÷ák÷O ×þ2ò¥J&D£ÑŠŠ PȾ/ûKT(åäË„n…ò&¤"_ÖÔTïVü‹"ÀÛoß8qòÔ7×jÏ“OùÑF3Ué!‚ ‚(ÆT­†Ö»üízwÐ6C´>>sæìÙ³çô¥¹aYKÃ2ܾñ!DEü϶B @a”dMS ùOceý+Ö2Æ›’þ B ¹l|ŽñÖD|ï…ØÒRßI!NߘÀÑ÷>}ÿ7~çѣǪª«vízÌ:4œþþŒñx­H°ü× Åç‰"^Üu:Æ$%V*øC¥{ˆ†Ì|{žÚ”AiK©ÁÐí/}íKˆö—ÑhTHõNæ…ý¥÷‰eùÒx­p•/Qõ)b»BŒÍÊ—.½óî»-ÔÔT?¾k'¨Y»ú ½lkk}r÷+W®Ìôë7”0NAQÐÓ­Œ´šÉ`¾u/X#I™ã©›in¬oi¬ß¾éa@B@^É—V¦ˆ¸¹©¡a)}ÿÃwÞÿÍٛ著©É©cGîܵ+éh‡Rº'kW É—D– ³pŸ¾ª7dF«÷øÍ‰vëaJùR/8®"_ý¥JgÜäËx.„ùSÉ—)“–Ö?¼îÕK¯ÍÌÜqü6._*ðÎ;£§ÏœUõÁÙ½û‰'w?‘ÛŽªôAAøªäKÕ”8ɈýïtÌåWäésJ+Nž»¸ùŒŽŽž9sN]¾lnlhi¬ß±e#¢!Pæ½|)mŽ€Ø°hNÃÂèæe‹ÎŒ½7ú«ßŽýê®Ê©œœœü§üÇGw©1†s]² W`î’/‰À‚YÈ`Ïrrr2p¼$ä™ý¥ž9®’ž}ûˌʗæmÒdøò¥:[6mì?ôBòç;ݱ¬N©¦ÞéÓgNŸ9«øsmm­»w?ÑÞÖ–«»Œ‚. uä7x=ëÇ{5Ýø,åk—šu÷ÁtŽ›éÅ” ¢€äį@ò¾dÒÌÙ3ž­ I>˜æPÏ9ç.>˜·|écÖΘÙKfü/Q@"Bž;•dô%¦±!Sè›ô¼Ú;ïŒ&_º6ÒÜÔ°cËÆ–Æ@,\ù¥Ö6/[ˆ¸`ô_{vâWcÜS9à/;¶ã‘Gjjª…5° i‹QKÎDâp—Pb‘IèÖà_]<11q°ÌÌ@ò%‘¤`*ƒƒû`ìÛ·¯¿¿?y…ÉÉIP–  Y³¿43Ç»—eûËóÕˆd@mŽÄÁåËPFñÍ7œ9'{ÙÀöm[««ªRn86vóÂŋꡗ?¶k÷î'\+¥dªÒC³_Ç¡ÏcVì¥ZÊÓWc5!UøñøuMjÖ¦`¢é^ïUÉ' ³#‚ ˆ|ÓdK*d’žèöW(y9")˜òxÎ¥e– `jJMÉj&“ p0{ˆB¡XåK?}Äê 0::êT·Çù ÿôï~¬¥q™!,ƒ|ij\ml-ýÕݳ·?PÑ1O?¾}ûv+“#hÖ©Œ]Æ@Ó¬HŽ<ñd%ÍÊí¯µ²'¦y^(ú’( HÁ ý9am®+\{÷>uðà¡ä5‡††Ô[+PûË„ÌqHÛþ2Õ{T0ÓûC‘/º Ã••²ˆ¹}ëGª*+›Orò>áÂù ¯*^E­+V<þØ®ÖÖÙ×_¨JAA‘æ |ÖæîÍæ¦o£££?yN©ìøŽ­›w|dŠXQÊ—ñÊæˆó# ËŸ½ýës“¦<&'OžÜ±cGuuuvO<É—DÁ@ f?8ÕŠùä•ý¥º|)gŽCùãêÝ˦ý¥Hå|ú¬‘XI¾t½4 4CÂ1·mýHUªè˱›7Ï_¸póæ¸bã»v>ª^ÍßYä²³œ÷mË"gŒIYäŒs9³mî …0 m6—¿Ñ—ò^""0`R!Y†» P<ÿÜÏS¶ÛÒÔø™ßÿx\%Œk‘Ò‹Q¾ŒcÎÓ›žú͹éßz¢'N<ýôÓ²…e¼«g‘sù=“›'CJç`¸1Î8˜c˜ÂtB”‹²—É—DÎ ³ØŸ¯î¤ú¨‘eûËÊÊÊh4ª”|™ê=a1O_¾”Fb¹'Ƈ²4©"_BVGôñ‰‰K¯½>1qKqý>²óÑG²ywèÙâƒtIø™ý&ܳ*¯zò ¨|׳+uy¹z<7LHpÉîìˆ ¢Çðįä•LyÙfd)•èIP04‘°¹½°¬Â˜^…7MËÐì/”VvÛ0å›G^ñ§?ýÙØØMï†wlݼcëf›| ˆÌÐ+‹U¾4½6UDæòçÇRd”Ÿ¹H¾$B„Ì¢~¾¦?`ˆ K¾TQ!}È®ý¥ƒ–ڥɼ—/}…^677í|ô‘¶ÖÖ¬uï@oßÀÀà Ué!‚ ‚ |H$¡MÓ24ýËU#gΜM)_~æ÷?ÑÒÔ Ë—²FY ò¥Þ™†yü™ú¹ÏßüDZš™™9}úô¶mÛ2tÊ‚^)$_9†Ìl=íü'S£5ÚzŽ!öïc±X¯ý¥/ùR1ÓW÷²m©ð(Q¾Ä”×Kçïç­Û·/_~ëöä¤â‘ß¾mëöíÛ"Y)8®×ï¥*=AA„o‰$#C‡o-|BÈ~—Bš suy¢å\Áå4d[8²¹­hŒ¨Ûcr§Y­`ŒŽŽž9{Î{¯I¾4ÎbÃöL]ôù‰»Gljjjjj*eÉ’/äËR‚ÌÂ~Š¢Ãƒ-–rDÈCûËh4ZUUå&M³ý%ƃ,SŽàåK«49f<óËo^~ó-Å•›››¶oÛÖÒÒœ…{e``°·¯opp(F(aœL¢-fÒhœ0NÚlŒ¤ÏÒUÆXÊ"'‚F`ׯ䕜jHÊ·oáEîÑQà¯Ú û¹ü /®X±¢­­5Œ}DA®Ø3gH¾ô!_êí4ÌeÏÔ–=ëžÇq;uêÔG?úQïë)ȥ̲q|IdR0‹ü9Zö—Ñh´²²R½oÅciÏOýP–/ÎM\¾Ì¤€yëöä•+ê¡—[6oÚ¹óÑ,Ü&½½}½}ýé´@Uz™°[Y•Í=Æ”mŒü.}*˜9™MAädLNgsW“%Õb6Öa ey¤¯dËcMбÿ$zæ´+'Ü¿ùæ·FF®À—¾ølš"¦¡±1•ÉŽy‘ÆbâÜùs£cc›|™,_êóÀú9ì£ÕÚÏ&½"Nœ8±qÓFMÓ"RU36Öcôàœ›9n ™9-dRå1°ÿ‘ÆíªGô=,åµ|Iï½… )˜Eý¬-ûKS¾T ÀTìUAØ_f[¾ÌØXý/W® (®ÜØØ°eóæ¦¦ÆŒ^üƒƒ}}ýtIAA¤5¥ÊÀÔ,Lù¾ýï¦#bŽb;wî¼Ç ;¶n&ùÒQ¾Ôûß0‡}´ŠÿlJ¸À™™™™é™êšê.§@W`¶~‡äKB R0óì)ßQ)M0}6›}ûËÐåKÅL_Çrg©òb$Ë—Ò¤}ξ|995uåÊÀÔô´âú[6oÚ²esFo¢¾¾þÁ¡¡t´KªÒCAA@&åK/—uPX:×7¿eÉ—:ßþÎwŸ}ö Ë—/·ÏÅœ›²ù`Êþ6R@ži€ˆŒ1ÆDòœîܹóöƒd ÕkijÜñ‘ͺÉH¾L’/õu¢°a!;ÿ+×ëaxx8AÁ$ùH¾,UHÁ,•kNì/½Ið¾Ì²ý¥ß!5ö—V·1Åé M¾ uÔP ëë—mX¿>G‡‡†úû¦ÓUé!‚ ‚ |̲ԦilëÐη¾õí‘«W“WþîwÿösŸûÃ+–KSç>!§3é·ìÊ&7LŒ§ ]wc FGÇÆnzÕÿÌï} ÐKIv$ùÒ’/õÿm\cwá¦K]Ÿ™™™á¡á•+W—e–d8’/‰<„ÌB~¦¢’µ¡ßxÉìØ_êÑ—²Â‹Ìþ2…|inh?èäK+= ¦§g‡‡gfî(®¿aýÃÖ¯ÏÐ044tðÐ CC”0Nä€_KÇÏÝÖ÷bn—®¾™ôNF‘Þîöy€²<›$ø`º9iy1Õ wÊnò¥Î÷¾÷÷ŸûÜgW¬XB·Q¯?,®éÿÕ þœ;çUÀç3¿÷1YˆD ùÒY¾Ô{±aþü®ë 000°|ÅrH©ÂIȦqŒhú&çL3\1™Mþ“—¥ß .Ã$_Y€Ì"´ªçû_B”/U0Õ»—·ö—9‘/K“‡4p  (®\WW»iãÆ†úúL\þ:xèp:-tvvîïé^¹’‚. ‚ ‚ Å9–ïÙSXxË—:ßûÞ÷C1½s Àlijhnl°„ÈøÉ—Îò% 6”á†xþ·º:0<<ÜÖÖîÿ výû®JOò%‘HÁ̃dà;*<ùR1S}B”/‹Ãþ2ð¹T•/jû8É—éÜwî¼{íí·ÕC/×­}pÝÚçÎî-34<|øð ÃÊ*ª#==Ýû{ºiü!‚ ‚&)_ãÕ§rÒUPÈNÃp>ÔfvDz·‰/*ò¥Î÷¾÷ýÏ~öš››m-KÇ@Ö§¬,{Æ:cÌÌâ23Í…ÐWgÏžs*…ÿèÓ¿û1+ú2¾—I¾”äKýæáØ]vóž³t82<âWÁ ,_ú¿Ô³$ƒ|I‚Yøï"~ÞBä…œØ_êÞ—ºJÞÚ_f¢zGfê‚Mvù2QšòešøwßU”/ëêj×­}pY]]¸Wû¡Ã/ §£]vêUzH»$‚ ‚(š Qx"E6EXg¦zñÿÍ7¿¥(_ê|ÿûÿýÓŸþTss³Ñ<Ñ“³ACÍü5ósDÆsc.Ãu‹L!c0::္}Ë&!bÆ–†@)’%$ùìò¥¾æ†y±Ÿßs•hfff***BQ Ü·"ù’ÈkHÁ,ð§u׬Ù_š¥{|õ-ûö—ÁŽ^pùÒûÑ%ô²+_@ëŠåW¯]O¹ÚkV¯ø¡¯óá‘‘‘‘«/¾x$Fzº÷uuuuuuÒ¸Ad7à LÕÈã’ "ÇvðïGN—0·Ê•|™Š¿ùfbåqþÇÿøÁ§>õÉ––{ß ñŸ¨²GÂÐß‘ŽŽz¬¼mË@ÀåËäª>$_škÖGD}DÜœuÎ%\»v-ØKÉsf­¬f—ˆ¨ ÍšBš&˜™íSÎç€<”¹^N™»AH¾$tHÁ,äG¬ÂÐ?ö—råq ûKY¾Ä /aÉ—)~IÍ7œ9wÞíÛšêêÕ«ïÑõrddä¥_ ðÆ&ÓÓ½¯§§›fAADˆªJFÛ1yá…¿ ÿà?”EÌPº†ˆ.\tûvÛ–ñ q@'ùRÄÉ—Iò¥þáú¹³~u<¶ï½û^æ®@ò¾$ R0óø! ‚“ç™?ö—äËí/3$_†"qª—’,_''* IDATJ j—eeeEEE¹c.ùêU÷¯Y½*¬ëÿ¥_}éGÓi¡³³£§»›‚. ‚ ‚(¥)ªÌ$›RY-#;*Ÿ;LaÜÓ›PmÞ”¡ž§ÞÊæqo¼ÿ D­­iÕäùÁ~øû¿ÿ{Bê7þ®¨9ye"0ÖˆÜp¸BŒßRÕj{ˆÛ¶i½!2‚î}‰¶J>¦ì“”G’/­4Q¯ÅnÆ4dzyýúõåË—‡Rò8Q ‚Y/(Š&й´¿\´h8i…Ù±¿TLs`iõMU©V”/ÑúÖüýЮ»-›6özAþ¤ª²rõêUËêjÓoüÚõëÇ^>~ýúÛé4Òݽ¯§{AA¡Î;òªGNê*‘NBg}e¹¹l‡Òk=¢@Ä+Z¿øì³ßùîw÷ëG?úÿ>öñ566šŸ˜©Çîs:äÈõo ä ˜®ŠÑÑ1DYÁ´´6C¾ 0ˆK„ˆ€vïK°DÉÁ±ÄåKý æ»ï¾«xSø¹I¾$ R0 ü]"¨.•MûËÊÊÊ9sæøR³oékÌ M¾DcMµAØY¾ÄÄu2*_êȹä+»ºî[Ù‰hi¶yìåã×®_OG»ìèèèììèéÞGÙâD^¡âé±Z€ë™¬3 ‚ üŽÉùÙ,‘ùT.äK¿­¶µ¥+b>÷“çDL—©¥•þ%'s颾xéâEp¹à›ëãò% I˜³{_’|é)_â†9÷.ÜuM$Åb³³³æ'±HÌZŽÅäA‰ #Ò–1aœ2Ƹ|Í µ˜e»E{–n4’/‰dHÁ,äÇ­Â-sûKS¾T ÀTïXöí/}õÐ|‰*ƒp²®ìW¾ôo àŠžKÎ_¹²«ºª*¦Þ~ûÆ7Nžz%FöíÛÛÙÑÑÙÙAÃAA‘9U%›º‰Üj[[ëç?÷‡÷½¿ÜšŠˆi›k³öBæî47ÔÙÌ.ѰÂ4½/eùÒL6'ùÒÞ2"ÖóÙ›ÂY«yûúÛííæ?eÕRÎîB rÇÙºÊÉ”§¨~ý1C¹ÑH¾$!³$ɹ²¿¬ªªŠF£¡Û_ª÷*Ïí/SÊ—¶Ý±ŸÔòe¸Õæ4-‰CÞ~ûÆÉS§nÜx'þì}jÏÞ½OišF÷;AA‘QU%›ºIr«+V¬È®ˆiM\LëÒ¥Kn«mÝø%_"2S¾4´¹dùÒTI¾”ÖXÏcn fæ.0Å[ÆoÈ9É—Dˆ‚™ït´·rùêСƒO=µÕJÁdßþ2fB¾T ÀTLsm©4à†}øaNŸ>súÌÙtZhooÛ·÷©Ž º$‚ ¢ä±—ÜqÓPŽÏ’ÓHã¶Ð\‡™/¡BÈ?é?»½ÒË]E©«(„þ•—Ù¥´-@hïÄeL¬¹„ò)пŒÅb1©Û³±XSsóg?ûßÿþÜáç~ò\wOOCC½ùËãl Ѩ•¼Ì9׸¦w‰s®{FÍÎÎ ]´NJ<°¾¶Z«tg •èr:9“ÕF]þD@ò¥ÞÚú²ß\˜ãxß{|\·Á«TùÝ„äKÂR0ó)DÜéIì{ÉŽý¥^|ÜoDzléëˆAFì/Sw&Lù2×#ûØÍ›¯^¼8::–N#{ö<ùÔž'³ðêLAA%9—/ÓivùòåiŠ˜}½½=û{êëë½ç q• ¤Y À¥K¯¹…à5-«µ* ÜTñ•|, /I¾´2ÍK^¾ôñÞÿýXÌTÐ#‹Üü'cŒY>˜ŒH’—Î2É—Dž@ f!?zQeØ_šòeØ_ú£ƒÈ—ž?2y{´ª¦1¥|i| æYßÊæhx?áÂØÍ›7oŽn¡­­µ­­mßÞ§èÆ'ІË>("‚ h´$ Z¾ÔY¾|ù'?õÉþà‡¥÷€‚ˆ –|É€y÷«iYœëò%‚d¦|))Œ‚ùåØÌ’—/Ö>|56ÏñhËÇ6ÕmËfä4cȸ±,wúäsÌlWB+L’/‰L@ fQ?žsd¹xñbð#_–ˆý¥›|‰ˆK—–ß¹sÇq«ÉÉѪêGùÒê1z§/³ÎøÄÄÄÄ­K¯½žN#»w?ÑÖÖÚÞÖFw4AA‘Öü(´yV†¦oªk¶´4‡"b.[VJÏ›êkÑ0¸D† §Ë—ñŠ=†¤hê} ¥ÐgH$_¦,Àzãí·›š›³&)0ÿµ|òY¾$ɳ !³àioïp¶Ê2ë”eÍþ2CÕ{ŠÀþ2øCÃ<h ½Ÿòe6ëñ‰‰K¯½>1q+Fl×îÝOP¶8AADÊ—[áÀd+K[æ©´“²M9çfkò2$xJJñ\a…µ¢‚­§lÙ‹ÅôH´˜vÇNw9¼Ê=Ɔ‰%¡õWöXÌJþ•àÞÝ{æòÝßÞ[V·ìw~çwþéŸþ)ðŽôè}â‰'ÌîÍÞ›5:‡š¦E´DÝóõ×ÿyv6Æ9㜛è˳±ØÝ»w1ÂÍÌqÐ43“YäÆî¢³äKCFYÉË—õlöÕ\kpÅ—¢iÅ]„/ÈÊ ‚ ²2÷Ê#ùR§©©é™gžyþùç·pøðaGS±WÆt‹ýÏ•¦™0›•m=e~{÷·vùÞ/ÉKJþض_1›æ]éLÝ›•<1ïÞ­®®îîîîëë ܷÇ?òè#MMÆ• 8ç‘HP ¦i‘²²{÷îÅb1!çVv¹¾Î½»w‘‰ @3¾Ò¨ÕýL9"˜ž˜²àˆ™9ù!ù2¬”l÷&ù’Ȥ`ò›ŠÊ¨ûËE‹ýe&ª÷¤€‰É'Nvûö#_bHÃ÷ôÌÌÐðÈÌÌtÙ²yÓÎÒ]LAAäû´+C•ÇÃx77gõõõiŠ˜Ç_>þÄOÔªFb&ÎA $_ÚJ³VÓ7Œ—ë±äKIÍ4V+qùÒýêxÿý÷CÒ Â¹ÔI¾$²)˜¥ù ¹zÏ¢E‹ ðí/ºpäKL=è[NC¾ e?xøÅt6ollزysSS#݆AA¡ðíïþ-„”ü‡ÿðé ¤äÈ‘#q­T0ifW$n)PRum‘%ˆq²°h)† ©„ÆY˜¥,_"> ¿z•-L>Mï¿ÿ~̈5–ã©g¥8kb¢g\pãC0—!1®Y>­ªñÔ$_Ù„Ì‚G$Ý̱XL/„b©fþÇEù/^ %c©>(û’/½ñ°äËP2**ʃE_nXÿð†õëçΛK÷,AAAä'ºˆY]]ís;Ýû’&˜²‡ 9X¢$³«‡à$B©Ë—ñÕ3 E_’|Y¤‚Yا›ÙT0!üqÅTUUE£Ñü´¿ÌDõž°ì/ÕåKHG¾4Ü/CôÁ¬¬¨ð¥`ÖÕÕ656nX¿žîV‚ ‚ ‚ÈŽ9²sçΚÚÚx†ˆÏGD,&„àœ )ˆ/î@zï Æ3åK±¸b(Ó8¦‹’LîBòûw¨Qަ¼(ôÄóÒ“/‘äËà#ù²X!³€Q©a°rèùã*ö—’/S`ªÆìÛ_ú|ÕåˤÎ@¸>˜ímCÃ#*k®[û`]]í²ºº¹sçÑÝJAA…±cÇyô‘ºº:yî‹ÅtÙШä`¤ÇfgPĘ)ä ÁMƒKC¦Wò` @è³”¸¦^Áœ1#„SØÔFV’òe@U1›É—DŽ ³àA·˜ò%(×÷Õ·<´¿ÌTõÛª)N±£|iå†ÛÅJ7ù2øÊNJó5«×?üÝ›AAQ ùøÎ];kjjЮ¹¹Ï™„ÍÚ%UQ¯äff´$èò%2ùJU¾Ô¥[b±x‘z9)SĬÊõ…Àø?Q4¾BÆíiš$ˆB–/‘ÎB†ÌÂUïýðí/åKÅL_}˲ý¥¯q9˜|™êuÀôÓ“/1¤ÑÚMÁ¬©®^½úþÚššH„‚ ‚ ‚(lŽ=¶s×ΪªjH]wÔø ¹Ð¦*"ãñ"ãÌTñ€%öt–/±$åK¿a˜AÂ/I$_¹ƒ„†ÂG¡Œ5„m©çgÙþÒ×Z0ö— òeBœfnåK„z>+»ºÖ¬¾Ÿ„K‚ ‚ ‚(&Ž=¶cÇŽšÚ` „îƒÉ…4[á0;À˜ÆMNp®§Š œÅ§1gñ˜JkjdUõÑ—™þ!â±™Pjò¥Ÿé[ÖäK@æÓž“äK"DHq(pB•/ý`zÿ.Ù_Bö—‰Cqò%†§bv´·=w¡ª²²ªªê¾•]t#AADQrâĉí;¶×ÕÕÅ„@DD¡«–ò\2›DÑE<í;  3ÒÃg( e³Aa´f ‹RfzÉÉ—è'L&ØÓÿF}IäR0 žÔå|–/+++!Uþ¸rçKÝþRéˆ/¤Ì°c0++*öîÙ­iŠ»$‚ ‚ ˆâæä‰“ÿÄ'¼T"S…DI…´ä?ÀÐÔûÀ¬EŸ5A\Ô¹.y\¾B¯ðSjò¥bV6ïK—é¼ÕÚV“6Móœ’bÂxÇc’|I„©…M*áË_—ñÎöm43gNÖì/3$_†f’|©r¦RÊ—nß‚Tµœ ‚ ‚ ÂÛ·oW™¶9Ê—è4ÿ±-A¯Þcü±«¢“ÇQ¶ ]@¸E_y)˜ŒÊ­¨€é‹ªªªlÚ_ª‡ðåKÕLµ±;tùze} .ò¥_7‚ ‚ ‚ JœM›6-]º4‹ C!82L˜‹Åbc±˜™Ž—¦U 9Ó'RŒ1d ÀH0g–’¨{eZú ¤–¢|‰˜'(ä}Iä¤`æ/LõNG¯‘#ùã=ɉý¥Ï1Ûö—i½hüd²XéW¾Tä ‚ ‚ ˆð^éSyo7WctȈ|bÓ¦Mååå±XŒq.„@z¡„™ŒqÓ0¯«"9"r&0n‹‰œgF]r°4>»°ÈPÒKT¾ÌýÒï¨Dò%‘9HÁ,`FF†S”±†0åË”ùãjCSFª÷®ý¥RfHò%ÒNAA„›6mª¬¬²U¢û$Ê,Ö4åp Âìôɋе1Ä$ÉOc3YIÊ—î"&¢å_)Oñ—Ñ’¬¯ü{¦Ú½/y_9ƒÓ!È:ÚÛ?¿zu8;Ó—| !åè[¾Ù_†/_¦ŠÖL_¾ôíïIAAD ³aÃï ¼„)‹›|i)Œ–ʉ¦÷¥$_¢%_‚¬3–ª|é:7t˜Øš_É=•ON ½À¾¬`ËIò%‘iHÁ,::ÚÓ¹3C±¿\´hÙ_ú:n¡É—P¾DI¾”žit?AAáÅC=T^^îcË·QR“ä?L”/É—®ò¥ÓÜ-{Â\)’äK"óPyîñÃNwä )\W0Ý~"oí/3R½'TûK¥Ýq‘/¤Ls5ëE–/@ƒ9AAD¾Í2: ¿¬]»vÑâų±˜>wBhšÆ8"†(2Ö¬BÏ1¿wï D#š)PꞘÏçœóød†¦Å c¦lǬi‹¬’|)Ïç²Råq"O!³¨±Ä«àò%¤ ÀTrcégüÌ;ûË„§":„[’|IAQD´¶¶>þøcæ?gïÝ3—ï¹, !L§B!QXSÛû°¥“rn-3Æ¥å]Õ\¶u2Ó’«Êk¼ôÐCŸò¥9•€Ä©¹_:÷fgå“n}~÷®¾055500NOtù2ØN€Š|‰Ö™µ!HÑ—ñÉŽˆWòR”/m¥x”笎‹¾®Å'> ¿$_@ f¡ã÷O1ž¦•••Ù±¿TìOÞÚ_fJ¾Û,ù’êùAA¥;uÊ㔤ôû655õÊ+¯¤Óš5k‚Ê—‰´mf— ±™B/Æ!Ç`Šø¤†IB!Øëü” |)ÌÊ'q!ýë–äK" HÁ,öçtÚùã-gßþRq—sbéo¤·ïTêZäAåK‡Ã`zfæì¹ «î¿î2‚ ‚ 9DQåŒ1+ò‘s.o¢q+X’»NÚ^E0#¥˜ aË*E·WM9™ #è/!£Ü5î2ñ˜îþûæ²h‹ÁD±X,&œ·½7{/¯.!Ðqb0…üO)[\^ŽÅbÓÓÓ§OŸN§?«W¯^¼x±&æIG!1aD½2§¢ÔºF)„t) ]2®Æ±xˆ%0nsÈ2VRéÆuC’/3«Ó"ýÞþ$_éA fÁãñVd{$Ê7 ø°¿ÌDõžPŠ˜ƒB…ŸÜÛ_&½“¦”/í×CÆäK˜™¹ƒCCƒCCU••+Wv-««¥›‘ ‚ ‚ |¿í§G(òå’%KÒÛ »( ‚zÔ¥9ÒµIS¶cÌ’õª>qRòÇŒ'Ù—˜|™Åü¹,…R’|I¤ )˜…þ¼óº_¯_»¾bÅŠ`ò%"zÈ—*˜Šýωý¥ú±Í‚ý¥Šöš _&†ÖªÉ—!îCÃ#æòÔôô©W~ «WÝ¿fõ*º+ ‚ ‚ Bq:”f“““¹—/ÁšxØ3ÇÀˆÊd`*wquRà åª>†@)‰zœ;•š| ˜%Û’/‰‚Ó!(‚çÞcíNûq“(_F£Ñ²²2Ȱý¥z÷²oéëÐeC¾ÄäËPFxY¾”¹üæ[?úÇ¿qùÍÛ·'é¶$‚ ‚ ï‰Dš LNNž½P13yꪪäM€ì/Ù_¦”/=­œ|0Ê—á ÖÓ33ß^~ó­Ëo¾UWW»¬®nÝÚéÞ'‚ ‚ð~í—]&|0¹¦™Ëekî6-³6çŒÏÆ7q36I¨d-$¿Kθ½c®S畈`ø—Qlž˜(¦&§N:•Ntù’É^«Æ™EÉžU0Î9g<~A0æq]0Ã.ÓÚOfÌfC¹ÓW4äKÆÀV½ÇPý@ Ê— ^²‚p¢‡®ÛBö¾$ù²Ð!³ð|˜âþ?¾páÂÀ!“dé·ÚxêÁ;mùÒèsA˜Ó33º ¦7·&&n]zíõukܲy3ݪAAaMâÒ#,ù2Ì]²©Šz4¥0’ÄÁ&2n¬ÉâÙåz p©EŽXŠò¥ú$7[ò%²¬\ë$_n‚YèO¿wxùRwÀ²¿Ì–ý%*(‹êòe‡vù2 Ó-…ÜK¯½~éµ×ëë—mX¿¾¡¾žnY‚ ‚ ¢¤'pi«(“·oç—| (_¢$_ê—­ªO|M¦—/åê=²diÕ4/-ùÒW¶_ Á甸dÉy*‘`Éj.qÎ̓Æ9gqLãÿÛ7ysôöêÆjfî¦ï€~^ÌÏQúÍ' ‘€Ì/eé°´äK‘¡«0À$_ù)˜Å*ߺòÇG„¼µ¿ÌDõ•LõS‡Þ›„'_"† dVVTTVTt´· (zb&súÌY€³ÍÍM-ÍÍÛ·o£[˜ ‚ ˆRÀM]“+ù0»ç¦`F"‘„&ÌÍ3L!eÕ:é¯ó™Ý¸*˜r‡„µL?\Ü¥Ÿ9W0Ñ®JB%$H±Ìe¢751õòË/§Ó‡×®]ºt©ÜÂñzCDιÆ9èB*çœóŽÎ®‘á‘„KT_f 8®w˜ pf9žé¥âŒ1}ØQÇGR÷äèK«’OéÉ—Ug@7À,lBò%‘9HÁ,lÒ)Õüm43gŽßüq_½Í¾ý¥â¸œ#ûK?‡¬´<Šû/C½ö:ÚÛVj]wÞ}÷Ê•©éé-ܸñÎïœ<õÊöm[w>úÝÎAAD‰ÏÚܸuëÖÑ£GÓiA—/ï€âdìßqŽƒäqÉ„@CÅEY4Œ«ÂjЛY*Ñ—ž±'Ë–Õ™Ë Tÿ´@ò%Q‚Y„Ü»w7!<éÉâœZ¾hÑ¢Ò´¿Tí[ì/Q!f3Dù2ô!¼ºªªz[ ¾õ/W‚5ròÔ+'O½²|yËÎGY±|9ÝÂAADÑŠ|yäÈ‘tZX»ví’ ò¥ŠŒõÆ;“k*¥4’|i–0U°æÖíÛ—/¿u{r2@ ׯ¿ý÷ßÿصóÑÝOüðÃK–.EÃRå+wòWˆzæ8j qMÓ3Ê;»â‰äÉ— àŒYæ—zˆ¥žQŽ`~à ä©ùÏĺär RоDŸ»0´‹äK¢X ³°1E*ï»7eþ8H˜¾…ìÛ_ªw/?í/]äË”a˜Îò¥z ©äK ÅÓ“ÚššÚ]5SSÓSÓÓ—^{=X#G½|ôØË?¶«uÅŠvªöCAD‘"{_ÊÚ¼/|0c¦ùcŒs³ö Û»½ÔÌ4§÷™öÄD…J>òÞ º‚©in~—‘HYgkɧÀcæbÎ c‚³`Á&ëׯ¯¨¨B Ó‘ŒÙ*D×BÓ8×8&4ÎõÕ8çŒ3Çâ?oŒMon¯‡¸ M1cœY)bVâ³]‹dˆöº=%,_ΙïvB««ýùc’|I¤`>¦|YBö—˜ûË`gX–/“C2UåËlQWWÛÔÔ¸n탗^{}|bbbâV€F^úÅQhkkmkk{r÷t£AAE3MSocݺuãã6×åË÷©Ü½µ[ïÿÚQh²¨‡†`g”õB_4Öò$YUÈ ´äË)äÌUa”õ‚Kˆ˜h”ÉRQ“|Iä3œA¡?QÍÜÐC¾€… –œý¥zß2i™òœ|)³níƒ{÷<ùôþžúúeÁZ¹úâ‹G¾þçÿþ…ÐíNAAÊ4-¤©ÀS{,hçÊ IDAT÷ÖÕÕùÝ|ÃÆ áÊ—PQQîöÕ­÷?”äK›Îˆ²`gÔêA›ð'´EÓL³ÄäK@¼¸Ôõt×ÔTû½~|\r$_ù Å`6×®,_Ñ–æ=¼hÑ"pŠ…ÌûËLTïÉûË”Ç!4ù2ôräŠ4Ô×7ìßç/\8áÕ`¼øâ‘_<ÒÞÞ¶gÏ“í”ZNADñà˜“«Ã]²È…¶íML!„ùî—¹,ò ·î »§¤•Eãñ,òHÄ=‹\ GY i¿0ás§¹cÌÜåžýûûz{ÇÇÇkëÖ­KË—Š˜0¯a÷¤2O¥¦%ú«"çLãZD‹€žE®iZDcWGF¯Ø×F§j¬ÆA¯Ê£‡ÿ¡n‘ÿYfÌg ‹a›Õ$Ô‚’“/=0ç/˜ŸæMäyí“|Iä;¤`{÷>uðÐaǯ®]Y¾¢-@%ŸäL_ãB–í/t ÇþRiGýË—IB*`®$Lƒ ë×oß¾íwFOŸ93:: …áá‘¿þëoÀž=OvïÛKƒAAD‚:)¿:zbBB•Iµ”µ<&„0d;R}û[¦MôÌ奄o“ ËSsV0Ë*ùdó¨¸)˜z…"}ׄ(Õ_Ú·o_¿Šˆ¹mÛ¶êêꘌ ó"aæ`Rm f$b̃¸Æ5ÝnUÁ5®/ǘи–­l.¿66½¡¹J×3ü.Q_Ž ß ‘a‘©¯‰æѓǡ”åKïÌ ¤såÃÅOò%‘”E^ð¸øAƒãÓ=ù“ÊÊÊpí/3Q½GET2 Åþ2ù˜ºÊ—ÖÓ×S¾ȇq½©©ñw>ññO|ücÛ·m ÜÈáÃ/|õ¿vðà¡¡¡!‚ ‚ ˆ´ß¸ó§'®]Ù·oß²e)Ü™tùÒÏ2ÓQ%Þaå}+=¾½ùÞFJ¸iv)+†"Ñ×Ò*Õc•î)eùr|ÎÂñy®UÈõr&ázýȑݩ¦S^ü,É?M-I¾$Ò„b0 ýI ê…È“?‰F£eeeáæ«1d *˜V>¸]‘4„DM3KòåÐðÈ;ïV!›š;:Ú·oßvòä©·oܸqã]Z¹rå•+W¿ºøÎtý}õ–Ù%Xᥖ²©ÿ˜ÌœïXQ¨Iò%ša&Å/_ÂÅrÌùº)ÇÀʲ"c€?¡,ÁžÂ]ÄT’/âÇK¸ I¾,)(³(ž¸A74ódzl©>å­ýe¸ò¥Ça‘‘æÈP¾D KÆœž™™šž~þç&§¦ÒijûömŸþÔ'?óéO-_Þ¬…¡ááþþƒ_þÊWûúÒh@AAøT@ò§'J]Ù·o_mmmòç;wí¬©©ÉÂQ¨ªªrûjü_?¼ùþ¯Ñ%úÒªÞc¥´›ò%:Ë—%} ãsx`¦L!ǬøX¼|IŠg!C1˜þ¸µ”)¥[ZÖõÊÊÊ¢ÑhÊ{>CÕ{ ÝþÒO÷‚Ë—ðÞ»ãM°.$ù24††-÷îS¯ü²ª²rç£;ÒipÅò–Žö68öòñc/ÖHÿÁþþƒÝÝûº:;i| ‚ "çȱK o}Ž n…hÖgÒjn‰G ïÕ(DˆzDfç8n>˜Â^ŒÈX-‹é_Í™¸ø]Λ?2¶ÿiÚ" 3\Á¶_BÉS6E}úé§{í…}vïÞ]WW‹ÅbÆQŠÅb"s¼lŠ€qÄ„Ñ9Qs}Î5½x”B3}0c1ÎxCC}]]íää$ØK$éWï¡Á[_ܰBã €"p`\ºô:?€Àôÿ&Z2»çi)È—€è€ Áåi5!ã[(mEò%¡Å`>~ŠðÈ/4Š˜Z&ûË õsæÌq\áýw'Ò‘/åbçÒˆ=4<23sGþdjzúÇÏ=ëöíôßùè#ÿçÿþ¿=û…Ï·®X¬…ÁÁ¡o|ã¿~ñK_éíë§á ‚ ‚®¬dHF CWééé1=1uù2£‡ŠcÀ‹ÿ_o«Í cwâ3–¸–'5Dk:å”'^bÞ—úòøÜ…ãó¹RÝÓí¼Ä—íXëØÓÉ-GKf\²ŒÝ.$_!B1˜…þÐU½ „¿h4F!¤üq_ÍOûËLÈ—éÖOoåKëÀ~‘„2bëùãŽ_=v¼¦ºú±]¦ÿ+­­+žmýüÕ«×®]¿~ä¥_k¤¯¯¿¯¯¿§{_gggW…dAAië1éË(áé*===¯¾újeeeFå˸rÉâŒúòªÕ«ßzë-·M^½ùnÓ’yõ‹æB¡9M Íé—í«xÚ’ô¾Ô›íoèð8 ²o@‚:‰ˆºˆÉíŸ;Š˜úÙD@–Ô”âíÂB½ÉH¾$|A fé=©`áÂ…@ö—ò“Fm΄ý%*ØSºÉ—˜°§)äËFm7ùRçöääþñÇO>ñxCC}ú¿ÕÚº¢½£}Ïž'_xñÈÈÈÈÈÈÕôöõôwvvtuvîßßC#AA‘?òešæ…?üðo~ó›œÂûW­zëÍ7ݾ=?vçéÎZY¦D É—ˆÈ¬Yˆÿ3Aj,ù²ÞK¾ôÈâ`˜Û…äK"tHÁ,´]C40S®ŸŸö—ªÞ“:Sy<-Vùòêµë ù㎼p䥺ºÚ½{ž ë~r÷O Œ9òÒ°§„êÆààÐààPo_OO÷þžn‚ ‚È-Š¡Ošd}è–ЉŽÎS€t%€ìN^R~.„0ÿ)„Ð}0£Ñ9Íy†;wÎÜw?=å(¡Z¼_¦÷eLÄdëO®{Iê[2{ jƒXl6fx_Š˜07(ä½6íVѲ^E°û`ƽ/…×LL`L÷¾|è¡u3ÓÓÓÓÓVG¤]šø`öâÄ¿n\¶Hÿœz%3v6þOã*-eùr|Ήù‹<®œªªÊÐn®@ò% òC¡ß@Ao^’/‹R0 ž”Ò_²qdÊLåßÍý¥¡-ëö—¾ºg:½3 yàÁäËô‡îò¥Kל˜¸õ½ïÿÃS{v/ /¥¥½­meW:üÂáÃ/k¤··¯··¯³³sO7¥–ÅúP ƒ@‘õÜI‚”y¤‰äÓSVÞwß+§N¹­p~üý†…e £–õ k à\y<þm É—›¼f%UU•^¾–îËRæ8³f–>ÕHD0X’|IdªäSèàÃC²|©`¦Ÿ?îw É²ý¥bÇòÉþSŒºéË—˜n fyùÒM×WT”+®èð‹ƒJ<µçÉÿö_¿ñµ÷ÕŽöö`- þÕùÆ=û¥½}4ŒAA”Ìì‰äKß0'ÍKïKuuµwIŸŸ M½ÿ[s2”$ÿ9Ë—ˆ¥"_‚oùrþüù555<«Œ3ÿ'-²°®8’/‰ÌA fi=­qÁ‚déOÈ˰ý%¢Âpì_¾Lx²ÇFڹ䕛7nèhoS\bâÖ7¿ý±›7C¿¤;::¾öµ?þÚ¿ûêÞ§ön¤··ïžýÒ½4JAAý„(ýVrÒF^I1rgvîÜé½òÏFfnþê®é}i5j”CÉÉ—ýÞG¯ºº*œ‹!€éÉ—DþAYäð¥Þ{ï½Å‹›ÿŒF£sæÌñp–!ûËö—y$_!—I¡˜€æÞÑÞVQQ>4<¢b‹ ??Ð[_¿ì£û÷‡~ýwtttttôôt÷õ ÐÈÞ>è…®Îή.ªöCAD¦p3¯ à‰iyºû`† ä¿>˜±XÜ/R‹D¸t”d¢ee¡t-½ý2^Ìfûeú{ê[Þ“N:“–ãþ˜ñC±¼/…@aŸ4?͙რ¨Ρˆh%‚s®_rB ç<¢iú¡FùRDÔ"Úc=vìèQà’ ©|UÿìêgZ—6Ì/c†'¦£÷¥.22H%‹W¾ô¶¿¬®®Z°`Aú72’|I ƒYØèƒáöí»­ðþ{š-\¸°ˆí/3"_*`ú|…NÓQ¾”7xÊ—á¾¶VVTlݲE7¦TáæÍño~û;ï¼3š¡{¡{ßÞ?ûÓ¯ýéŸ|­££#X ƒƒzû¾ðG_ž¡¢³³ãë]ÂëíëïëëÖÈÞ¾½}]û÷ww)K´‘ÖXž™7-ªêC!ãb³DIÏ»R7‘;ùÒg; LïDÝ#‘1ÆQ¿ØÍ+žq®‡p>±{÷‹‡ÏÜy×£Í篽÷ÌòE ó˘‹÷%*ÝcÑÒÒl†Z37#Õψ‚­E,I‡krd.çœ# Æ5Î#ÜúÜ­ø H¥{ˆ¼…b0‹cê+¼“ %È3mék0"ûKKkô|$„ _†/`TWUmß¶õþûV*®?::öÿç¿e.S§§{ßw¾ýͯÿÙŸtvÉüË¿ú…dAAÐ|+Œ)[v¶ñšye´‘íìHÙÈó×ÿõÜä¯KÙûRQ¾ljj ãªÍFÀ&|IdŠÁ,üÇ©»|)c`& 9±¿ô5r±ýe¦åKL´ÛÉÔ˜¾êþûêêj/_~ëöä¤Êú?þÉsÍÍMŸþÔ'3zwtvvþù×»{Ó ÉÜßÓÝÕÕI!™AAd“4­3ÝšÊû Žó+«éðœs4þ‰œÇ}™ë^ÊÆ‘þ5Æ©´ËC¶Eibì Úú,¡j1ëPD¤Ïc >˜Ö¬®D«YDŒD,OLË“ ƘÆ5€V  ^ÛlsÇ#ÛO?é}ÎMþææ÷žiZ(_–€÷e}ŠÌq¦¦Æùóç§{‘|I#¤`6*£Œw¦úBö—ì/åKï6}Ë—òÞHWI¦óIkkjjwÕ¼qùÍËo¾¥²þïüçÿëÿþÔ'¿¥¥9£ëêêìêêìééîíë×ÕÌPµ‚ ‚ B$DiÒ­Ùžã(¼µ¢hèhˆ¨/sw “S0!ÌCŠŽb‹<÷aˆÆk|zìØË)›~ìÆy|cy´a+nù²¿¾}|Þ"•CÞÒÒ¬€«÷_Ó4ÙïRŽº-“>/‹”™ç=¢EÌèZs¦¥&¹¨=‹„Æ“?OO…Hã"ù’ð€|0 ÿÁª0xè%È3j©8å³ýe†ª÷(æ{ÂÄÊãAåK¥çLÔÕÕîÝó亵*®ãÆ;ÿéÿøÏ×®_ÏÎ-ÓÕÕùþgÿïw¿ÝÓÓ¸½jù_þå Qˆ ‚ ¢èfY9’/!¼/ƒõ¥¦¦æ#[·¨¬9ö¡xþæoÎݹW¬òåÅ¥µÛ¶NQ¾L޾ Ry©ò8QÌP f<\S|o`ÊãBNì/}YÙ·¿TUC”/* ËÉš±Où2«ƒûºµÖÕÕ^zíuÅ`Ì¿ÿþ?,_Þò¹ÏþAÖz¸¿§{O÷ÀÀ`ooß@ÐÌ¿ü«oÄ›¢L‚ ‚ rA€HÏ ê·…¾Ê{m&3m}J"éÇÏ:d–j8pÉì‰çÖ²Í%LÁ¹úš9 ô>ˆVkDŒHž˜Œóx¶8"cÌXdÜô¾äf[ ˜¹\UUµuëG^yå—*ÇéÜ»³çÞ­ŸÃ6.æñxÌ—/Çç,Ÿ»ðbyúÅBò%|I(@ fƒ©ïÙäLõá#ûö—¾z˜Ÿö—y+_†>º ­ºÿ>–ÕÕ-ÛS÷Æå7Ï_xU¥Áë×ßþ_þ×ÿô…Ïÿa{[[Öî¡®®Î•+¿>00 g—k„ªýA 2D‹Ì">hÌP0™»„©~ôB±¾ÄT­0‡î™SS_Æ\̈&Mé­I½B£J«y¢ý×¥£'+˜<Þ¬ÁX\ÁDœ1Yè4Ì„J>òruuõ¶m[¯\¹2==­rÄnþ6€ ‹ØÆ…Æþš|yqIÍøÜ…ãsúºZåËlÜïXŠ¥{Hô,`HÁ, :ÚÛ‡†‡“??uê[·=–|·ß½{W¯<FËÊÊä+Ž2ym™ùR%Sýà­Ô¦^|È—ŽÕ~B­Ïœ;?3sgfffåÊ®êª*57¬____þÂ…›7ÇUZþÛ¿ûûÖ+žý£ÏgóëêêêêêÚ¿¿çÀÞÁªöC¤?oIõy«\•ÍÉ+“ ¯þå-9.{‹*™¤Eæñ<ƒ¯ééBÙk0ÎâÞ—‚!°øEË9GDËûR3ã4!V1?¿)ãœÕ-««[Vwüøñ©É)õ_?ÿ¯xþ_6,@Ø0?ßåË‹‹ªàâ’Ú‡zþüùµµ5 ,ÐÿÉ9çF1znÆÀh-"ù]FçD-A§,⼬E"ZÄ8Mš)ykœkº0 ˜0ÙÇ.ùsÛ â=z‘|IdR0 ƒŽg3åÝ®iõÊ/·mýȲ:¯gvC}}Ãþýç/\P ƼzíÚÿüþã³_ø|{G{–ï5]y8p xj9Uû!‚ ‚(8r'_†µ¡´áþîÖl}ë­·†††ü¶zþç?ˆKoëçŬ3„h_kRIŸÛ `<ü”¡} +…MÚ#4çQ˜Ð‚.\¦CuuUuu5ç<û×O0!O1w0· É—¥ )˜ÅL,‹ÅbòÐ@ö—`Uô5°fÈþ2å!r”/Í:@ò‚§|Ö€mÊ—&§^ùå£ìð1`ÃúõË—/?}æÌèè˜Ê}÷oÿ®­­õ+_þRöï®®®¿ø‹.8p 7ÍÔò®ÎÎýû»)µœ ‚ "Dcæ[´;æáƒ™ZÝ'yÜo#ISffŽ'$Å›À‘KŸsc€1ÆÌ$tnÏÞfr˦‚†ˆ²šÆcœÇ™§/ý×ZHøOÒ‘ìì쬬¬šQË(wä‡ZÚÂu tŽB ¥¥Ù ½´_?Ð&ù’(\HÁ,ÜîÅ»wï¢ÏAì/³l™P‘ÜûÄ$…bú”/Óü&Ë—:/?±kç#µ55Þ›75565}üôé3§ÏœUù¹‘‘«_ÿóÿå/1›Î˜2û÷÷ìßß“fH&Uû!‚ " ä’†œáñ[iì Zéƒé¶kÞ»Œ¥-¯.)J1ŒY„ºñ¥á2iE  GÇ àšÜŽ<ùpóÁD@Íô¾B.ÄŒƒ&ôüñ¸XÉ€Bg‚÷e¢²)QYUµ¥ªêÊ•+#.™…y|¥…Ü`eeemmM¾Ii’/‰ì@ fÁãV &‹ ƒ³¿Ì|™IûKk½‡öäË´Ã0_½øÚwßuûöè±ã*"&lÙ²¹±±ñì¹s7n¼£ò»ßúÖwrŒ©£‡dRµBý­KѤRÉãRZ™÷Á$M‚ ˆtd8˦‹É_Iái~ZÑF“(M'ýh^=r}ô߸dQ—5L[,§Æ9jú]­­+®]½6\ :f¸çhÁ‚ÕÕUóçÏ·)ÅÌk:ιY=IÓ"¶êó\C•³jû2·N‡,.{¼’ze;ä}Iä ¤`íúîÝ»ò*Y¶¿Ì|™2Su +ûKù¥*}ù2”§¯‡|©£‹˜ õõ)›jjjìèh?yòÔÉS¯¨ü´ŒùÕ¯~¥£½=W·Uû!Â$H–®‘é f€Aa¾MAäˆLÇ82HP0¥eî®f’:Y`éL>ëC}2¤Îèýw•õA×½˜yM3Æé7‚yïp&Uø‘ÂB"G§êažííí탃W¯^ÍïW¸ÐšZ°`~MMíÂ… ’<²p®™_qÎ5KÁÔ4Í’n4M3/9MŠÀ•›•ÅPÆ8ãâg™Ûƒ—SŒ$_y)˜ÅùÐ`J“á,Û_*[¹±¿ô3¤fÞþ2u¯üÊ—¦]¦´á ߯[ûê¥×¼×9zìx]]íÞ=Oª4¸}û¶æææ“§N)cþõ_³½½í¿úos{ÓQµ‚ ‚ ˆbIeA‹ÉCù2 ¿›ðC­m­-Ë[®]»výÚõ|¼6B:G55Õ555êß3²'ÿQò8‘g‚YÏÞÄ;Ó”/} "Y¶¿ô5ºeÙþÒסK_¾TøÑòerµùÓ}M+/_ºiãú³ç.x¯61qëàáEÌ––æ––O¾òÊ/Ÿ8©²þððÈ×þäϾúÕ¯¬Ìu"6Uû!‚ "Ÿ)¾P·øV+r–yŽäæBð¾ôœõj?òë?“r±Qr®—>Ö‘"ì3[àŒÛæ&ò<Àååß, ôÿ³÷æñqTw¢ïïTµdYjí­}ߺm³ØÆØÆØ¬Ãbk’I&ÉÍ@B–™„$ñûç½wïs?wî d²Ád&Ìš™ ¯Øì°1H [«eµ[-[–äMê:ïSË©êªêªêêM:?·íÓU§N:uêœ:ßþ-‚€ÕVÌT5@Ô©$& œäSùS§«1zÍFUiiiiii9}út$2ybh(S—×¶%¿ ¿²²Ò«G¯3 äWŽáK& DÁ\3¹É³‰ °c¶¸¿´86e¯ûK°/5•6ð¥ãxyY™ë6Þ¸¡µµåõ7Þ ‹ö³ÈF\×\ÒKÁxô£Ó‚ Ä- »ûÊÈ„ &Y.ãºèG#Ñ`2k£¨î5‡ùÄ̈5Tâe¤ß$«úV A@EñF„kÆó}É)¦ÊÏqG­Nplóù|¾â¢âÖ––©©©©©©ÉÉÉ©©©lìÕÕUùùbãÒ}º‰¹·h¼¯n7^öƒÉój+r^®œÊ'¦Úr\Ió¢9Ì!ÅÀ©¹¸Ñ˜Ãð%“ÌF0ÀìoXèî/m c™êþÒÕ`JF'3j>Nö"Ì5|“´rH´ßWÃR‘¢GI¨œB'ô&Ïór±‚ÀÉ”M[s:\Œ€ ¼¼¼¼¼¼®¾¦Îœ™šš"?ßg`ר®®& ¯×[Xè58C!¤ï0—ç9E…–ÆŽÔøÃó<ïÛçxÚ÷¥Ç£ò‰I«¤9Žã9Œ1¤ö‰©võ«ª¡a#hF/†/™¤KÁÌþù7ÞÞŒr™Œè=YíþÒRÝ\—.äböôöMNž6ÉF æ_|á ÖK¾iÓÆÖ––×ßx³`ÀJþO?ÛÑÞÞÕõP†<,ÚÒÓÓêéééííííc­Á„ &nÉÀÀà€ä_ïÕW_€[n¹ôh&“E¶|bø¡$œâÆ..)).)‰ÎÏcÀõ õ P¿þªŒê“RÙìÝAËÕMð‰Ô´?f¾/“50I›0‚¹H§ádàK+tÒbÝ2Ûý¥ËøÒ‚égBøS¡É]¯ËËÊ*×V~÷½‰HÄ$ÛØØøï»»W¯Ze%@9‘ÖÖ–ŽŽöW^}í•W_³Dz{ïÿÁ~èA¿¿#sJ퇉•® õìÙ»5&L˜¤FÇ|õÕ×ZZZÚZ[n»õÖ&lÝ䑸€ohfÅ'&hÒ±ìL½ǽV Xmݬä§Òl$„+Gk¼1¨7»cê)"w™L¶œ4ó}™e7™‰%as!LÃ8Þ3ê8\8¤Ãý¥õá)Mî/­ß“”ãK©Lí¥I'ššøL÷ÄêÁl¸aýÛß1‡˜##£/uïüóÎ-Ö!&ÜzËÍ­--¯¼úšEeÌ'ŸÚÑÑÑÑõðƒõt²h? {”µèû’v^F`ß¾—wïÙË– &LÒ%¯¼úZkKËÍ7mjjn¢ý`Òãhu¼…連Q¹Ôt¤±Å Ï]ŸÑ](#5ø&iðÅ:ØB*oOÒî9m<%éAà8™‡ò<¯çúR{ ì¡Þ°@¯e©w*Œ•]T»ª_ÂûŽ]Þ‡© Æ¥ ‘7"ò'Ö^›„ER9¬T9²¤ü`*Ö÷Çñ„¸t IDATœ¾ïKC?˜ˆSÆ"ŽSX³ÚŠÜVcø’IÚ…Ì쟈M÷~öÙgŸ}¦…Våååå>Ÿ2!Ä.»¥-¥¥¥¥¥¥qÇ#æþR“ÓæÈw¨5×ÒÎøøÒdÌ.*,tÜ­@L ³­­ÕzÉ­­-ßïø›W^}mÿþVò÷ôôÜÿƒu=üP àÏ´ç”EûYk"Cš)ÌžžžÝ{ö2Sq&L˜0ÉéèhjjÚ´ñÆææ&ÐLÊyœæUÊ-j¼Êq\|Â0eª¦êd¯¼\Ã7®Ü6â‰é–H\‰!Ð J½b·KåhS‡Œª%©²¸Qiy ‚ :Š/²²ª}pCÍSG1–\(µ™Cœ|”Êg(åÔRE09Žã9j»B* ý`r¼|9jß—œ<2!‚‰1æLÇ+†/™d‚0‚¹§ãH$‰Ç›ìJQqqqQ‘2»Rx”Þˆ ½^/sé`HÆêVµ‹/“7h[‡˜ùå/54ÔÛ*üÏn¿­­­uÿþ}}ýVòoò)¿¿ck×Ãø¨²h?‹Yvìx¦§·—µ&L˜d ?~üŸ~ýÂ1[ZšYƒ01xw°LËJ|™àyS'=ñÖN™•:N Bsj<žüÇð%×…ÌìŸJ3ãi<;5uvjÊJÎQËeæç瀉R×-¥ó–.ÍËËKžûËdàË´gHÎ|©6…P¶ß´éÆ7ß:xòÔ)óÃÿý?~çb¶·µµßßöòþ•1C¡žï}ÿ]]üþ |fY´ŸÅ&{öìeÎ.™0aÂ$ó…pÌææ¦›6mlinf ÂDý>î`™f]áQq'úÄÄ¢ ·:›â£†³ êk­2X£G¾Æ‰j€TWÁðeZ{i¡U†/™$CÁ\ê}&Ï;wîÜ9wËÌÍ]²dI®ºÝ´L”$rrrrrríú •þw_‚;ø2sí-7o?yòµ×ß4Ïæ bÀŸÝ~›ßß±oßËp·oÊïïxdkWÆöpígÁK(Ô³k÷žžžÖL˜0a’-28xüWÿøë›6m¼iÓFÖLT«{ 4—޽݉<î„Ó9ðoà¾Ìò•ÄÛš1PÀ‚ö%ûÎ.¾d’$a3û'TöLÚ‘K—.^ºtÑÝ2yž'þGô(ýs‡ŠÅ—æ?l:Ç—Jƒ+Æ'ñ¤ª²òæ›6Z˜õõu߸÷»åw´·wü¨}ï¾—÷í{ÙJþP¨ço¾wÿÖ®‡3Ð3¦,,ÚO¦­ñÃ5hœ]ª¾F£{öîK\õ²sËæE°ÊËð“b7&_œîKÆŽŸ„$W»\8v\8NbìΉ°óŠáT5©Kͨßñìê õ:¿ñfo_ßnh¦”1ißs9óóJ:'GNsœ*øå‡©<ßQ®ë, s—™E[–âKÙC¢É!Z%MN‰êÃóúlCÏ‹£Qž“ýƒG£Q:Ú!ïáåæöÌÏÉÕˆÎ+Å XyÝ‚a0XRP½–$SH¤1Y“ÏÆsœn3Š.¶5hHBªrr¨±È“Èóð*—šÔøCòQÎnz£Åèd3|É$YÂæÂ˜W…úúæááAÖi‘h4Jâ [ÉéüÍÉ_*.A¥Á:ùø2ꩨ¨¨ðùèU••·ÝróW_3?vx8ü‹¿½ç¯¾îà¼wÞñgíím/ïÛoÑ«àÛŸÌpeL",ÚÏB’O?ëÌë%A–ɼ}i€}6OžT6çNáª`îœÔNÅTïã8‰WmûDVNŠí,,°Íû·ÂV ÄÙ±Ãr*ÄÊ: Ç)Û¼j{ Û¼¹ñ·ÜG›…c€ÛÅd¨7ê õÙš'N ¿øÛ¹ç¯¾ÞÔÔÈæµÅ»ÌrpHök_:j(œžæÎðþƒSÓK™ïË´5>“ #˜ `\0@#˜ ûÍI_RŽRŽ/¿÷þääéHd²âŸfWuuÕwܾwß~ó††N8†˜ííË Xw/H”1ÙÚ•ÉʘDX´Ÿl—P¨ç©OÛ»é©ógÊ𥠅3|™ÀI¾tzu _Ú*\ÝìþöZ{-ÀJL3wí?f½7¿ðâo7l¸áÆ 7° n‘¾„ÛF1)×ú¾é-VÆD±Î‰Î]šð¥3çÔõG–ø©i|†/™d©ð>ú(k…ŒætÓ==½Fê¾Ô$¶ÿtĕúz1™fZfäè`f4¾Äö¦‹~01¶tYÌØÂè`F£”b†é`Æ–t0å¶M¦&O­‰¬ë`Ê—i¤ƒI·ƒõA&ñ%ÓÁÌ^a:˜Ù!íííæOr||‰!^6úm@=Å×*v fø.»|-]¸N ³.[¾Fž © 6ÔÕÃ=Á÷µk¥‚ª©| ïƒì{‡Sß3 ¾ÔPj|™ì+^{ýMbjä V æ¯þñ×ߺïímmŽë°ùî»üþŽÝ»÷„B6”1ÿT+Ä%*,ÚOF‰E|™ÕK`øÒ•¾Lठ_:½:†/mž0¾€®ïßêÝþ³8O 3MÌ,™®}i²G÷ ¬L)o7;ši_e‘r&Òç’º‡i_2Éa:˜ÉøÜÓÁá?¤[Ôuk6^wÝ&åªPZõ\Ç—2l´ƒ/q |4—t ˜ãK ì©ÛÚ"ÿö¦úÕNª}Í$ÛÔéÑ©3c8vÙJ‘@²szjlúìxR;RûНH°±¦…máKŒa¬Ï¹&XPÀ›oÚXW[«ÑÁ$ ‹š˜ßýηdˆiW¤²vîÚ½k×nëœÍ8פ»{g0JD%3…±°³fÐ6IÓ#s0²b<ž¦Æð¥ …3|™ÀI¾tzu _Ú*Ü |)gõîÚÿQ¨?Ž2檕׮_·ŽzÕYB¿«x¤· žçi7ßôkŒQ´:«J1LH‚fË˰„ϫ۳L\dÒwDu§°êNÉ«8Q0A ü\ÍÓ:˜”n&_™dÑ&`yí†Å¤žB(6x» T)í"B!D_2½KóØÒšâSÏ)J”€€ŠÞ£ÖÇ$:°Ô AŽCÍP§×DÌ÷¥¾0Ì즃¹fxS nàK5þs_j!crð¥ÆšÞ_b E%5…ÅÕòfEÑU}-Xk¿`ÒÂxæìøìôIÀêQÇ,1œ›9y~ö”jrp_&.¥¥%ím=¦Æ?ýôOuµµº»¾ÐÙÙ½k×ðpØü,Ï=ÿ«¿ùîwZ[[©ê–Íwûý»ví¶¨ŒùØãÛÓ¤"ç‚ò˜ˆiy0 †BD%“qL[bc†/“J“Z8× œ”áK§WÇð¥­Â]Å—ào«éº¿fûO÷šCÌ£kjlª¯¯cóàÂZQÙE1Ì÷eê®9IЇi<`H¾¾¦¥º1íK&Y$ü£>ÊZ!ƒæNc7${ ÖÉuu͵uMà:¾ÔÂJ7ñ¥Ž»‹øR£ni_ª3ëãK-0‡/ '×[à­*(¬Ê/¬Ì÷VVx+ó + +ó½K½•ùùÞÊ¥ÞÊ¢Ò–òªË˪./«¼¬¬êrñåÌé n/**,,.*¢çáxÊÓ ¥PUY™œ<þ‚Qž=wîä©S”ßù—+¸êªõõõúÓgæÂÇ>lmi)--Egè&Æ -ÿå+/_·v-°1#‘H÷Î]€ßçóeãèáóùÖ¯_'ÒL§ú˜„cƒ!Ÿ¯„ã!@J”F!9R¦{bQ8NÚŽ°”)9â8„‡8)8DU…ú¢éìUÙVESg©bœî¹TÛIšSÒœ|„¼Y¹Hmé¢bÒ\ï'Ø{j-ôSÖãK`:˜Œ`2I/Á¬«m2Á—F˜Ì|‰ÑdÂøRA}ÉÄ—±]Ä— áM¾Äêãqñ%€kò—.˜tæ±±ñêêªB¯7–`ˆÙÚÚúÉ'ŸÆ˜§O÷õõÉÓÁ$ q{"bU-q(cìHT2e8ëŒc’ÖˆD"‹G%Ó,’ ìØñÌ䤙Vï¶Gº˜ö%× _2|ÉðeöâKòM‚˜³Fýxdt´ª²¢¸¸ˆ"˜R¨%©¥lÑI„çy:m…N2}L× &Ã—Ö f,,Ól§ –5ÛÉwŒ±Ú ˆ€˜ŽC¢uv [$Íœ˜¡Irˆ¤|œ\,‡¸$áËX¼¨:‡‡xDáH’‡WªGULþ*qNަ€NéŠ8KA€'#ÌX¸©Á—º$taãKF0Ád’N‚9>~äÈ[G޼##ÇÅOøxxä8x½ÅÙ…/ž˜|©1w_j[ØM|©ÔE­lk _bŒÏ ¹F0ó—Vø|'LÃòôööUWW•–”ÆLðùÊmAÌD&ÁyëEeLK:‰ CS¸¢’™ˆi¹¬’¹8¦ Áì õìÙ·Ï_2ß— _2|Éð%×َ/Éq!æÌÌÌe—-§ÞI”•-Çqò •ÆŠœ6<7²(7y¥a3q‚™•øÒæè­©0ýÍÇø‡X4m±°¨…Nj㺺Gd… õw)BE Óž1•üjÔ¨¥½j3Pg3 ˜°X}_Òõg3{aº)åS¬ùÚXN[E9ýÀJ¼J55Õ5Mš³]sí#|‰!†Á)(ÍMB šŒÍƒcYjRð%6ðqé ¾4Öou_b͵'Š/àÔÀNÝŽQW[ÓX_O¿4ÓƒoN®’ÎÍÍ¥eÏé3gÞ>øŽy¯ûóÎ-rtò¼¥yòö¼¼<8~|è탇†N˜ÒÖÖúƒû¿O×ÊÈ™”•×ý`0Ô½sWȲmuö†)7’îî²y¸3 øý›³ešŒÒ²Å_lú‡?ú±I™iŠçÎ𥠅3|™ÀI¾tzu _Ú*<åøRNß¿í×&}ú‹_謯ã’{<9òëŠÇã‘»õx<4µ\²d‰ÁË•þŠÁ4š é™~-F£d×ûGŽ9rT÷ØþàþDfÍŒÐ¾ÄØÖh#µR¢ñ¸jåªLcîŽÙޱ¢òµú+êåù7*€£ €Ê׿ª5°´Â€êÚÇÛ„w]‚‰1&þ.U“CÀLÚD]E0%?˜:c±@#‚¹h}_Ò__xáÝlU• òWÇ#[æääȋ✜zYZŸ/§—Rizlç8NÞÁLD˜fvÌÜ&:˜¶dffjltHùŒ }ôáÁ><øÑGï|ôÑ;øè?|ôŒ?ã'pAAQâøRŒÉÀ—”é*¾ÄXËp]Ä—Cwwñ%]gë:˜ºz—£&PèõÀÄ„™Nßôôô2‰sÑ:˜ä=¾¤¤xÅŠ+ MM5)„hb–—•———ÅŽév|¾òE«Œ)òGíÇéziÏž½==†ñÇ~ÿ·¿}× _2|Éð%× _€¿­êÝ£}†¯:g§ÛÚÛ¢‚rˆ D!J¨„hQŽÄ …ˆÆŠ\×'&ýê¢yŸa:˜z¨Îª梌<î‚ÇC“CÔ9‘ŒéÈm¡Ÿ/$‡ª1(SÓçB€Eº'}G²"¢õ›„-Í2ö›ßÈè^Û‡œF¤òœÊ('…ïÄ)W¯LªyŒt0Õµ2(0›È×Àt0³Y˜f¦XHŸ¦»rÅ•k±ê}_~ÅZ0ó¿é¾ÔQ·L¾Ä±ÀÔ=|I]²ûøR:µö']ÖÁ$ò­ƒÍ!fmmÍ:;AOS¤™<ÿë^<·×=ð£vt´Cb:˜²0eLƒ¤)¤sËfâi4«Gi£Ñx~~^N›+`>÷ËŸ3|Éð%× _2|¹Àð%Ioÿéþžþq£Î}ÍÕW]sÍÕãñÈ/Q99999â‹“'ÇC¿\-¥Þ‚–Pi¹‰®æb ˜ÉÓÁÌn|)+’X®¹*%qu0-BOÍ*‰Ú.€¤JûXcÚ§?Æ*Ë9ê_Sš˜êHåÊúc,HzžF—#`Áì2-6*ŠÉi`bÀ´V¦Ì €çhÉIG‚*³JûRL‹ªÆ¥ƒ©ƒ$©Ù%•cqj_a:˜Œ`2YŒÓŠø*ê+*êèµayEÏWï_jí¸]Å—X ®aš.àKmÐpwñe¬‡S|‰1ŽßeH0ê‰iƒ‚ibšLÇóÿøOÖ!¦+“Èλlñ»4y9Lºtwï CŽ—@Àïüé°¡NÁܽgïž={JK‡ý8× _2|Éð%שÀ—ä¿û·ýƤ‹û[ßd3à fvàKsUBý—³êë^‚94±åû2Ö0™LŒõ¸¡}‚ùÁ‘Èq~øQê—íåŠ+.W^ÁTk_Z"˜ä&Ä#˜ê›¨ ÊÕnPa±j_a3{ÅÚ€IReâÔðÄ©aÕ¦Ït²u®ÓÌÊíþÕº®-Ç—šI—:5×äJ_RŽ`làK“ÛD,ÀÂ(I»t¡sß¼ião½}òä)£GFF=zãôe„àÛ÷}Ó Ä|æÙŸ<ð£.[æC$öÔÝÝ»,»Çß¾ •1 } ƒÖ›B#ÁPˆDûI“/Ȥ‹ ¾ øý _ºÆÚ u…3|™ÀI¾tzu _Ú*6ê壣c55Õìå?cec¿žÎBëXÇ—É»ä>8&ÿ›WŸ~úG:QYYYUUIh¦í›Œœ´vœ{…Ýw÷鿉R‹/™dµ0ÌdN“LÓU)-«--¯Ux!`hi[ Vð¥Ót _Ê%ˆC°‹øR[BbøÒH³¾®¶AcEN9›§u0—ä*Žç'OŸæ=ž Ÿ<>29ùêko˜ßįüå—êIšÖÁäyÞ#ý*eQóÁðwt´ƒÀªqPÚn7¾ÍBUÆtÖ±’™Ñ~,Fï‰ÕÁìééÝñô3Ó¾t¡p†/8)×N¯ŽáK[…g¾$bbK^]]uÛ-·ääÐ:˜¹F:;ùK—Êé<*­ÑÙÑõ›‹ ªO2t0@EòI%þ°] ¶4î 6¯jâõ7ÑÓDø‰YÏâX LÿÑÓÁ€£G?]LŠTµ²ª²ª²òò+.¦¸"8—3Kê—ˆCrvDþ(ñ}¤à?âø€ÅÛ@  l’0c¼pꮆjêìÁ—L3{…Ìô¬“ÁLž4µ^K vŠKjŠKªÝÅ—X} wñ¥&.’[øc<9´[·ÑÌÃï½™œ€¿øÂŸ€ÇÃ{<žñ“'­CL#‚ÉóüñãC¿xîù¸÷Z†˜nL°¯¸ •1iID%S–ŒRÉtœÌO?ÛÛۛ݀áK gø2“2|éôê¾´UxæáKõ=ù³FÝýk_ùKÍ*W^ÁÒiÍŠ—&˜tŒr Á\TqÉÓN0Ó©}©s Ök 3‚é´þ* @ë“Þ(†'åÐãšäápxdd”h\¦æ¾¤¾WTT\~Åe•••´A›*D*R È9„Dï^`rí─æzJfé”-é.x,®†`h_2‚™½ÂfzÖɦ’ŽF÷î{YÎF ¿ `€µ¹E)*®.*©I‘“´}]ãÕºø^‡Äñ%U!wñe¬^êi7f$2yøý÷å->_ù7Ü@&X‡˜&“çùþþ+³££ý¡ì"Á$”15’EÑ~‚Á ù)L“Ÿ‘RÛ¾t¡p†/8)×N¯ŽáK[…g$¾$Û·ÿô€‘æ—_vÍÕW3‚™¼eTjfšÇµ‡5…!ÁL_BÂÓÓK0Ãá‘£Gš(]&éÖ¤ TTT\qÅå•••òªG”xÙ¦u‚ICI=‚)záÅó¦•ÕÐÂ7Çð‹Œ`f«0‚™žÁË.ÁÔ¡‰ÌÍÍé¦C¡ÐñãCrQäwf÷źÔÔ¯ŠrboaeAQ$ˆ/%0šb| NŸ0$˜ ‚IEò¡èÁ¡¡PV íÆÖ×ÔTËQ}>þäÓO$_0ú§«¯ÿêW¾lB0IQ}ýý¿ø¥%ˆÙõðCF/[Î&0eLÉØh?ôý2îŒ`îÙ»oïÞ}pë¾t¡p†/æL IDAT8)×N¯ŽáK[…g0¾€]û?6ñ†yï_}Ìä-£R@0Óƒ/ Y Öü]ª›Åµ¨º ù¹@j/Œê…Ì»Æ!°N0_z©Û:»t÷È"sL‚´HãÛ%˜€7%˜Ú{j`.| Àf #˜é¿R@0/]¼H/¿‰ª&IÏÏ+éw)eºèü<Œ³Ûg]*k®Äꥎ¯æ L)pbÊ8=øRÇ:^_$D0'OŸîë8}æŒn 7oÚ({¯÷xþäSù•î¾ósˆ944ôÖÛoÜpƒùyÛÛÚ~pÿ÷^Þ ¯¯ß$[OOÏö'wÈš˜.J ض-`ÕC¡ïþõ÷‰2fgç–ÎÎ-‰¨dC!B!ÝÕdŒë Ó–i_@ÀïOÍ[q: gøÒ•Š1|Éð¥~~†/³_€¿µj7èÌñ“'Ù{ïâÁOnÁ—ÄçýdAWk{äèÑ£G?H¤•6¬¾Zvž%]6&«"å+)S“#åüK ä*BR™âZŠþW}jù"Þ=>á¸%?ûìóS§&nܸÁ­;k0t²:PÖâK&Y-Œ`fŸXU1Cún˜iEtŽCœ ¨ÝqÔÏ8’ŽFéŸ|AÒæ$fHʃ|>ŸOŠú’¥ 3±0(yç”Çš¡Ãì¶™šøL³åläs͖ܼòÜ¥>ÚÁMÙ‚aîÂÄ¥óyÏÜ…ÈüÅIKJúYO¿ÿp”——¯^¹òÈòɧ¬®ªªªª”‹ZyÍÕ» \yûíƒÍMMMT]T“­½­­ýþ¶Ÿüôgq!æ“OîØ¼ùî@À}¨ÔÙ¹%ð[çt=¾}ñ(cÈ›`´Ÿî»ºwî²í'ü~Ý;ÒݽkÛ6 fŸqR@0¾t¡p†/8)×N¯ŽáK[…g¾[¥Ñ3pòä)`’m’Qø’ö}c,B¯ª0µXÓWMÅ2³ÓYQrº:wjg ÒS¸˜R,ÆÀqt0r©ùû—ºGFFì¶Rc}MS]mc]uSm•¨’Š1‚h±&`0–þ!&”4`Œþ ‚€å´¨Á*¥åÂÕ1¿ (:#ʼ¾¾„l<<€w‡Ïغãÿù»ÿÚ´i£áÇxŠC'1àu\†/™¤IøG}”µBvM¥‡(FŽÛd³q 1átÚ í©„>Ä(­™uÓãOš¶JJŠÉ§¸¸¨¸¸¨¤¸¸©±¡±¡^þ4Ô×-Y²¤¸¨¨¸¨¨¸¨°¸¨.^¼ÄzDçÏ_º™£>g†Ï ;º83<1"}&…èy‹e!Õë e• ¡í¥K—ž9sæüyÃbûV\yÇqäðšêêž^3‡òISccii©üÂdäõéºÕ«úúû''O›”™œ<üî»~‡¯¼\.zKHEßçó­_¿,BÌH$Ò½sW à—ÉþŸÏd=ÊH$â `(tèÐá`0‰D¬pLŸ¯ü^€²H$’ F'=œîÙ³wò´~ÇK>¤føÒ…¾Lठ_:½:†/mžUøRÞØÓ¯¯n)›¤ð<ÏSNrh3pÚº¶F”éhÞ‚›LÇë£ðÈÁd±²zõj‡ø#‰HÇî 3Ÿi,Í-Š‘­¸º;Lñr^„nä°.ùÍo^˜žž¶ÞJõµ+. ÜûåÎËýMuÕ%Þ‚ŒÅ—XˆŠ1®/Ê«/Ì»¾®¸Þ»¤p ž¾hýŽ ¯¨¨(((=+cHV9¡Ök&~HcÇdnE{Kâ#Ù/?þD?›·  ¸¸˜nUyQl6žçè‡ÀÕ¸þ ÇsfEÎ&#˜YC0­\{^ÞÂ7‹‹ Ä,ªª¨h¬¯o¨«k¨W>"z+*Ä-Y’·dIÞÅ‹YÏq  &477š»@?ÙÑÞ&^XXØÔØøy0hrÈÇŸ|ÒÒÒTVZJMúïëk×®íéí5‡˜pø°1]$˜D@ àLD,:Âãú\$Ö¯_g«•bùc0$ªsšsLŸÏg¢õi×Ühh}áÅß’ä> _ºP8× œ”áK§WÇð¥­Â³_†úOÌh4ZVZ*¿?G£Q’F ’ðOTÈÇãñÈÛ=ïÄh•ëÀå÷âYÙ%˜Ùf<®Òh˜f«¶È‚oD›I8RÝ»HlŒ¨âÆÅ&~Ñht8~é¥nU™ÒGW6¬½îƵ«n\»º©¾cA¤ŠÙ€/éC sùzoîõ5… <;‡¬ÉñãÇÇÇÇkjkÈh@†!ŠG£Ñè|T0° ‚  ¢Ö.ÆXFBTØ$ÀÔ‚ :®áл}Ah_2‚É&F0Ý$˜6¸[QQqQÑŹù¼¼¼¼¼¼bµ\{ÍU—]¶Ì[XXSS][S]#}FM#Ì0‚iB0yž///5;;[S]]TT$o)++%/”&835uí5WÇ%˜ϯYs-ˆé:Á¦Œi§•àsµœpÌ`0äó•›´žnùÁPÈ.^4Z÷8Cèܲ9™ÞN¾dø’áK†/¾ÌP|IäÝt¬¨ðUTøbW¼´~%­ƒi¤iôFĦ[3 }_ªf5Rÿþþþ’’R»µµM0ս޾dM¯“[‡»»wZ¬Ø†ukîýÊ›êkJŠ ãìÅ—˜ªUƒ7wMU„gç¬4Âùóç#‘ƦF£_/8¤ÄêáxNî ´-¿F_;!XfíÇ^ÏÎ8ãqF0³W˜ÌÌU—µHN“¦G.Ìóòñõ¬óêbçç9ÝçMcÏ"Ÿ%ÒÏÆú¶-Õ…86Þ¶Þ`dPÏÊ;ÆP]]]]]-Í£b容æj ³ 8vìCÕRFGGGGÇOo£ß’4³ ª_UVT”——E"†6÷¾¼ÿ¯¿ó-¥lŽ[³æºðÈH8l1?ÿ«úηï³RÙ?𣧟yÖÄ5!‘íÛŸÚÚõpòÜ2ϘÖ*©Ñ~:;·©aºϧ§§'-k´tÎð¥+cø’áKýü _.|éo«b«•¬–ôãKƒ'7&2§¾¿Ëùhžþš››¿õ­û”CdmõÚ‰¾jz±Æsúqh²‚8•V-äÇÎ3¶ˆ/›êîùê—ËPràKéâ5•k*òß;9ûÞÉsq[#‰¼sðІo°:¯;Ãþ0ß—L2@˜fÖ̱ôãj[“:„ÖÁ¤u3`~~^÷p‹:˜ª™LpŽÈ™©i£]55ÕÆã˜òNPSS][SSCéiú;:V^{íÊk¯Y¹òš•×^³òÚkkjj ½555555µ55µµµµµ5£££  •”p&464LD"&1GGÇüþ’öääÀòåËÂá0ñ€£Ïœ¼öš«9c¯OòKÕš5×ùþ÷Þ{ßüº~×ï÷û|:>1iqüSÆ´ÛV€ßW^ž J¦\ ²=Ò5WLDlÙïë­ï½÷¾‘/×ÎÎÍɹ• _ºP8× œ”áK§WÇð¥­Â³_Ù}@?ù©‰‰Ë–/¦ƒ™”%‘;:˜™¬}iR¦²òŒ1&ø’¼E^#Ù3uqúªU¯ý1<•žF§Ñ]ý"­ò)û¾ ÿþ÷ÝV.þÞ¯}yú5 _Š‘Í¥øæõùžë*–†gç¦çó69wîÜÄ©‰¦¦FbïÏ™è`by¸°¡ƒIFH̓eR"}=cñ%ÓÁd“ #˜é$˜pA/ÎÏ… ZZšôGaPâäQ#,ÀÚŸ£¤K,òÖÖÔÔÖÖÔ†Y[SSS³råµä³jÕJ’€ÚÚú333 ‰`@þÒüÆAäÉõÖÖÔÈ“@ÌёѳgÏš@Lhkm‰K0 Âçëèh·1ƒ¡Ðºuk“A0‰0Ϙ¶8f2¢ý˜Äó‰Ë‹(y8íéíí5 ˜ß–ô¾dø’áK†/¾\løRœ# \a¶·µj|_"„!Ù÷eNNŽìû2''GvZ§ñƒiD-^cÁ´B03Üx<.Á$=¼¿¿ŸàKù-:bbã¦s@0ÉOt§HHœb"@Ñh4¿ôR|íËæÆú¾÷íâ¢BaŒ$Ÿp&ãK ¼¥!ŒˆŒ@@€Y_ªñ¥d~ˆ—çÖåóŸOÍÅ…˜óóóÅÅE‚ Ñùèü|t^À‚€qTô%žFeO»ò‚Hî1ºJ)+x¤6厇§}É&#˜LÁL'Á¼pñâƒHåÍÍMô0€MÇ7 gØXq#¬Þ/%4Jšµµ5€ÕÊkW­¼¢óµÕUµÕU ÓÓ3yK–xI ¢Ô‡!**,¬¨¨(..&¾D—.ͳG0óóÀÄ–|tl¬¦¦º°°ÐCãeeeüãŸLj588ØÚÚRVV—`rW^^nbF"“b&oz`ʘvÅÝh?ëׯ32$÷•—[7$·K0“ƇáK†/¾dø’áËlÁ—8Ô?nD0þPë]ò<ÏS:˜K–,QV¿L3…3ó}_šL_>÷Ü?höˆyõÕWë>º&o¿– ¦A,))æ8)j8<Üݽ+î•ßûµ/߸þz,cDŒ1ˆhG£™Œ/•ü‚ œT©Pº™Z|I2yPÝR~zN˜ž7ëF“““eeeùrkÓcÆX½FãtÓ1°L!“–VF-HãqF0Ád’6‚)G*D Ñ(™[ù±—0Ƙ„}£ƒ’ñV9œ:$ª:\I«Ÿ4$§É.嬿N#Äé&UikráâÅ .ÌF³†5Ì•4±vý#1MP¦ž¦Ïž€é™é™é鯷¸¤$O}¨µ¹¡ºª²ºª²¦ºÊë-ÈÍÍõz ¼Þ‚ÂBo¡× —.ÍYï8…^oEyyqqQQQaQQáòe~9’{e…¯¤¸¸´¤¸¤¸(/o ™å&åy^N{<ÝtuUu$™=gèÏefz&ðçxr䨃åee Ÿþñ&u>vìÃÖÖVšqýR¥!˜`bö¬_·6©ÓSÆtF~]‰öc²×:gL7Ádø’áK†/¾dø2kð%˜†#_3y¶S®œZ£KA&î‘°!Á\µj•käÑ¥Rt¯Wu]€ésE£ÆÐß?ðÜsÏë–xæÌ™ÁÁ+®¼RTÀ%*`òGù`,Òh…=L‹òU§ $½KäݽúêëÓÓ3æË¬{¿ö妆úʼn/Eˆ™Ã-/ò„ÏÏ›CÌp8\^^^P¯H½ìLJ[%˜{v¦û¾d“L&îMåThLšÎ¦‹ iìHSKÁ)Û:#Ä‘€ÊÛ¥z’¯â£HžF9M¾Êi„è4¢Óò7\r¾äÔ_õEÉtáÂÅó.è6iSS#6…•ƒŠ¤*iNOOG333333/\(Ž ˆäæäkInn¡×+~ ½…^oyYYMuý©­©&¤²Ðë-*,¬­©nmnª«©©¯­­«­©ð•ÊSj8NaÅR “Z!•Áä=4ÍÌQnǵµµ~j¬S93332:zÅå—Ó´¸¸¸!ÄüàØ±Ö¶Ö²²2q¬ù§Š«Êczyyù²€ÿð»ïŃ˜‘`0¤ 1]œ˜2¦cøK"#9VÉŒ[~ÆL†/¾dø’áK†/³ _.‚™á¢™µÉב‘83ñ¥Y©T·ÆGAö}i$g¦¦_~Å•ÁŒ’• 1×@2ÁDÁ”ƒÿÊiÂY°dXýûßï5]{Á=_ûróâÆ—2¨^îåÃçãhbž?¾¡±äžo‹`Ê«~ëÓI´Ÿ, ÝÃfö Çš€É‚»Qÿ’ˆMft³<‚<½‹_¥Ùä¯t>iNLÇJ\jkªÉ§®¦¦¨°0Å |ó¦&{GGdž‡Ãš Fê²¼òÊ«Öëà÷û»~ÐßÑaž-$…´N¶tvnÙöHWÀo5 úcoì±'ØÃ¶mۺ푮Î-›],6 Y-ôÆ€Th 0|éBá _&pR†/^×¶ _XøÒ\„x‚é¶òB š¯LÒ¶²HǼOnz\|Idhhè·/¾˜Pí°òPa}SwÕ’ˆtäáá°>–垯.víK¹ùȉ¾X“[—gª"‘È¡wÏS"û¾$Î1É'äjtB²í[ƒFwPÕ´‘…¾Ã"3I®0‚Éd¡¿fˆc5€€Å!ˆ3L)åLöJ§RoP²x½^ÀúŠ›Ù+••••&~÷_ÿ»qýºuMMfþýý?ÿÅ/­WÃï÷wu=dbZGZ Â8ë$. }÷¯¿ Ù3Kô1ŸûåÏ;·l¶NÍÛÖAæp™È𥠅3|™ÀI¾tzu _Ú*|ááKÓå}¦¿¿ÿ:)p$ĨâÓKk×^ õD&'Í_>ðû}¾r“¶u Æ1Ϙ‰´^‚Ñ~d±ÏG׊¼··¯··O7¿Vä _2|Éð%× _f'¾4µ"÷w´ƒÖêÐã1²"§Ó+répgVäÉãžÉ&ª&{t¿Ê~0Ã##ápr¬ÈÝ÷}©hj2(¾ˆÔ úû~ùËçlijjjèøÐ²eËdg˜Q!ª†\¢Ði  »º>JÁc¼sç.â,‹^©Ð_$ß—„H €°€£Å/‰åÞò.|OG ïæèÈh[[+íYB³2òðýât –M†Ãq£òì—̊<{…é`f®hýýQ ©÷)+ÜúpÆÂÓ^óÑN³¹89FWLoÙÌ[PMV”*¥‘’&ýû¹«s‹Jš^o\Œqß97íš ¤¶Ú…½±¹8åC}1î'ˆãP$yûà;o¾}ðµ×ß [n¿õ“·‡Ãaí!ôÍoÜk®‰Ù××ÿòþº`$]]ùýq³=þD*41)cºÔ€D%Óq!&¡~Ò* _2|Éð%× _f+¾d’þITWG›ŽØÎ?‚b\íô£ø†ãëâb,¸¯¯ß.¾$râĉû×#fÆsóóósÔ‡¶@V›(“Ïœ¨ï§Ø&GA¥î§(ùápxDv©+÷hñ%!ƒ _˜ _ôqæ½½Ïàuw©¡CΘeÚ—l g“ “ ~¹¡ yŒuç±Öò›šæž4;ž4å*x³´IÿôÙço¾}ðÔÄùzàÕ× ººêš«¯29ê?~÷_'†‡c·o¼ñFóÓ½üòþ^›öÖ®‡3 bóŒéR>÷ËŸÛjFZº»w::’¶ReøÒ…¾Lठ_:½:†/m¾Àñ¥á³£ãøReC.Xrƒ©Rs+ÞM˜¯ÌŒ²ÜWnc8þÏßýÎ99Â1ޱ)4÷ÁÇLJÛ°nMSC_r'`áK±I _(7»}}ý‘¹¹yùC[‘«}bJÛˆWLƒ¡&ÖŒyòÔÇŸ|Êq\mmÍÝwÝaÒó¾shèİäkS‘ÖÖÖ[n¾Éü©yùåýF^_ñûý[»Žû<¦Ød›)c&(Á`°»{çwÿú{vÕ*ã†ÈL‰0|Éð%× _2|¹ðåBl_2¡ª çÊÅi6øRã$@ùÄø¾Œ gßÜÜto<»% ‡Ã/½ô¥À§rŠ)m››Ÿ§‘io™QÚ+¦\«p8lÓú†µkëj°ÅÑ(0¢˜xTˆFAºT‰! ‹YûR©Æ« ×ßߟ„×ÇÅyœi_2‚É„IfÈÌì¬É@ "Í™¦¹_m©(pìISö\]PŸ]í¼á†õ&{}(þ&\[SSS]m’óÅßþ³îö[n¹¹¥¥ÙäÀÞ¾¾O?kZ‚˜ÁP(Å~'™gLÒݽó±ÇžxìñíLÂý~¿?à·î>,9«4†/](œáËNÊð¥Ó«cøÒVá _‚P-€uÜ Æ¼§Æ]ð3ß—.Ï©ÉÔ¾Œ÷*b¸·©)!ˆ96:¶{÷nÓuŽy,$Z'CA8fîsíj#JøÆ—aP\d.v| ×åàÕùQ£~púôi½Ló´-¹` ”=¹@ÔiåÑð;Ë-’Œ'ˆáK&q…L&Y/3³çl -¯tKIS3k*iÊa¶É²@Àdï®Ý{IâÏ;ãh¾ðâou·ÿõw¿ÓÒÒlr`OoïB‚˜LÓê…SJ—¶ÌƉtnÙüÈÖ.®3]_ ¥³p†/]©× _êçgø’áKÃÛhxÚmÜÆÖçî¶gznŠ•C’1-Õ–^0°0:2266f”ýëùE#J˜OD– _áK²kÕÒ¨É}èïïÆÁ—©Tؤ6¢˜z’ð%˜(ÒêlIx‚¾dbEÁd’Å23;;vòÔøÉSFjkktA¤føvKI“Šòf¨¤YmªŸ˜á²|Y ¢¢ÂhïèØØˆäÆû‹ÞiRÎÐЉãLJtwY2ðô3 IDAT˜{öî³[ó@À¿uk&BL`ʘñ¤»{§3¥Ë€ßß¹eós¿üygç–@ÀŸîë`øÒ…¾Lठ_:½:†/mÎð%“ì—LÆ—D܇˜N¯Ù_6Ö×6Õ×(P°¢ŒÉð¥¾$g\½tÞ¨mu—QNºœ3ãqœ”'ˆáK&ÅÚ yB»çKð§Q•§?uQFñL8úìÔv ”{^HP²ywèáD_ÎG¥É´¤ÔDñM)Q*-Ðm‚1Ý>*·ÐºM'xdt ¦§g¦gfâ¶Ç!õ)€„2’Ï †°qóÆä®·e/½Rô©©Ì©3§‹KJÕ÷TÇk²Æe»œ@ÕÕx%íá•']íÂYqíÌqOBy†öx”»yõŠ+¼úšQc}øáxžoll¬«« ‡ÃF9_üí?ÿÿ÷ÿ¡»«Üco¿íÖŸÿâ9“[²gホööÀ²€þb̳ÙÚÕ½sWÈT‰@ÌmÛ¶¦r¬Û¶º»wZätDsÛ#]S­Ø…!€vÚ<ÄïïìÜœIÃ𥠅3|™ÀI¾tzu _Ú*œáËTƒ6K±n¬nå«yZ©†½ˆöD¥4±27?ï"Ft6`0lvù}]µò€ù9ñêjë¾ôå/ýî?~笂c£cÝ/ußzÛ­ä+T`>ª,Üx*–”ÇÃó³‚/ëëjâä˜1b4¬ƒ‰r¥F•ÒE%MiêŒ!?_ßæì¹ {Gªª*«*+öŽŒŽ=úIù/¾XWWgRÔó¿úGÝímmm÷ßÿ=ójìxæÙP¨Ç ÛöH—?ž¶#˜©7Öfʘw͈$J—Û¶meøÒÎɾ´õÂÎð%ÄÃ^v døÒVgcø2£ð%[/(I6¾4™dœ¹¾¾þK_þ’ãjžÑ†x||Ü$ÛºëVÒ:•rÀqLi_bLûÇTáK¼¸ñ%Ù¸jÉœQóNMM%Òoy²`ø’I&#˜L2BΞ>1ú,ôÞ‘cä Œ…GÆ”Y\\,Æá9öµU›r+±PLüp«ÁmyÒ¤ Øó ²ñfÝvëÍ&{Rþ¼¯¿î:“œƒƒÇ_{ý Ý]í æS;žv1À*Ä||{ê!&óŒ©+q$à÷o{¤‹Œ[ziK]4U†/](œáËNÊð¥Ó«cøÒVá _Ì51”ÊJTŸŒù½%øÒÑy0$ b*ñ^´¦g øXÄx|Ì`jð¥b¾Ä¾¾4—€q­ÇÐæñãC/]ºtñÒÅ‹/]ºtéÒ¥¹Ksòç%óóórùh4ÎÏÏG£Q /¤I©ÚPß@wì~Ofø’‰Ma“IêäÌÔÔЉaùóÎá÷½+~þøÙçÃáò9;=ø¹Š‹ŠJJŠE@Éqq²ö¥Â4—%MiTÅÙ;¶®¸ò “½¿I4ú­¯¯[c 1_ãÍAÝ]ímm·ß~›y5vïÙã¬þV &¤bSÆÔkÃ]™¨t™š—'†/Ÿ”áK§….9¾´û¾dÚ—L2pÂÎB|)½l§NS36~òÉ'F9×­¾VbB4ª ã#A=Ñ-¦>¾Ä‹_@oÏçìÙ³YâäŒ]Ù…/1À³Y˜ÌÌ#×~X½ö}IûIá(‡&€/Š@ÐKÎÁqlŽòwåÏ,„áE&OONž púô™Ó§Ï¤¥õV¬¸\ô{‰Äá‘x«”|P"ŒF¤0&$1Öæ§Gd=Oš–îdŒ'MŒâ®Ç—ªþ€T~0|_ÒmèíÇÉ^Jiß—D=U<Êà““£Ù~í5WŸõt×Ã:è!Ûézìñí¡x­{|{ZÜM2Ϙéܲ™nЀ߸­h\¦q5”ξt¥b _2|©ŸŸáK†/Ó¶ú•ð ¶èS“^)h|Ó[,ÁeägmùÁ”ýEFçç…¨>÷¹xñRÚ_èf7šäˆž£æºˆ\šSÌŠç¤tUUÕwݵ×éïú'OžÜ·w߯MÉ×ù¹yL­&<Ò‚‚çyžçÉSéá=ÇÍK~EéUF¨¾¦znnNT´¢Øq"5FÄ-¦ ÿ˜ ”ÁŸèskãKÀ¼*çâѹ%F³²²"ÞN¾d’Ja:˜LìÉgŸ?†È'êÙ½çeòÙ³ïÀÞ—_yÿȽ}ý½}ýýýƒýýƒé—W­¸G‚ÈJ˜j+r¢—)§c•49”L%M!û»¿úª&{¿û®œþ«¯ÿ7“œƒƒÏ?ÿ+£½üðíím&‡÷ôôlr‡³KÈpML`ʘ”ÈÁÄ;·lÞöH×¶m[¾Lìä _Úzgøâa/»2|i«³1|Éð%&fRSSsç]w9>üÔ©So¾ñ¦­C>ýôS£]õµÕ"–“ð¥h-.ÂÄ(жäêhãÔG`øc\ˆŸ:{ö¬½7†äüìÀð%“ ÓÁd¢’?g£¨€“'O:5‘Eõ/))¾úªÒØ„"iŒ0„0`J1Ó‘’¦8ý)iZZq–¿WWWUWU©a‡~wíÚëÉׯýk¿ýí¿508800hD*üÀž~æÙÞÞ>£Ã{zzvîÚ½eóÝ®bÛ#]Á`èñ'¶›gK—&&0eLª:·lÎlj™šÅ-׎OÊð¥ÓB — _Æmg†/4¾´ðÖ§Öa´œ gð…@LÇš˜bÊš˜ª> Fœ¤üd^“úš*Œ1ŽFñ"¾à`!*B:Šå8(nbH¼à¿Å/ãZÎÐ|úìô¥“Ëó ØñäP‘µ¬X2R4l(ƒ?óºï`FV¡–GAG#×LŒ…ÌE'#£cápX|;ð‡ýa\TIIIiiqiIIII± ŸÀ0"‰ƒ4@æ’4ÐÇXß1ÆÒFŽF@Ã4Õ@S3P"\Ÿ?;;+úSgN—”fc³ß}×ÏÿꟌö~÷=™`656666˜Ø’ÿò¹çÿîÿ­ÑÞ;ï¸ã™gbR“]»vûýÖuÕhÌO,Êͳ=öøö€ß¿mÛÖ´4ugç–@Àßݽ+Ïì=j›¼FÈŽõE _èørzzfzfÖÑI±ÍJ¥êªF+ùôÆó.\¸pÑd¥RRRd•Å­$N¤æº( Û¬ŒRn—Bê‹-´–ø¥Ð›WèÍ‹sv†/³_2aâê@ü.ç ļõÖ[­d6q‚YWS…£QÂä€ò})í@€hd tôB6Šf.^|I¾®ò\8:Ÿ—Pçq8^Å‚ _2I‡0‚™YBÿÊaôxºGTûÄ<|ø=9ýî{ï/Èæjkm€––fZRR¼”™$ Q+“V®›;uJšÞ‚™Ù™¸=Au£Õ¾/Õi}ß—tÚãÉ¡;‰®ïKŽãèChß—FižçW¯ZyäèFWñïÿñ»¯í«ÄÎ7ï½çüÏÿerÉ?ÿÅs÷ÿoHMŸ˜‡òû;ˆ&¦ÉáÛ·?ÕÕõ˜tÓ™ÿrH$X˜ÁPè±ÇžHdʘٱ¾Hcá _ÊÈrdt¾Ä‹_@ š3ºS‚ ÌÏÍ“O4ª|È–¹9²o^Ù#‚ädTÀÊ ,`é#(IŒ½±ÍÉÈÆð%·„é`f· ø—ý÷ì­ÿ²€_Y|ååååeôð&'1‰ü-§É>IÍR´qÂòŠ–âÅ–”4!%Jš’# -©««­­­1QÃ|ó­·7Þ¸|½iÓÆÁÁÁÁãF¥í?ðJGG»Ñ޻ﺳ§§§§§×(C(ÔóÄö'ÙÚåìZÄŒ«á˜vML`ʘ™% _&ZøÙ陑‘qÉÇ%NC›3|ÉðeüÌi×tzfö"¡™5UŵU%{×j»@´/™0qE"‘w¼q£¾æÁ©S§Œ–ºê ‰ßa_ sÒC¤x×RGòÔ¾/UøÃbÅ—Øéo!ɶ¤}i¿T†/™¸&ü£>ÊZ!{e÷î}SgÏfr +|¾¦¦ÆŠŠòŠ _E…oùòe+W^³|y`ù2ÿò@ Âç«ðù|¾ò Ÿ¯ ?Ÿx{D&©J Ѥ˜ˆIƒ¤ì¨¤INSŒºlú?M‘1Û¨ŒHÊÔF ŽSUK.!cãã$uîܹsçÎ@l$oA¾Æ™#â(;xžJs¼’öx”Ü'g£UIyž—Ó¡œœN'‰W«Jó<Ïó4´iÓF¹&e¥¥~ø‘QæÓ§Osut´ÓWH·Ãõׯéé陜œ4|åŠLC¡uëÖʇÄí¥tŸÏ·~ýº`0‰DÌ^ì"‘`0äó•û|¾t=\¤ª`bF"‘î»ëœáB;“ÝKõöõõõõéæïìÜÂðe‚…Ÿž<12zòÒ¥9†/3_^¸pÑ0’(‘|¾L¾¤…pL Âþ0|™j|ê?ÕÓ?¡{$ñÕNޔȇ÷xÝ‘%yÊ[²Dq››+§‰rÙ`Sã]×&™ËB!³Ÿ{™0±"çÏŸLL4Ô7‚@^0’ž˜˜ˆD"XÒ j»eí*ŒE½> ‚ XQ!Äè=ˆ&˜XL+“@FÐrÃEŠ/1þ@È×½GEE…EEâ¤LÇ* ‡ŽãxN…8^ {@¬*Äóò(Çqô⋳²€Ê:|iŠÊ[PP\\L·'µ=žÓiÈ5A7—Üà±ãy"-¼Ø„é`¦_¬Dï1êÁC'N¤½þW\q9]÷ÊJ_EE…òS¶é„¤‰EíF XŒ‹#*<*!wD-HÂi¿år;W•4éÓ)Þ3L•4‘ùÜ@IIõ‡)Pb¥«^aU·›¾áÚH>5ê‡rRÏIœz Ö‰Þ£‰äc1-½~Íu&£~ýëî»ïä,­­­ßùηžþWF™÷½¼¿½£½£½]i,õSððC>ùÔsMÌ;wm1µ³6Ÿ¶mÛúØcOÄ×Ä||{ÚCå0eÌ´ ×ΠWë]&ˆ±¾LK7fø2møR–Ññ)I³˜áËãËŒ›èmfÀÎÄ 9uêÔ[o½µnÝ:²Á< BT0 ø$Dç%*'D9DH0 Ô‰ÅÈ$Þ) øƒT2£DZ‚¹Hñ%`\—F!7ö…Ã#uuu¶‰¡£×!1d.r8.e¾d’ÕÂ&“8RUUYUUEo¹òÊ˱l<•ÿ4ôȯq2(Rh£ôóšÆødãk‰R¦Û Ç‘ &¬’‚›J¼q±`Â)é¸â@¨¤”ŠRaÉe–Фh<d~•¥&Ü9Å^õËff¦9„0’¬Èóò–.€~²níÚpxäÄð°îÞÁãÇ›››É×Ö––––æA£Òž}öïw_ùúÖ?ƒ*ŠAaJ¸öÚk¬ 7âð ˆL4KlÄIjŒ23”RXÊ+âF„°”Ï úLIDIS¥5)ƒE)6™’&!‘Jšä÷@»Jš"Ç4UÒœžžCˆ#sÁìì9“¡õƒ)½,Påë&RNY”ÓVäÄ÷¥œ¦-Äe— ¶7)ª¹¹©±±ahHß³Áàñão¼ùÖí·Ý*oùþ÷þæg?ÿ…Ñå÷ööõööùýF ×ï÷w=üÐö'Ÿ2iÃÇŸxò‘­]‹D˜2æ‘…Œ/Ã#ãzAÆ-a¬ŠÊ2’µ¢²4ö%UP½ßc•VÀùÙ çÏ] Cþé‰ih]V«žé̈yû§Cô5€ÓËq'µZPRXo9 ÑzPVÔ¡*0¤‚+cÃÅ} Ú6‰ivñëÅ¥ü¥¥9sÓѹ-Üin¬€U+–‰îÉè•R±RCqH7Œd ¨ª#V]#¦ò o±ˆUyT ½.¡è²a½æ´åkk!uøØì¥ñÙù‚ΛÑöøö¬€˜À”1¾ÌT|©g9§Àü‚¥K+ªJuŸE| K –äåç’¥¾"/2|©_a³Êð¥¾Ä€«òsªò=1˜Ò¾¯¼` @hž¼hãY›™½ê;ÙÑZ!Eøaø2iø2“ÜMj&39g⺴¶µÉAÁÐf4AT&y,a80œ¸pã,HÖã#ITcÀ `i—øà ¾´Â prâ•3|É$…cM°P%''§¨¨°°ÐK#sÆ^é(ÖÇÍ;^üñÇ!q<‰h†8ž#A­9$åERPk^N‰y9Ä!)8éœ%›SfS¥¨Bh#r,BˆC‡xBtÑš“ ê̈Ú($¦å§‰ØÍ!Äq %IJ9ù¨Ó)§@³3³Ç>8633#:¿D€(@ˆ<77'{;Þ†Ö›ìýÅ/Ÿ§¿Þÿ÷ÚZ[MòïxúóÓmÙ|·ßßa’! Å…q%l{$>}ìñí=öD&Ü…@ °mÛÖNÓXF´eÌ`0ÈFNÍj0µëÀ‹/ÏNÏùàc[ø²¢ªü²+;š[ëÇ—òÍŒ¥u _ê5ŽwC¾ÔÁ—{öñ¥¶I¼¨ZWAK>.à±¥gôôŸŸbø2¹ø’­–™,Y¹reYi)5¡w~ _‚œQæ† ”¤Ü\‚šQ *˜:ør‘i_JÛ[ ÷Ž­¨f.£×LÁd’Br±, Ž©Ë4E(‰D ÉóÇ!^dz<Çñ2ÓÙ ûd(ÈS›(Ĉ´@SJ«–&¥RýO3M¤€E)-‘O5ÇDÊùTSJK5–8&’NÁ+L“$fgf{B¡žž‡€£`0_=pÆŒöö ô÷Ð[n¿ýV“ÒzzzwïÙk~Æ­]gÄ †B1 ³s˶Gº~«Fô™C`'2]ÀøRÏñ¥~K+ªÊ/_ᯨ,£9שÁ—¦ËR½¾L&¾¤ËñòК[– •¹‚Eò8:~6ÔwŠáK†/™0q_–•Yžüuð%¦ñ%(ˆSv—I!EŠ'êâKyjXTøÒ„ã¸øR©¡›‚H†/™0‚ÉÄm9>4d1g28f,Ð4SÒäx¤M­’&¥gi¬¤‰²OI3 †BÁ™™i„@ªø97;;>>z ˜°$7dMMéCéoÒM®þg(¼Ñ‡ÕôvúJ6mÚhÒa^yõ5$µ$B¨½½ývÊ´+øö‘Gþ`Úòµ¯~EPþàÁ7ïþþ¿:±Å¶m[ï—ÙyW·_Ó‘† b‚1&àËpâËÊÊXë™Í «j‰/¾Ì¾$šÉ¹†€/ó†/éJ¥JZcK Ѥ_Ò2ðe‘âK(þù5Ë—œM| ƒu×e&°¸2fØ%Ëñ%|ißó.eÓzy‰q±?"àKLG=µg¯1Ç'&xÅ¢Ñh,[¹rE,V´I¦€iú`¤©°F𨳑f&´¥ÀH‹Œ4 >ÿì³CÇŽ)4Î¥ž4<½ ½¸oeÇüª IDATßìì,B¨¼¢¢eÝ™6¦²ƇÕ5×|Hðíþý'n!æÀÀÁAÌ®m’3TŒ1_æ,ó¸,¾<«¥²*ø24øR‚(¾ ¾Ô÷m(ÓZËŸµÙ“óC#Ó€/_‚@.ðeMÍ«˜sDdÄ—¦—D3n·Ã—ð¥Ž/‰u#üÜ<™«Hxd’uÙw=ô#€/A@0A^511Ñó@ïøø8Bhbâøá#GÅåc±XŽM2@3k#M5+#MUu0ÒTlŒ4ggfÿôÆçžyvðØQݻ٩rhpð…^¢ì²áŒÆUg4ÙvEYY´¼¬¬8nÅ?»æC"_ò·ß~óÍ·Ø-6¬_¿þ,A…ßÿ×ÿ+ÁÛ:Ú7‰Ëd“JbîìÄcLÀ—9À—ê˱ÂÖ3›[ÏjÉ`À—…/ àËðàKZ[¥JΫ\0x”Û]»¡Ñ™™ÙyÀ—€/A ïøR騙-ýˆ kj\|Iøø•¾ôååã|½¿ ¤VÀ— "С’)ºŸø—ãW_õÇ?>e[~ïSO'm‰³§OŸ>|äh]íÊx\ä›F©UæâââÒÒÒéÓs!éºBIoУ•c‚Qú;„Ó¿g!Lpjœé:œž­áÔ;cLÁ¡ôèábv&„d‹õ‰Ý9rdzjjzz:ÝÎT Ú.úæžž9zìØÌÌ ­fyÍ ÛÀ—ºê2™þ°¢°¢(©¶P¸Ê¬«™çYÍ<Ñ‘HÔvƬ¯Ó[B_/c j”15µDß]ß~ÝŸ_ûÓŸýïdw?òH"Ñ–9AŒ¿þW_ûê_}]Ð?ÝwßÑʲ££½ààÀ×Ð’¦&ÿ›®m¾@Ì;îêp0êܱ³»kûÖD"žWMggG"ÑÖÓÓçØxýmm]~t¨ðåÌìIG|YY|N|IÇþ€/Cƒ/õ•ÖŠÅwæ¢'5Eø¾3¶¡µ¾ºªð¥Oø’äíÇÃh+¥gP¡*ÍS&Ÿ¯}åKt…†1LuŸ¦é¯>b2àb¤`vÈÊÚ¦9œ)óMÅl§Á3M%BèOýâxÛtttø|9²#"Ѹղ¿jÌWÉä’¾}i)©o_ZÌØb?ýôÓÇ÷ܪóÎ?oYõ2-ÉÜt2…1ýÈö¶^†YOê¿KZRÓ‘+œÂva‚pšÙa¢a&)¹’¡†H0 TZø’d“@îdÀ— |l0 [·Ýz ï«þþêQ~úôi±G¹ eRïòüZeZ¦ÕH3ådîÚHS±ib7Fš³33^ãÙ§Ÿ×—tˆL$Ѹh„Á—&þÈ|d褳Tð¥l¶mÀ— ˜ ÜiíÚ–O}j3ï[«G9/½eæ+V¦ÓTL‘4Ó@“ƨ´¤©X#i*‘4g¦gŽ9òÌÞ½o¼þúôôt*º%“P[ÁXAHAhfzúo¼qàÀôÌ J‡¼³KŠ/‹ÆœÕÿùâçß>d!˜6¬¿áú véîþ¾/³` §§·Ä!&‚Ș€/ý®Ü.ü%àË—N®²€/Ê/éÿÖ•/ÄMð b¾| |iÅ—ZV¨È¾Lýt;áKö#B¥‡/E"™ÔèbÖ-u2¯´LBxB<ÜÉ™Ká0Z| ‚ âAÌÄDí}êiÖÓ12¦U4V¦n˜6š‰,©²1ÒÔÃfR¦933}ôðá½{ö¼þÚ«GaŽ’9$J¥ÇGŽ{í¯¿q`z:Å.!/u••E›Î¨/J|IuÝu.øö?ü¦-7Üpý† ë»ôõí’bsNÖ…=½}~!Å®®m]Û·(ÄcLÀ—~UnþðeaáKçIàËÐâKºÚZ67Xbš/k* &àKÀ— À—V|™*âáKã¢éP’‚fù–…}ÆP˜€/}½Q½ÞÉy”5Äü»¿íjiiæ Æ˜ý꧳ࠤS¹IÔ0“ÒLj›OsÓt2ÒL^êFš´Ð‘ÇŽ>¼÷|ý•W9œNуí¨)V|ôèѽ{Ÿ>rä( ‹©³KÛlã¬jW.o¨[©(CÐÔÂ|PEUÔÌÂ(a–hD_¢‚¥,ê}(}p“®ÿȇÏ:ëL^¼ùæ[o½õ6{¶Š¢´oeã8ØÛ·‹M—ÄsmÛ·mÍRL$’3œ6Œ`Œ ø2ËÊíüÇ_(¾”A™€/È/éÿ[ËÒÓþRN¾ô_æ9½OÆÞÊ(=p¡+-,.êËâÂBfa6/-.鋦›~%“šÆ.„5òÒeÓÃÄ¥„/Qøñ%K343“®ÇBµ4³³‹<|‰J _zÊ$æŒ/ó9š| ‚ BèÖOmin^#(@#cêNåž9¦®X,¦{šSóÌpZh"‘¦ª( ¦FšÓÓ3‡~íå—÷<ùđÇ>Œ„pz_„Ó2Yp†>¼gÏS‡¡Z^³B’]½é%«|ø:Á·¿ð!Ó–¶¶ w~ý¯»ôõíêïwö%'Äì÷/ §¿cLÀ—Ù”·ø TVÆ_¾|™|I¿mP—röäBb¾Ì_‚Jï·<”ørÏž=aÁ—ô·Óï2øOÓÝÆûJk¦r„!àK½×ï› nTøä«>uËæ[¶|RP`bb‚:•ûÈ1©¨y&k¡©3ÍpÚiR959uøÐáWö¿üäc¿ºÿ‘wMMM3€’á• Æ)qŒ1žšš:|èðž={XvIã]ʰËÚ•ËëV֔ιþ¬³„f˜oÚBÌ 6꼫ûn9*×ÖáèNÞÓç',dˆ‰À3˜Ÿ¾PãËcƒ#‚••±Ö³Z_¾”¸Y_";ª"|‰ªT’ë¢s‚ 94:øð%(T(;³gÏž‰ñqÏ›ñeÖçèˆ/Ƙ:¹#™ßnŠ/_’’×ÄqJkðH#¦(!¬)4a #g¾ÁåE--Í·lù¤Ø“Ç1åóü¸bšº¦•læ‹o¾ûÎ;ï¾óÎã>ºÿ¥—Þ}çÉÉÉTf†VZ.RR¦¦¦^~ù•W^yõÐáÃ&véx\]–ˆé%«¯|é/ßþþ÷Z7n½ó¯Åu[ Éu´;fõñ‘ÁÄcLÀ—nË 4¬ª|YøRÆç ðeèñ%ýX‰“qœ\MÆ—ð%àKàKÿΑqOá?M³'‰éìWÎø’-\ øÒU°çØ—>ß„…ˆ/ ÎBVº /bÿN’å#d ØÒÒü©–ÍÜóÔÞ½Ï8rÌÚÚÚÄÙmuuuÇ'ŠÅbñx¬®¶6¸s§!Ù-±˜½ÑâéÓ§ý¹Ë#ýˆ3Ó3ìÞ-Ù³©iøKBÞ=ôîäääää$Ý^^QQQ“—Tõµ+ÊËmÀ¥‚3HPÔ̺ª¨™íŠ¢Efê몚)Æ®³îü캪ªúGG3³ÎìÎ^#^UÔ?ÕKL«¨·¾µUýèG¬ÉÇu}ÿ_ #Ký®Þzç×»ïþ>o—ƒýý‰D/¦®®í[wììà'1§<±«k›³§§Oœ6Ý߃ú®ÎÎŽD¢Íñ,tíØÙhk íé¾ ®r£¦_ÖVVÅ_>¾D€/ _ÒõÖèÜë •¼ 94:ÓØ° ð¥w|ؘ}ÓH…†¿¤#(â)fr)Å»“ɤ¦%õ·1[6F„g†Ì 3PTÙIŠþµÌ_$»™Í³¬i}{«ªª*˜7#-¼0&ŒÎ$zD5}âñ'²Á—çž_j„^ÓÌcL ªª¢ª´ M‡ÊΘyMæ ‡1Âôe…µ¡¯,š[€İ7‹/!Ø_²t²$ð¥ô`- ÎÔ#øäE`ƒY´ºòŠË7o¾IlŒ©sL6Ï5É<|ä¨/ÞåÙ(æ“LÀÔƒÞyçÇüÝwߥøROÔ#‰/—UW®iZe‹/KJ×ô#ë×s“Œ€/Ã/A9…ÝÚG}t< |yþùç³øÒGÃbbf…šÆi™ß¦+døãHŽ4 ñðeiY_"BdÿŽáƒñÇö–Ó ð%È£À³˜ÕÒÜܲ¹ù©½O‹1Sè¡ ¿@7É<}ú4¥™9°Ê ­Nœ8¡SKªå5+¨é¥d ˪+—UWÁ­¨ë†ë?úðCÞ·}»~¿µÍì9¾mëwußm…›ºvÞÕݵ]Ê³}ÇN‘ãyOo_"Ñ–H$ü:ß®®m;vÜål‰¹³»kûVë¯ÀÓÛÞxXø’1À´)аªNŒ/«*jòO'_ø5;“«T’q-yЍ¶ÏéÐèLcCµeÚøÒé,`\ ?ÌE„/ÇÆÆ<×|þùç×ÔÔhúŸ ˆÏí¶Á—ˆÁ—„ EIóGj¨©ãK,…/Q áK™\ä„$´köéñs0)µàK@`ƒYüºòŠË·mýë÷¿ÿR™Â¬I&ýËžn•IceæÝ037zçw{ì±ýû÷ëøR>QB¨¼¬¬¾v嚦U€/MÚ°a½Ø ³o×ï­ÛÛÛ7 ê8ØÓ+•ŠG&B¥©É©d,1ƒ8®¿cLGfEƒ/ A33³¼••±xU ­/A P@ðC_iPæòÌÉyÀ—YàKx•z‚ƒÅ—ÞÂæÈ•È„/Óv—š) fÆ0SÓR=Ö—™Ê=Þ-vïjù‘ŸŒõ¥ÿƒdÀ— ±À³Ttùû/[³fõ3ÏŠ…fÞpÃõ¶ÉÇ©ºïþþ¶­wš6v´oø’÷ôöu%¶:>/¡D"ÑÙÑ.6Ûìééëêò“$ ÄL$]]‰žž^I»WjŒò“ÊåȬÈðåàЯ@¼2VY| åqY§tõÊü¡¤ýðlhl6åHøÒñ,Bc}IÁ£Y ãÎúPc¬°CVÎ8ÓH0Ù"2 LÁÄX<Á1ÄöÕC0“4—û§zó¼7qûø„Z΋¤#êêøÒtŽ>ø`6øò½ï}¯)ö¥ÞŒ3ëÖ¡~Š`*ªªªú:‹‰ h¥¹(FJ* Íäƒ0Ã2•ô±î9N&Ñ]ˆfG ûU áKdó®ðïœGh©‹‡ý¯¾É¼ÈKN«W7ýÅ'>þ‰?vÙ¥—¸Ý·¿€ú˜S7sšü¥=Í'&Ž9zŒú›ÓD@!ô:Ÿ™ža?ž8q‚:Œ³Yzt‡qÉ:—/«nYÓ´ª¾®¢¼n0mºáú ¸¾ämIŸk÷[V²êììpÌê³cÇ]þžuW×6Gvzwr½3#™NÊ÷þ '9Èò{OϼáK„tr¾òú62$&ªDK¢¡ÑÉyÀ—ÎgÎã¥øôäþÐÜc?øàƒÃÃÞk~ßûÞ·bÅŠ,['{ ¦\á(íÎT æ£QÎyœ”¾$MÄcæÌøä€`–¨Ö¬Y}Ùe—|õ+yÉÅïóVÍüCiæS{öR ©'ÅÓ™¦CÓJ6óË7õH—ºÏ8—òÆ+ÊËj–W¯kY½|Y5ÜQnuÃ × ¾å9Œ;BLj,,#Çð”A@L™@œ¨@xDÆt=²/:|‰™™åÌú†•€/A ¼’¶³ªÇó¼â³'íe¾|YêOOîÍ=öK/½”-¾\¹"‡=H!ä¥1Ì%²pF#ÚÄÇ´âKTjø’쫪óñvÊj/À— | ¼ÈK]—\ò¾K.yß±cƒÏ¿ðâàà·J&&&&(‹LÓ‰ÚÚÚººTˆLÝñ!DSœ÷=n­PwK—”|8Γ“/½ôë-î6Ò%B¨¢¢¼fYuE…Å¥!Ö¥éfÊØ{ŽBϰåŒŽÊøn«ªÂ‹CÄ^™ULSPKÛØ—ŠÉ9È.&f¢­­mƃö^á{ûvÙÄL´µµµ ð}±wÞÕýÓŸü»äutôìîèééíììðñ‰“ ĉÒü4ü$!2¦ì„¶ñåàÐogI|©ÁÈ ÀÆM´^™KÚzŠ´#9àK·ø2Go0=H Ã|=ýªÍvÒnˆQɺcAŒõÌÜ’‰5iô"Çú( b+9¡ö‡ƒõÕ’JÊ‹<áz‘KÄÁô†}²/«M!ŠõÜ5M#¹øâ‹GFG‡=4î’K.IáKbi ÉL@HÚUÙ ¢( !i÷üŒ97佪àTµ˜(Ì1±~ ÊÄÀÄL³!DKu5"B&ö%I½‡J _ nÊU«VùHîã}€/A~ &!„V¯nZ½ºiqaáÅ}û‡††‡²ø+U†i¢ ÖDF²™:gÛDdz‚N§Ã—,yäðaÝ[¼¢"æ*Ò%BhEͲ5ËéxÂnÜ…']âgì‘Ì™££TTEU3ØQåDO7ÒLCL&Œ¦jÓ|tcMcUœ8˜üL>úúÖ­_ÿË/}…×Õ}i‚iÚ½kûÖ/þŸ¿\£;»åYgg»&öôö%mþÆp,2ˆ ‘1K_Š÷WÆäð% .A à^LÄ ‰£¥Sæ‘?8;E¾ñeî…™¡-;Ä5Å^7PK5s_E"ÑLUŠ}tÌ:Ò.ÁT´LLÕ‡L>9…>Äp.:ú¢3M#¡ÎÎΞž·ó²÷¿ßà<ÎNS4CP~‚‰õjÒ ¢„(t{†`ªŠÂ±ºxùÐðEëÎ@„ ‚1F©Z1bó+aD¨iIÓLJi†Ÿ¬7»Ô0;¨(5|éz¾Lgg‚Ø— < &È ÷]t!]yþ…‡‡G†GFý­ß@6)ΰ¸ýZ)§¹cÔéAËkVP|)¿K¬¢¼¢¢bEÍ2¸CüUû¦úvýž÷í]ÝwÛºoß¶uç]Ý@Ößß/IÇò’šÄD%mŒYºøR`ƒYYUÁìøÊý‹ÉŒ/B•x鉨>ìC£³ U‚y!àËÁ— —Å|)=Öê|àß Éš\qå•+V¬H&“¾Ÿx"‘xû­·xBݽ3Á"ÒQ¦„‘ Œ ÒQ#µ¶ÔÌIÉK0u©N¹ÛÉk{þM àæ| ò ˜ {]xÁ{Ð!4<ˆŒérxT¨ø’Ÿ…œTVÆ,¾„!$ÀTÓ_"B*Ñ"ïÑ›9¹ÐøÒ%¾ µ™û¶1ãTlþ&=2äǾ´_7„b"'bcÀtw^äL'„L*š–L{‘+{‘ûsñMñú¦ÆxÇcMSØSÖ:::wõõ ÝɯþÀÕi5Âô³¡«u·qdô"W#V/rB·«ªBÃeFT•w_:4zqë*z6 ú#„1ÁÓ#ãL+HæeÅàK;šYŠøRüž©¬ª úø6Á¹Ð{Î?7™Lnܨ!„FÇÆFGÆ!ããùL)î»jWÖTTTÄc©·$/†X‡sÜ—xhȃi`HQEµ¡–ªªâmsF–¦õLUª åµ›â`bûR‰¶¶¶ ¢”>¶ ¯;;Úž䮒ðtvvô÷ˆ³úd é˜P(uôì@DÆ”°õåÌìI^%ñʘ$¾ë%(HteÀ—â¿Ìœœ—˜d¾,|éIÌ 7cëXñ³÷°ëŠ¢èÅ0ƘÍêã7ÁT’IJú".ÁŒF£nH°ª1ïû’èyu4MÓˆ¦¢§ÿ±ìß=Àƒ˜ºæc<.̌ՙÞS2GT°Bb½‚,ÁŒ°3ihhO_hÃLAaìPz2¥`¤Ðˆ—83ýlÒì=ôjâÌ#kHbŽuYjø’ ¡òJÞMTUUì­lš û4| ÊF tÈ›êë7nLœ}vÛUW]þñµw´_¿aÃYtY¹rEÁN,VQ»rEbÙµ+WP| Ê™lƒ]êây‹'mÛ·mìØÓÛ×ßï"â@W×¶D[› …˜Aô€ã¡©vììvuFùUggG×ö­2祟]@Ý›i‰o…¼–σó¸^ÆÆð%”¯×¾¬G§äg}€/K_–èCB êØÆ=>öñÑx†µÔŸ_w›Ÿ:¸s¬¯çº6¿øîH&ixê]¤éNå$ò4²@0ûR/¯•.¾D„¦ÌeÆ)ç8£‹\€OàKP°‚ òMmÖÓåò÷_ú‘뮥ËYg¶¶®[K—ššå!lv]íÊæÕ-kšêjWÀEÌ—ÚÛ7 ¾å©L$ÚÚ„€Ì1UŽI2³§§7ˆ(Jˆ™H$ºº¶ÙšÐòº÷ _¼£€N0‹áQÑâKv]_ÂX æ5dk}I\Ìú_JàKxƒòCRÈø’jÓ¦M&ˆiÁ—ždmm­°µF|IÒ)f¼áK&¤&àË̶¾Žw;aVȰðø¸€`‚‚Õú³Î<³u-]Þ÷Þ >ø+õåW]ÞÒ¼†.ÍkV7¯i¢ËšÕM9hX<««]¹±m}}íÊx<W*¿êhßÔÖ¶÷íÀÀ€5g=U×ö­âšÝšõuv:à6·¦ò*Jˆ‰ògŒÙßߟ‡Ž*|iŒƒIăTÀ— PN_CžðåÌÉÎ$ð%àË¢{H _R±3—ø!T›Š³i£}‡Ç2¨iŒñ%‹2³¾ÌàKDÐPYeneÀ—  â`–¢Üf¡á¾HŒ\¤^%ÆÕ-Í«SUMcó ­nÊüE1™4„¯ÎŒmqp††G­o)6{ð†úZMcÂW|èýCˆi_ìø25u­m”Œ1Æl !v=óHF£†@BlÀJ=¨)&ß§ŒY7E´±Ý­!á„7R˜ícÅ.¤æú܇ííwußÍ»ézzûºö°rû¶­â€˜ýýýòó•šœª«k[¿£éhÅÄÌôjð‘1ûûû)éÖÒÙ‘ÛôG¥d}ÉÄÁ4”¡‰È%ñ%Œ'A gž6ø’ÄÉ"Â1Û¡ÚÌÉ…êÊ2ø’_¦¸ñ%;bÕG¼Š‚y±×ËÊÊl×Ë+*Ø ;ÎäÅXw„ó¦ìôAÓ4:5(++¨ö3ÜXE…g´çSæqÓ}Flo@MÓô)O2©±S6ÿ'Õ7ÞøÀï¸à‚ õ®^ZLÓÔÌÆ#*Š¢±,ZÆÎôà§4&&­-‰`„õP˜ìü…ÆÄ›=Ý´,®bŒ‚0A„(+ Ö'J* &Á©Ø—ìiúÛŠ}©?m%‰/!ûjVñî¦z>D|0ë~?À— ¿6˜ "Ñ«貪¡žYêêÙ¥–.Ð]¡„\mb3Ì~@Ì„¯¾äbŠËg)stT˜Z8³t IDATQ#5ÆüÂïØ±³»§·O’æiÄWÜÎãÑ4>€/]ƒ®"‘HEEÅŠ+ZZZ.¹ä’Ï|æ3½½½9ˆÁ÷öÛowttÈ´._He‡/Ùtäà<ø²TŸŒ|âË€Nêcÿ˜Ž/=Iu|‚ö¡±ôchL/ž‚ÎBRØQ7·„Ø—|¹¼×Ãñx<ˆ›Ð«í%àKPî…EâÌ<½|2åèíö% Çè==A‘2Iˆ\f¡à”ãȘ¹£™€/í$/a\é d29???99yäÈ‘^xá—¿üeggçÕW_}ìØ±€Ž877÷íoûÜsÏíëëƒþ/\Hc‹/g}€/]âK/±B{2Š_fßZº ÆJz‘úÓAdãÆ¼o§N!–9Ò°âKêœÇõÿjd¨¼Š×ÃñÊ8õ†ÓVÆ0˜²÷†ã­ƒ‘ÍŸ0_‚r, ˜ (Dêð”Ò9ůô§³³#_©ÉQQCLT|iÊ_ÚI_B>_ozê©§®¼òÊááaßkîëë;÷Üs¿óïÌÍÍA?.¤àK"zî_ºÅ— RxžJ_zÞKd†ydœ%€Dc@¤ÙÜR3BIøid¨¢r(Æ%˜‚\ðÞ.4X_‚ E³¨$ø££PæM.`¼udZ×?2ëa6š¤lULKx¯$ãIaþɲãQ½…†bl66#!ªýÑ zî[ÐFcN·.ÂÄÁT˜8˜‘H&x¥‡8˜l"^LLÞ°1.Õˆáè™íLÓ}å*¼ï«ÎÎŽþƒöy{l#R«IÍ]Oo_"Ñæ**bW×¶;îêç4¥bW×¶ g™ˆœA·!8ù“wõ]EAõ6vó«×òŒ/`zÖ¡C‡>ó™Ï<üðÃþVËór1þkÀ—ˆ„jÀ—¥ˆ/Cwvü©U6Ü¡±ÃåŒ9Ù¡,æÏ<ÇÁL&“tœ¯F"Šq°ÊŒ“£þb/äˆc mÀ¢iz¼~U1ÄÁ4ÔÄÄôg×Ùá=ûRÓ4¶+¶ÇÊ¢Q½a4ö%m“Ö¯%µ¦¦¦ÉÉ)öb*éûŽ¿êÌU©SÖ4„‘‚SL2i5=ÌØ"_¢RÆ—¡}5gðn*·.äÎøÒÈ| ʇÀ…Kbcï¶£Õ¤¿oGÿtÖò¢ÓÑV±@-1QNŒ1yYìý¾Ž¡_–<J+™LÎÍÍ¿òÊ+ßýîwW¬XÁÛ½{÷c=Ý>¦|éüP¾ëKåÉâ}îá¸ôóxÎ9ç¾}öð„sìËô£õe©ãË¡r‘f]}7!àKPa & —‰¶6>ÒC±)¢7Ò'öOGõôö”Õ¥GJBÌàšäåö'2f"ÑÊó+9|I_ú8DS”òòòÚÚÚóÏ?ÿ›ßüæSO=U[kÈDwÏ=÷@/ø)àK0¾„÷Xñ>Cù×¹9I²YÙHÊë#¬àT°EÄ|îðÄÑÉ“îc_ê ²Dñ%ÒȾg®CUUU&Ê%6xH¦Æéè˜ì•²HÁXI9²5±1G`<&á}Åðýþ| B@0A P%)ö;ïÛ?0àóå75yê¤$!fÀÍNÙcò\Åó–šÜ˪àñ%gœ)À—0Ìt¡7~ãß`·<ûì³Ð- Îc ø¾y|†J _fy q@ÆçŽwû2"±ÄðeÍ*_"`‚õ%¨(³0dMûåj®h$šô‚Ø¿ã0‹1¹™¢*ªu¡yÏô…ýŽý£ŽjZÒ2Ôo[-“&O1·ŠÝE?€¹LD_"ìQí—hD_¢•e–2öS»Ä~QΈW¦Œ_̰ Gã™è‹ù„ Ýnß·†¿ÜñÓÞñï8î-jº;„FySÊD"á—Ü)²dáBÌ4#8å8M9àKÀ—…(SœÊ#GŽÐ•ÑÑÑh4ª¿N›šš!äšššô’Ñhtll Yâb»{<ú裷ß~ûºuë***V­ZuÍ5×üä'?Y\\t<£ùùù_ÿú×·ß~û¹çž»lÙ²H$R]]}Î9çÜ~ûíÿó?ÿ³°°àjhtèСø‡¸ôÒK—/_‰DêêꮻÿüçâzŠ“bºÆ—2Xðe1Ï’y£;ƒi–a¨Ï¼3K„#Ãà>a‡£Æ!«ƒ ÕfkßfÞxß4ŽF²]"ªj|g–÷ ó‰²]Ѩ±ç":é0LFÊË*Ò*¯ÈL***b±˜þU̬ŠôÂlJ×RQ^QQQÁÎD¢‘Ô¥[½º©±±QŸ{±=Ž:6=÷ü‘ a¦œì:¥ ¾Äiä‡*M|9T^¹¯¶Qð¨Ö7HL"A_‚€`‚@ ?êìhû’ ЕcZ¾ä4S¸Œ‡8›1Í×= cLÞÊGo¾|ˆÖ¬YÃ~<}ú4]ihh¸îºëôíCCC<óÌgžyfhhHÿøÑ~Ôm>S“fffnºé¦k¯½öž{î9tèÐüüüèèèã?~Çw\tÑE‡æí¨iÚ~ô£–––[n¹åž{îyã7fff’Éäìììî¹çž-[¶´¶¶þêW¿’i†¦ißýîwÛÚÚ¾÷½ï=ÿüóÓÓÓÉdrbbâøÃç?ÿù /¼ðÍ7ß|éš6¾,| òñâzáBÄÓ‘s•yÜQïyÏ{ß>{lòèôiÆb_rñ%ÒȾ•"ÿqÉŸlÀ— "LRyNéƒð%G™‚rQ§ ¦ïƘÁ&óña Uºø¢aº B úúm·ÝÆ~õ»ßýζÓöOúÓY6éšk®ùíokûÕk¯½và 7,--Y¿šíèèøò—¿<::*¨|ppðÓŸþô§?ýiGsÎ-[¶|ë[ßâÙZ8p`Ó¦Móóó¥A`_æ _“æ"{xü{þr|Ø\ÈྗѪU«Rú›d°#ááË}‰4²kõú¡xµ 'ëêjÆ'Qµ“bë©`ÇTì gwû¾…B@0A PhI–÷”>Ž&“ÞŸLjò@LG—vTày2ÆÌkÈKÏc¨"×d‰¤ð% 9Ýé¾ûîc?®^½Z_ÿøÇ?^U• ›Å#˜<ð€¾^SScrK÷ _|Qðí믿n5¢L&“ññûßÿ^ò¿úÕ¯n»í6ñæÞ{ïW200ðóŸÿ¼”H àK<¾×C“}!Æ—„{¨sÎ=Wðí±™ùûúG|‰ ö¥_UT‰ñesó‰ ±/}8P˜³$„-m_òƒr )¦lflÄJ&`%ó-ÃFNTŒÑ.Áyâì¡*ÁH•RÄåv‘ôC²ån“è7l¸Rœx©æ[ËexV×´.‹”>Ž&“Þœ¾e ¦­ú"™¸œÈ.ÝMaÉ­1¦·ûðeS+À—èõ×_ÿÇüGvË 7Ü ¯Çãño¼1ó"êïÿÓŸþdªáÀì6n¾ù把 ö:Ygº Û°aÃoûÛãÇŸ8qâ?þã?b±ûíý÷ßo*ßÝݽ{÷nv˹çžûË_þòرc ƒƒƒ÷ÜsÏyçÇøÍo~óƒüÀ±‹Î<óÌûî»offfjjª»»[QqKJ_ò.%| øÒi6ašØ[ãY"žÛ•ÉfÜÈ›  ÌÆPãKó ÚÐAì©°¡ÿ ó§ÌvvvÀ|ˆ¦éš–(œÆø£™%‰d‹2G#†Tª1T*ÆèškIÍÍ.Ü70Ž I¸´Å—•2¾ÜÕ¼ApçÄb±x<øð%f¨ 2?ó2YzäÒúp¹!æJæçÕÄœuá;ûû¬HíÂßÃ÷Ô,e»Í g® ”gvÕ=íâ‹è§ Ìt–G2cO–wu§×”>¡ÎÎö P£¸ZŠÌ‚¶”„˜9° Zn1ó _š÷|™å|˜ÌÏÏ?óÌ3ßþö·/»ì²©©)ý[UUo½õV¶¼É‘Ü ìL[n¿ýöìÙÐаgÏžO|â+V¬¨©©ùÜç>÷ÏÿüÏl×^{ý899ùOÿôOì–-[¶ìÛ·ïöÛoojjŠF£·Þzë‹/¾¸yóf¶Ø7¿ùÍ'NZÒÔÔ´wïÞo¼±ªªjÙ²ewÞyçwÞi¸€/_¾Œ½R gH†< 3™N•…‡£8ë…—q@« Ôefa˜?>gæ,L#cF#ÃÔÂ&_’P†É S™a2b7«khhhhhàmŒ<¹xìäBš 1b*ÉàK®ZZ𳯗^S_‚€`‚@ <ÀJxMéãˆù¼¡Æ0¤&/)ˆ™½1fð¡0_zÁ—Œ#…slEQ***/¿üòï|ç;'Ožd‹mß¾}ýúõì–k¯½¶±1“ÀÔêHÎnimm½êª«²oí׿þu6'Bèúë¯g?޳ÿë¿þkzzZÿxöÙgÿçþgYY™©Ú²²²_üâgŸ}¶¾ezzZœÕ§««kÕªUì–O}êSìlj‰‰’¸ð%|龄7X<.¾\D’åcè^99¡TO~èC»ïÍGg ö¥[|éè?¾tÜ ð%(÷‚ Â.±Í£Ø<‘HˆÍ÷vììöÐ$€˜ù¸ ¼cöJ0_Ú>_¨öööï|ç;¦ªªnÙ²EÿøüóÏê;ö /èo»í6_€|øÃ6ma)*BèÔ©SìÇÞ^ƒåûwÞY^^n[syy¹ÉˆòÁ´äƒü iKKK ûqnn®ñŒqÅ2}#._€/ý|O‚òþ|9¾ôÅeØ.€±$eÛÖ½®½öÏÄõßÿöä³#'S§±/%ðe<¯ª®û!šY£\Ee¹}ÿ ÄÌ–Â!$'¾åE@0KN¬Ÿƒ´K¹1¬¥õè-”$ãâmðrVÕìSš6Ú¿ðÕ<(ócÂþÄ·Û{”CÚD¤b_ <ÇÁu]zÁRssÓŠ)¤£3xW×6Ôè‰î9& B^Cmz€˜Ž\¯8 ¦_‘1_f}Pø2_*++û»¿û»ûï¿ßü±Žä„6oÏ<ÀÎ ²ÏBNÕÚÚjk±“É$ûñå—_f?þÙŸ‰æº¦o_yåAá5kÌ&*Ë–- €V:¾të øð¥_3̬bŸª´«‹Ê³#XÖ-ˆÏ¥¿¹`Àmˆ‰©7–;b=ÊÓs‡L\«ˆ!f*&¦aÊÁ¸ §g‘L(*ëTŠ™J ĞʪU«6n<[|áž=ußÛ“ûR_Æb±5 |ÂÊ÷_‚üWº ?è8›—æ ØíìÌ¢?„úW §*S,|™—¢fvQ‰jûÂÑ0ÆXŸÙbýܱ¦CKر&vì7cÊ"óNt;oŒeÜÎö6“˜u…éöL1UÍ<†‘ˆÊ¬g¶Ó‹~9TUͬ3WGß.XW˜‹®0ÕÒa‘íEçÝcˆ‰ÝSÅźº¶}á‹wpAao_g§(£n×ö­[KꊞH$Ü>}ýýû>Ê ª\/±cÇ]bKCÚ˜ÎÎvg*uvv$m==}ò–•½½}ÁpOÀ—€/ƒ•¢(åååÕÕÕõõõ­­­W_}õ-·ÜÒÜÌ †uÑEmܸQøxÿý÷éK_Ò×õb—_~¹ÉݳL”Ðô‹c•É©œM§n•éÛ±±1W-‰F£¥{ë¾|éjDÂÈæÎC8c`M,±î²XëÁÈytjÚÑ—£KÿRs&hœKQ¦eZ®2·¢ÆN˜Ö0ÖÒgª±w:á]hEÅúC‰Dˆ¦¥' aL·+ Fa³c !éÄC™ ÅÆs6ŽOŒ úçØ©¥ûÞ¹´¾bML-M|¹kõzqæñÔïàšÕùø‘òIw{û¾ù36†.@…Á­²Hé/9’KMžãGÇ–¤¼o{JcLÇ\Š/SSSa¾$Æ”ö#x]ÉdòÔ©S###¯½öZooïßüÍßð%k†ùøãÓä?“““O<ñ„¾Ý—>TìÛd`„+ªhºCÄ5[ƒiæÌQ „÷qð¥yðe‰¿cóTI .·ù¤85îê\UWW'ÞûØ©¥ûÍ=¹Xjør¨¼R_®Î ¾ äž| òK@0A Pa(›”>HðyæŒ2Ó[Òsß[’:ÓB†˜ýýý==½==½_øâ=½}nvÈkÃK_˾Ìn½õVÜ-..îÚµ !´k×®ÅÅEº±¬¬Ì”æ;—ª¯¯g?²‘:­:zô(ûÑ”¨Ç¤Ò啼'ß_À—€/Küù($|é)£Ž·ˆ™2¥0FŠB¬°Ÿb>Fׯ¹æC¦¾­î?rê¾#§J_î«YµkÍ|ÙÜÒ\U]eI‰DˢѲ(ÀK3ä£e" ¨¦àbØ #EAØ$Àíâú| ’ÌRƒÙÁÅö+ÑÛÍKÄK›x—*‹’yýúzR¦T„»pYò‚ZÚ‡±L}ÁƵtÞîþ4¸A?yÂüè—a…˜ÞSú8îž g׌¼&=ˆiB–_øâ;vv÷ôö¹b—úõ|<¾Dîð% ,ÐÚµkÙ$ã4ÿ8›…|Ó¦M+W®ÌWóÎ9çöã£>*(üØc±/¼ðB¸¾.ç‹|‰_¾,Ò™ŽÄØ5øRòÒn÷8sÇóçÅ.н¢O²¬Ó*Ó †™5D s C$}UŸÉÑyƒ‚±‚uÿòÔ‚°>Ÿd/Ç>ôÁÚÚZÇ?v:ùƒƒ³ÏM,7¾*¯üÙYî«m”¹Ö4¯1•vuẕƒ ö%&(¯?ä®å6K‹ h2yrÌYoì¾1ÿ\s0¥@’Å‚–%½œË”¸jÂü=’ñËío9·¨Ì.Y¦ôq̽ã™3†$5yñAÌžž^ÏÈ2< ð%_Âà2±Žä>øàÔÔÔC=¤oäð1½Š5Mó½m×^{-ûñî»ïžŸŸ·-9??÷Ýw³[ÚÛÛáâ¾ ¾,%[r,7Ò“7z›¿Fžét8¾NŽ,G÷:Z¶Ïê#ëL#SÎNÝÈÄÌ*Ù¼ œÅPÌb¡Ï,¨‰%¢SF³ížý¹_qÅå2!ô܉ž3÷܉Åâ×C•»Î8kW“løéÕ«WÇb±3t"&ð%&¹Äs"àÕëh†Ù!¦{ž³‡‡ b:6Ä'hr¥Þü0ÐÄ—ìÔ_Âè2 Ý|óÍz¦òÙÙÙ¿ýÛ¿¥kkko¸áÞŽ¦ •§OŸö½m·Ür °òÀŸýìgLÅ>ûÙϲ/¨ÚÚÚO}êSpq¥ç‹<|IdvÀ—Žï@¢ÁíVbÏÉÁ.^”ÛŒ]~Åûkke ÿŸ›LþðÐÂs“Ú±9Rørßò†Ÿµ^°«qýP¬J²V¯^‹Çü’eËó8(<‚  LÙ¤ôAN 4_rGO”!õÐIˆ™›DCyW›„]ªïÂÀ £âKvž …/aˆˆV¬XÁbÊÿøÇúúæÍ›­otUW"sý⿘››Û¿ÿ3Ï<ãWÛV¯^ýå/™Ýòë_ÿú}ï{ß=÷Ü344´¸¸8<<üßÿýß_|ñ¯ýk¶Ø¿üË¿TUUÁÅu3}³Ã—Dîa|éø|YjUîðe¸Nœ6†?wÌúuEÖžÓમª5rõÕW×ÕÕÊôùiíwcÉûÇ’ÏÍcó…‡/÷-kØÕÐú³µïÙWs†üYÇãñÏ^¶¬:‰–´¬^zååúa ü 11S&¼ªªB0P1âK@œ- ˜ ¨À”eJ„íe³ÒÑÆ3g©É‘4ÄÌe“¼œ…Wò˜hkëìhïìhÿÙOüÓŸü»LWøL‚*ŒÂ‹/3CRÀ—yëHÎNÄYÈׯ7ø¸}å+_‰Åbï}ï{ô£ùضï}ï{]t»åµ×^»ýöÛ›ššÊÊÊo½õÖW_}•-ðùÏþsŸû\V7Ó7¾tE$_¾™ß¢Áí‚ÂmJ™eÓ®ºêª+®¸ÂÕ.ƒ èùô» ôoCøwøùYüüI%œør0±ª~_uÃÏÖœ÷³æó÷-_5TáîOn±XlíÚ–¬î(wOÉY_¾,tE  B‚Ø1ìóÏS” žÖ4M/„ų®J´DÓ4=0–¦iúÑ5U%LÀ,%™Ôד ³®©ÌðÙþègÄœT2©Ùž¬Æ‰}~[ÓË1Ëa{Ø>ÄLW+LʶÔß°Òåë™bj$Ó'ª¢Ún§¡IÓ‡Ãú:yÃì’y¢U5³{„ÙnjI¦Zct!¶Zv]>ŒQ ODggûŽÝ¼o{zúºº"¶•H$ÚÚ©]vììþÙOì­m]]Ûvì¸KP9%†bSP¿D!¦ ¯rß$÷§Ð&™„'ÑÖ–H´!_}Ͻ„'áÆ—ÌŽŽø‘ÁiÓ¦M555“““ìÆ¶¶¶÷¿ÿý‚½®¿þúgŸ}Öºýí·ßö±mñxüᇾñÆ÷ìÙ#SþK_úÒøC¸¦nÞ€/s†/á%VT€øÛî„7š';¼Ç˜™1E¢ÜûßÒú:0§™NÊ’™fæ,ãú£_D”ôSOXÛϘÁÊÚ•—^zé›o¾yüøq·]:¸ˆ1Bè¹YŸ¦l$R5Ï÷X³0o’òò _z˜Ò¾*°Á@…§,Sú '_r$á.®ÜÑÓ³¯º‡¾*KLûSc¬,öÓwumëìì|éÓA½àKvGÀ—ùUyyùÍ7ßlÚ(6ÀDýÕ_ýÕÆ­Ûý%˜¡ºººÇ{¬»»»¾¾^P¬¥¥å7¿ùÍ¿ýÛ¿) ŒWÝ¿%ìð¥Ý´ð¥t àËÒ{œÂl}Ir–oÚ·[}eíÊK/»Ôdït߆Y±X,‘h -¾ âN| ÊR0"@©,Sú '_rot:;bfá«îVb:ºc‡b²D2¬ÈÒó˜«øð¥3_æ_¬#9Bc|ë­·ŠwY±bųÏ>ûíoûüóÏÇãñx|ݺu7ÜpÃßÿýßûÞ¼H$rçw=zôÞ{ïýìg?{Þyç-[¶LQ”ÊÊʳÏ>û¶Ûn»÷Þ{ß|óM+‡I¿-lñ%á>ì€/߀/Kí1ʾôp;y¤K’ÒÃ!blÛ6Œ‹)_:“¡\1І]L$Ú>ò‘¯X±"'}RÅâ±5ÍkÖ®[‹….ÆžJÇU”tNx…É ŸI¯09èÙ òl ¶iâÂ<2~\DÀ— ñº¨:;Ú¤ÒÑ-š&ÞÕ°³»kûÖD"á¡m2îÛÙÔï¥=] ±{;JCÌÎÎöÜ´JþB‡ Sú2æ*N|99>»¼®Ê²ÿ~ý«êêê[n¹º¨”^Gîð¥ð¹|©9×*¦G(ÄÖ—(`ëˬÎÈk\Îå5Ë/¸ð=øàնɸ‹_Æb±ææ5--Íuuµù½¹ƒ¸Ã_‚r) ˜ ¨€•}JÇD7Y¬ìììpÌê“ãè“1ƒáM>B륃¢¬Í$eR“Ä,`Ràt¥`R÷°š›Aðeˆ´aÓG9¨È_J<|‰Ðx¤Ú̓øðe‰=<¥Œ/u”eÚLÉ%ʰK»´06©hìh™Ù¥}p IDATÌE"Š/#ŒV®\¹nÝÚ~ð\ðžæ–ææ–æÂº‹(µ\»ní9çž³®u]}C½*ì†LDÔHÄ.éÂöŽ¡[(bÉðcL d½>Xɤ\R°~mŒ™Û„Þ)€/A¹ÄÁ,< " ²ïS1ý#“]ÔÌn%J&ΊÆÄ¾4U¥$3ñ.Ù€Œ‰°­Íì.ˆƒIìcb¶ûNð÷ÍÅFu1ÆdÊÃGf¢L"6¦awcL~ðJ6`%“°R5Fƒ¶½0'R§èfàÜWÞîX¿”eJ„PW×¶/|ñÞ·”0:V"®_€’š‹æ2àcW×¶þþ~q˜N”óH…G œ®„_65®VéŒ/‰ƒËÀuöÙg÷õõUVVBW”ÒkÉ_ üÆú¸¨àK™:A…þèä9ö¥ýíIíähêÓ¬LcnQÅ.% A!Õå!c‚í'‚¶.BRéÈ]vs×ÕÔÔTVU"„ZZšBãã333™£fŽœ+Â3C…úsÿÄãñʪÊ,g=NÞÁxy#‚ƒ¨ð%(Á@¯ìSú „ÄYw$+6²]Œ {zû‰¶\²B™\C¡;»mmÙ\P1Z_rwœš8¹¼¶ðeîuÙe—½õÖ[SSSŠ¢ÔÕÕ]pÁŸøÄ'n¿ýö²²2èœR{ÛØâË1µJúÁ|©¹¬Tà 8{º£I®âr"„–-«^¶,eE®iš>ŠÐˆ¦'²L{Û^nXÂüKæ–fõF!\À— | ¼ÈA P1(û”>Ž¾äŽ¤Ï±~qÀM”¯m™V¡|ø¹PȾ|ØðeuU¥°^À—¹Ö3Ï<366¶°°077wôèÑ]»v}þóŸ|YŠo{ëKQÆžªÊ¨ý›ð%àË’xd_º¾£SÞãzE6ž¢MÆL LúoÄà=Íë&-’Qê•nð«fCª9 îiòÏ4+Â.ì'±Òå<ÏqÛèšÔ_œõø7¾Ôc˜â‹} ïïBLT Ê>¥’ˆ™%ňYš3#_ʇÐú²ºšK0§&fàK(?,ÙãK‚Æ#Ëx~ue™Í›ð%àË’xd_ŠîhsÄKãWLj˜LLEcLc¦EI“7.©‹22lDÙ%¤6’¦j‚~¢ˆ“ú¢ÊáTû ’Å—FÂêAÌ 2W•o…Å© —éÅN©H˜¦ÌL<² ø”_Á,EaޏeŒù̘ÍRrý‡«ˆSˆcËbþdûw­ìÎÜß@É“âýñLNÆ e¿]FEpcgŸÒ9ÙrJ’P1.ììh—É2q@LÀ—9À—Hhƒ9jQ_‚›àkLj/O*e¼¿*|é _ ðe€øÒyBgGœ~²Îõ%=KpÝ]8µz¹ôµ„so—¤ó8¼¶ _@0Ã+tI GÊI–N¦—ì©%ƒ=ý*Ë?s-•C<#y‡„Ì"‡6ßFÝQNÉûÇÇ»×‰á  }s$Œ=½}YÚHvvv„-59JCLÇå1ƒqXìK æüéE뤾rfLš1 ¹AÕ)rÀ—€/Kí) )¾ÔÇÎnÇç&|iã7l…ž±S%µçˆÆŒC®¨g@t/Lø’ñZ0&¸õ±µÞžÀ— ìÄpfãɲ䇤£»zÞ ¦SÃô¶åØÕ½gF¾”yêæôÄ) | cI(Ð9}Æú”RÎ+^U| ø²ôž’0;{;L.ñxl[IàËPÜ¥A^DÀ— Pr‘ƒ@ ¢’8åwOO_W—s²ï®®m_øâ¼o©/yggG6íìêÚ¶cÇ]¼üé~%ˆ†Ñ¶íØÙݵ}k.3§ÖÌÈ—òáÏ<^]]93{Òö«é‰SÕµq}_À— PΦږ9ã`¢Œ ¦õÁ| ø²XŸÀ—¢#aççÁœh[QX¾ Ž’>PcMM­+IUÓRÏ]2™Ô×B V˜j3ë‹‘Lµš¦éùÄ5MÓsŽc>òd2i|ëÕÌÖ•Ú”ù¬wu‚Ó·«ª¢×A×Sý¦FØ×w¡Þé}U}!aú¤,Z¦Ÿ[†fTÊ´$c5‹9©Ôg„Sg,ö«| Ê™À³Ô•¥ƒ¹}¦9‹K¹£ûs–)âŒ1¢³Š>éc Kã]:ºŠ+Ø‹Šï–ö%¥BH2{_räsÓ¯£xŒ%&ÊGÒ¡B™ùR>üø!ÔÔØ ¨czâàK(o/¢L(Z2YÆ+×Xç<ø€/ñ%WYA>¥†/±Ý‚rg¾È9gÑB(UM-ˆ¸^XÕ+AÝÛûTð‚Yð’‰ÂÛŽå"$zk*»x¢…úâ~g6õŽy|Dy„¤ÓÕùì’ŸÕ;¥óû²  _RúˆI(BH`é)©p¦&×û fN¦+Œ/©sfâ$âKù|A €^D ¾S— J66Äí|À—2øT€ÏFãKÛ‘9FX¾<ÝG4¤7EË”±ÓË}å꺇ê&”¸å¸Q½Æ¾ôã| òO@0A P*û”>ȉ„Ê×#@Ìb£~”/ |‰ª®Š :=~Ò2Ðd @ P/"_"‚Æ£<‚I2YÈ_¾,…g£ø¬/INb_zíî@ΡÜô­Ì«–”º'4±/A!ÄÁ@E¨ÎÎŽþþ^0GšÒG&†c×ö­[Kùz¢ÙÏÅ–¡’á;}WW×¶þþ~GkSˆ‰éi”T ø!R]]Y]Ÿ™=e{Йã§ËbÑòXð%”ÛWQ _*«”K`¾| ²ÿI|iŽ}©ÛnšŽ®(*s$!©Š‘ÛQÓ´ô^š¢iéЦ±q0±’1úT™8Ñ¥¨~Ph™uMÓ½=Øí!-™ùȾM4æñ·íFqß²¬†u„í¿Ò;Ž„‰}©FTý°lðJêig]GE#dR“ƒ™2ÓÅ´! fÂŒ²–¸V³\À— p l0AÜWs6ñ1Ý»«Ì*?®e(Å?'Õö³ y™¥JçG™”Ì'Nñ¢Þù³#„©Éõp´¥ý¯†ŠdY¾àð%ýÇâHnØwæø).¾Œ õF"ˆ “Jù)µœ÷ðVÅ£Õ•e€/=ãKŠ)î§ð¥×ÆIc ÄKÚa=¥i΋°/—>^“‰™ µ)×/åy(¤&àKP¾³0$I |Œ‰™-µ”‹çÈR@6dqˆw‚*¯Œßp!)/Ø¥ &¦ûx— gØ8©_)}ñ¢$ ËÑe;ü3- ,ð¡|âK„5Ãäí»pziþô¢-¾„Ù?Ì)5¿ã$ðI=w qÀ—ÙàK¸ÕŠù)*4|‰MÑ+- ©K-Ù5Žã< ’­3=ƒ‘iþâ}ZDø”Í•c'û¿Ðñ%¼À ZàEŠV]]Û¾ðÅ;xßöôöuvvÈÔÓÙÙ.ö%ïé镬JÜÚ;îây¾ûx ¢ÓÑœBLÇø¡E |(_¸ø’ª©±¡ÿ໼}›^¹ºº<µâËCãoOŒÒ¿L†›Ä®é,µŽNéb,§×ElObry'æomÇÆØX„m­é›35Ÿ{N¼$HÄÜHb=kdNÀöcæèÓS§¦§N«seêœÉv¬­­B]¸¾1]«)­à ° &*ù˜ÒÇ1N¥_™vBžš\¾‘´ùu{Ïå´Èid[Ìø’(±¡U1S…§†OM ŸÔ¬€/ó/YHÇ›V¾ ¾<©”ˆ­9¥–;>¼Ö.¯®Œ¾ _ ¹`§_¢ ð%Oü˜˜ÔÍËÆáõ†f]Ö”thH³›Á)[µõ“KGÌL…Ï´ø§;ÈiÓàj˜‰gÉ-dò/̸äqÜìÂ&{käKkLLC·CìKPq &*!ù•ÒDZ*ý»‹ bæ=vgn¦ENÓ„âÇ—©»bÃ:Ä4T¾8—wfan ðeþñ¥›»ðe~ñ塲z;Ïqøð%Hâ'ðe¶ø’a‘FFÏ‹p¥Ø-zfŸTö&À–!ì–9JW_Z‰¡­Òr,[ AÊl1e„¾L¯—m…µI¾ÊnÏœ©M01ç¦ã‚š’adߣŠéÒ@ìKP1 fQ)›ø†Y†MÄæôrÎRå¤p™‘ª¯’9Š ‘n«Í}¼K^µEö˜ø•Ò!$fv>úw;ú­#ÿ¬>b¾ô_ÒvÓ¾òéáÓÓçç’€/óŠ/‰¹Áðežð嘺Œcz øð%ðe.ðeîû6õ‚Íü>øÒ 1©¤A.S¡.“DKj𦑑á‘ßüϽ=ú˜–ÔØ%™L.%—–’Iº$“š¾è—’É%úeZ©Ýi$M9"ûAˬ;õC&*&!þß“¬/A@0A ¨°äcJ±[:BhÇÎîÜ4…ƒ –<Ä|iS‰bŠ*_š×fFæfFN/Î%-5¾´;¢u‡LçŽ\âKÁçñ<ãË“JÙX¤ú@Å»¨—öÏàKa=€/KQ€/=7.¨Ø—¹j?=ÎèèØO<‰â‰'ÌM àÉ&ãTßïIøT‚ JN~¥ôAN¾äÈ¥Q§ã± b:&(/Fˆ ø’[Ibú¦3ê%+_š×fGçO>uzjqIG™€/­G45Ø|Iøá/Ùò€/ß/Ç"Õ‡Êj—ÕG–I>¼Uñ(àKa=€/KQ€/=7ÎûMoò67º§Û_ÚH1dýäR u™{â‰'Ÿ|òI½’ññ‰ûî»bbBI{gó}ÿ8žíŸ<=þ¦¬SuhdÆ%\J öì'øT‚ JN>¦ôAN¾ä®Œ:%1ýŠ¿™M÷:¶¥!f±$(|éPISc}bý:W•ÏM-ÎŽÎO>57µ87µÄºÆ—Éy²0­¾4Öi"hDtc¾4~«ïuEÇPœ×0·øò$.S«EëT¬TŸRÊåÞªx´màKA=€/A²»’´¾DˆûÒ>¤Æ¡4±DfH—^2€3ŒO¡SU3P¤›tn©ÇøRUõñÇŸ·¶äÉ'ÿ811î.úXIC†)LKš°G1îA[dÏXUÕ”·ˆ…›,æTŒÝä@~!ö%¨ø.yÆØ¯—EA[GÍסAŽ(ð _¼ƒ÷mOo_gg‡<°ëìhdÚ±³»kûÖD"áKË;;ÛÅÎé=½}‰D›_‡Ë¦‡w츫`@P¦`ÀßÎ Õôð¥IÕÕ•‰õë‡GgfO¹j Å—óÓK!µ«åŠpàjƒðBK3„1ˆè"šÞ±ØnÀí€/mÏ#dö,}‹e& –¸ZÖÓ'FHËÁ7!Ç‘9¢,E¬¦N/"BFN.Úı7Ø-¼Æ°»cs·ÛÝM-±Ë¤nâ¤Ö¾á·\†Çé»DÑS8:†â´½q²P‰˜3H7ÛTÎÖ;®V»Df†õñÆz·ð%àKP ¿ï²¿½.ö*x|éWcÌ› ±®ömOÌÌü1SK½ýGGG_yåÕÑÑQ^ç?öØüàêêëx…pa¦[0ÂúƘ ¤àÌg}ÛŸ hGÂ6c÷1X_‚ŠH@0KBìûQò —Ac„Éb®ª•lI–Õæ «sSU–G,eê€wÜåè!ž©ª³£¿@@ëzzúººütÔM[ 1C‚e fxZëïô𥭪«+Õ­ƒC£ƒÃcÞ“œ'Éùdö×eiÆü.dvä½¥p¢ÌY`NœÍµp]˜Ø®–Í£è¼f[ùÔéE„ÐÈ©%þñ‰Çp;Ëcñå øù"6W+ezdSøŽžÂQOs}ø²*m¬WWÒƒ¾| ä÷]ö·×Å^€/]Ÿ2á4“Å™tuddô}ÔTÛæÍ7íÝûÌ‘#GõâO<ñd]]ÝÕ¸Êq¦‰MËÄÆ2zòoãt‹=uÏs¯´­«Ï7àKPa ¼ÈA P‰ÊÇ”>È)¶¦¿ÎÝ2 svì샃¶Œ;yxZëרð¥XM ‰õk-9Ê}d2–ć£ß‘Yžñ¥ëÊ}Æ—rû†_:ÌšH“vø²±>žöG€/_‚úñòƒà°àË`N!ÄÇ—-ÍÍ[6ßܼf »}||üOîñáÖ!^R’Ÿ¬Dñ%¼Ñ Y@0A PéÊÇ”>ŽT±§·ÏGHG]×Åe\µ?8;Ä 1_r+©®®LlX—X¿6€%àKÇ€/}(|Y^tN]Ús¾Ì!¾Ô`\¤|™¾DcDvÑÙp—Šb4^T„ŒUŒ ¡$#ªJš>‡YÒkŠòÚëo˜ðesóš-›o^ÛÒB{Ë-Ÿ¼êÊ+Ø–LLL<µç©h$D¢Œ"©Ì‰F¢ú‰D2á4õæClF"lìL½±U˜se*À9Ç»4^‚Œ1¨ àKP! &*]ù›ÒG\ò)ŠmHQ˜ò}wums4EE1_º:huuåÅï=§éŒzÿF”€/ ¾ô¡LHðeU<ºaÝò¶uË™í€/s‰/AE)À—9}‰ˆ¾‡U$è’+MÓ´dòáݼúê«lUÍÍknÙòÉ––fvã•W^¾eóÍì–±±ñ{ïýíðÈÈÒRÒvIjš¾°JjÉd2™ÙNCˆFmTz³ù4M_Ít„©l·gy_‚ T@0KNâð%$À-*¾ _2¡ ‹ O(c64&¦[Œ>dÌ^4–&V†GFÿð‡ÿe«ÂoÙòÉ––„°xiii¹eËæ––fö(>óôÓ¦è”ÆUEQ¬`ºBÿc]˜ˆ–ªª*´µa¤(Æð–†Oœ³‰wiêK±àKP¡ & À_»PäcJ$aé/ž+ Ôä¨!&àË,ʃ¨ºª²éŒúÄúµ:ÍäMÀ—Ž…_úP&—ø’"KJ-©ÅecC%ÿy| ø”í/y?ý®öëËt]üàŽ$3î08_[ÃG¦ƒMj„hû÷ïßýðnSU›7ßÜÒÜ,ßÚ-›?i²ÄŸØ³ç©%F‹K‹úB·$—’É¥¤–Lj1Æ´˜I¿LjôôÅäg΄Å4õ'[Ìí5| *E  @ºd(›‡G)ûŸ;pRÏ¢†“ý¶ßRGìÎÎù ;;Û¶–*tl×ö­bëÎ;»»¶oM$aèí®í[{zúx®÷ÒŽw‰C‹æÒøÒûA‰ä »ºª’oŒI$~ªˆÇVq9ñXOV•ÈLˆryÖîn6âòâ:oç\G—•·tq ‚²ÁaŽÜMæy| ø”í/y?ý®ö|é®ù¼¿D‚4-õÖ¢,o÷îG¬/·lþ¤‡6oÙüɧö>½wïÓú–‰ñ‰§÷>½~ýú•µ+Bš¦±3/+éV)Ôv%]Æ`%ª¯*ëÝ¢0gˆ™b„Ãî>]yÀ— âØ`‚@ B~§ôI$âðš¾{v;ù =ËÖ:Zª¢4Ä m‚rÀ—YT_ÊVÂã¾tìgÀ—€/_:^/À—Å*À—ûò‘GþྤºòŠË7sû?~â¹çž?>qÜ|‚Äÿná˜WfÛu€/AE# ˜ ”’¿)}Äá5Q<ÑñˆasÍ–…˜¡qÏb|øIÀ ¯•pǸ€/ûð%àKÀ—Ž× ðe± ðe(ð% áˆøq2iLHÃ.c¬ŒŒŒîÞmÆ—W\qy6ø’ª¥¹y³%A9…˜Lk12ìÔm-12†®Äì^lŒ1SK¶ß 2í%{_‚ŠI@0A (%Sú '»Î ²„ˤ&/8ˆ‰ÂÇÓÓøð¥ ^ñZ wŒ øÒ±Ÿ_¾|éx½_«_†ÉúÒý)?üðî‘‘vãæÍ7_yÅå¾ÜbšÂb>÷Üóô”wÜù½q:À—~¨PäNؽrÄ𨰺„üNéƒg­ "K¸ ÄôœÚ`*ˆÙßßßÓÓk]ò:ÿ|)ƒW¼VÂã¾tìgÀ—€/_:^/À—Å*À—9Ç—‚Ô=L[ ¹m Ø|8ÉÁÁcg·ÇQ-ÍÍÖÜ>¾ùôÞgæææææææççæçõE×ÂÂÂ"£%6íORÓöü’ÌùÑüDÄN¦ÕìKww`éáK€³2¢)}xßz`â Bâô;Þ$æ°(ršü‚˜ýý=½}Ö%Üs$À—^+áŽq_:ö3àKÀ—€/Ú€ IDAT¯—#¾$`çS˜|YØÖ—»w?òÈ#ÿËnin^Óµ}«¿øR—bNNN¾´o¿¯½M‚èmÀ—¾=e 0 &äoJÇ ‘û›Ž¢É¾2¿ì®®mŽmöÜìôÐÃÃÃÏñ,óöÈhËæOšÂbRˆ9yb2ˆnñ¥·_úö”B& ˜ d–¿)}“/¹‡›Ž*Dˆ)ÓfÚìP…òÌbTøÒk%Ü1.àKÇ~| øð¥ãõ|Y¬|YÀørxxØ_Þ²esbjYsûLNN¾øâ¾ãÇ‹w¤mÊ,v鉬Y}Ä'øÒcµðÞ. Á.æýPpò=¥O"‘p ¢ÀDǃ¢ò¡gßfˆ¶|DžF…€/½VÂã¾tìgÀ—€/_:^/À— éŸ,ŸàK©Å¾$©¨–ĺ°’Ñ—cdžzh· _nÙüÉ[¶lβóåe› üùç_:}úôÜÜÜ‚®ù…ùùù¹´—–2 3¹´”L¦-iXRÒ,¡.5ØEµ¼¢ˆð]øT°‚ @6ò=¥˜Š¢``¢ãACˆ b¾”Á+^+áŽq_:ö3àKÀ—€/¯—|é@@…-À—Až²; ïÞýˆiã–ÍŸliiβ%nÕÒÜܵ}«),æ+¯¼úò˯¸íÐmÄ8ð%(L‚ @6ò=¥r¢¢¥—IM^4³³³#ïSŸGŽ€/_Ú—| øð¥ü…Á— âàË€OÙÅ^û÷¿lJ;N_æ_ê²æö™ššÚ¿ÿeùËê_”}½€/A. ˜ d/ßSú8‚¹€R„Ë@Ì ài6¢}嘠42b“·'ûÎ÷EÖå'Nœxä‘? ÌÏÏ/0š7ha!½,..éËÒâÒÒR’.4ü¥®d’nÔ’IvÉI„Å è.| ʱ€`‚@ W¾§ôAN¦bjr½»¤ fžøR¯x­„;Æ|éØÏ€/_¾t¼^žñ%Ì‹M€/ƒ¯¾úêÔäT.oK™Nðp˾…S@0A H$ßSú „Ä0ÑuTqC̼6ð%’€^+áŽq_:ö3àKÀ—€/¯àKôO–Oðb_Š5<<"“·å_RÙBÌýû_>qâDnnKÀ—¾=e L)ˆ”>޾ä;vvt.b¿x˜{–’‡˜¹ŸòøTÆÍœßsålyÀ—€/_:_ À—€/_–„_†ä” _RÙ&(ißþ}/¾ôð¥oO¨pä{J$äJP#ö‹G¡LM.Ùcy™òøTÆÍœßsålyÀ—€/_:_ À—€/_–„_z9e6’%?ö¥Æ†jÌ!l<ÇtpLòàƒ›ðesóš¿éÚf|©ËšÛgrrrß‹/%“É%FI)iIMKE¾Ô¸±/âÝrì¥Êæ.-t| ˆ³ œå{JääŸ\~p™Ôä¡…˜ŽŽðögÔ?¯i‘×Y àK$)\UøÒÕÍøð%àK‡w¦+ÇXPhø2ÈSF,»4V•Y¨zh÷ð°sÞJ|Ie 1÷¿´ßŸÈÀ)™Å¤ôµÔ43©´¢I°¾ôíI…I@0A ÈYA¤ôqôé.?¸ Ä ˆŸf©ÿÏÞ»GÇQÝù¾¿ª–dIÆø%K–¬–MnƒM @[&œ$“d–nÈ–3™L8™dfeŒX3ÉÉ ¹“ÌÌ箕¹k.!“3’Ø0 ‰,†WNÀÛ<ìÄ`Ó‚€%Ù’llK2~ÉV×¾TwÕ®êzw=»¿ŸÕ!¥êzìÚõè]ïýû9‰æYL$úÒëFL۸З¶õ } } }i{¾üÒ— ñ@_|ÈŽÖ=ôÌ3ÏéÒŽ·¯Xž,})Sœ |bâøóÏ¿066îãid,Kú$LpD)}lÇt•Òúp(HZ"Þ$fø¯E^ßR /ɤpµAèKWô%ô%ô%ôeE}‡C}öY½¾ìZ}k{û _J>&¹}v»”˜%DèKßîD?`0ÀA¤ô!» ›Áõ…LnjrJ€Ä„¾ôºÓ6.ô¥m=C_B_B_Úž/èKàø'Ë'ùR>úÒ<Þ¥nyÃØ—òÐg%˜£ørddôé§ŸÕmÁ0o%D_ÊÈS7¢|÷îÝï½·Ê›°˜ê''IÊGE3ÀÜ<\¦îÊtÓÝ• } ‚œDJ"²–qÁõ…L´Ä$¢ø%öqøAîÞù=oœ_úúúÒþ\@_B_ºÑ— ïÃIú2¸C–´îR»1 O?ý¬N_¦Ó­e /eÚÒéâ°˜û÷ïß¹s—k){J-Æ©{ÿ4s4ú$‹*T8§³c•…©ìéyÀÚr"÷îÌšÇjìY¿áG=Äáȱ8­Ýkoo_ww&’ÚîíÝìÙ GõZäõ-ú’H W„¾tu±A_B_B_B_VЗq8äâÀ—fy{(úR¡kõ­7=64t@™311±{÷ë—_~YÇèaЗ q &¸ ˆ”>d×»“¼¦;/ýˆ(Æ©ÉcøZäµ™ }I$…« B_ººØ /¡/¡/¡/+ãwú2‡üÌ3ÏV‚¾”éZ}ëŠËù9²Ä/éjñãDC_‚$ƒ î"¥Ù%÷ìFíÚAjrHÌ’_"ÈÝ;¿çóËC_B_B_ÚŸ èKèKÏúïÆ‰ú†¾t{È_’ãØ—Œ±‘‘ÑÿüÏGFGõy{ÊU_Ê´¯X^” |âõ×ß8räèÔÔÔ¹©sSSSS9ëP˜…áå&äôQ1ug!?Ò\Sóˆ3 ˆ } b &¸# ”>ò€n‹ IéDb”S¨<^‹¼¾¥@_’IájƒÐ—®.6èKèKèKèKà§|©ðØ—ES}æ§y{¨\ô¥Œa‚òßýîwGŽÍMåò'11uJX–ÄoŠ%GvÓÊr%C_‚ÐÁ×”ÒÇv@·çžÎö¾Êzàr %ýÇë[ ô¥«†5ô¥ËÂ@_ÚŸ èKèKèK`ñ‹ }éÛ!ïÞýú³Ï>ÇϱÈÛCå¥/e ”¿ñÆ¥œÄ`.èK_`0À 6ý%½Ž¹¶Öˆv„Lzjò¨^p¼¾¥@_’IájƒÐ—®.6èKèKèKèKà§|¾4ãé§Ÿyýõ7ø9ràËÊÑ—2† Êu•ãü$B_z[¡Œ &x! ”>¶1ÐŽå*1[öl-åÇë[ ô%9®6}éêbƒ¾„¾„¾„¾~ÊèK3ž~ú]àK‹¼=T¾úR¡Xbʹ}ÜžDèKoË@_&*Tx£³sUÏú fßöööuwgÒç&*ô%Œ"ï”Ò‡ˆ¬Gs{¥îü¸lS“{ŽõYi/C>· Í–/#}¹eë·Úûÿóÿ·å¥Ð—q/ôeG=xpb뫃>±ç{?Øú⫝̸å4Ú)ô%ô%(ñúÒŸC}úiiÇ©Âô¥L[:½zõͺJxã=ãþžiGu }  &”D@)}äžV[6Àî N$fpi…ÊæeÈç¶¡Ùòe¡/yq908œ¸W‘J,ôeˆG½õÕ¡GŸØû½Ûö⫃• }É…¾Ä;qYýbC_úsÈO=õŒN_¦Ó­÷u¯ƒ¾,žÕ–Nwß{·S©„Ý¿ßýî»ïúu¦¡/ñ¨.3`0 $JéCbн ­£SÀi…ÊàeÈç¶¡ÙòÉ×—[¶îL”¸ôVÕeW0èˈŽZV™?yb×%úú”ø‹]ñ±/•ùò9I’$&˜2%I9Žÿþï§]åí¡ Ö— ]«oI·jrû¼÷î{;_{mròÌäí‡cŠãÇÔÔT.')î”1þD+“’dWpFLbÖ§ ú„ &”еéëíí hËA÷‚,×Ôäá¼ ¹´ª/wYN¯"Q0è˨zðàñGŸxó'OìÕ -‡¾„¾^~±ÑûÒ‡’ŒŽŽ>ýô3‡æg¶¯X}éäg¢«ë–¢åÇ÷¼±§´½2_ìží9‚¾aƒ ¥b=â»Ï(g·X è^˜ÞÞ \ÚJÔ—û&Ó]ô%ôe|Žºà1ß´| @_B_‹_lèK‡‡lµÖèèè3Ï<{è¦÷e×ê[-ÒŽô¥–5]·v­¾…Ÿ31q|Ë–ÇÆÆ<í•ùrUB_‚¸ƒ >\JëQêTZO'ØZÔʬ—!—ÍÖŠÓ—û†þé¯ùYoy¿Š”mÁ /ãwÔƒïßv¼øê£‚A_§/ñr\¿ØÐ—>²¬/u3­óöô¥mmiÄ$¢ßÿ~·[‰ }IЗåKª|¡³c•…©ìéyÀÚrZÐݽîËw}Åì[¹gggG€‡ÖÙ‘Íögûû-ÊPÊ–ÓËËfk%êËG~ök_*»iÁ¼¦–yÜ”B3ùÞÜeÚ5xÉ&Vhþ2候‘VQ¦X¾ÅÌÔ¦s᦯>Ƶ°Õõ•)ƸºbÚ?5­s¦¶Ñyý¤Ù¾î+ÃR¿n×êRù9âI)uJ˜;79vNWžéÓ§ÑõíWsEfš#c[ÄøâsºG3MÚ-(Ǫ½Èõ«0î|éÏ‘vÆŸfµM¥Ì|ñ5S¼WryÙÇÏø`Òðzk=¯ºõ¼í)âÊÌèå÷Ï”~Gl}íÀÀÁãkoºØð~„¾4.ôeEÿbC_Ú//1f¸G¥*~ÿû×wï~ÿ*nm_±úÒÛSB–˜7=ÎÏüýïw/[¶tæÌ™NêMQÊwSE1•JåK#Š|cAÕ®l’¤N‹¢àðA_‚H€Á°Ö|rJŸL&ãmãÝ÷Þc‘|¼ws_&³ØóÆ {]OÏ˜Ž›KЗm܇Ú;0xÐCÕÎ_ÐØÜÚHŒ.»z©Òr•$©°K&1‰1u˜›,Ø–^µ˜ÄæûÃGaDGFŽ9¶ä£fª¥b…ˆöJy ¢+?Å-£|‘/UX‚× …u$&)ºJÒl“å·©¾ª²L•‚’Á*ÿÉ”ÙÊ2\I‰x¥Æè/l“IŒøCâKH¼ØOQêTн{ÊÄ`²ëÛ?ÆäãÊïAÉ ÀˆI…±†ò‰Û°ÄiAµò”-0~kDê6Õ#S÷ÈxsÊ4þ”1͸å5[PO”ƽò…W S˜©^ú]È3w`DÔzþ4ùÛåÍ3 Û”KEꎔˇò×Ô5ó¦)û:prêÀ©½rô¬Û;epxâ{ÿïËw|þ’… ·¾„¾þÊľ,æ©§žq›·‡ /íhkKß×}ÏÏ7>64¤ô¬§½{ß<ÿüó¯¼ò Ûº˜Pú} b &øFgç*+ÏØÛ×ÝíQ2Ê¡6-bÏú ?zèÁ@Î‰Ä º7hœ_†\6[+K_îÞòÒN·úòŠk.¿ ©¹uq=AêKÎkhž=§y#ú°jý /õýO Õbª„úÒP_ÑòÖ™\Qɹ¾äª‚ˆ±ÖúTk}Š»vN½|ôìÓÒÁÓ’óüÑ_ýáº-øøÕ­Eï™Ð—ЗÀ³ËCìK=Зþµñ XÓu«Nb?~ü÷¿ßýÑ^^RÝB_â)žp|#¸”>dj“ˆzzú­s£Sð™…bû2ä²YTqúò‘Ÿ¹è}ÙÜÚtåµ¹ëooÿè5—ú¢/e¦.}i¢/óFÎúÚ‚¾$}©-*yÖ—šM±kæTÿÙ‚š/´T_3;åüÞÜúÚÁŸ<ñ‚¾ V_2¼'óú²ÔCùßÿû¾ìZ}+ô¥¿zlM×­ºåããã¿ÿýnïu }Ixl'Lð“àRú‘uZpy z G‡ÔävÍ%èK}WÖ— ÜÜÚÔqóg:nþô×\ªØ=èËPõ%³¼º´òú2h}Y8ÿ¬µV¼fvêëT_3Ktx KL#ïfû€¾tqD Q¿ØÐ—TôÔbºå%¦À/%#ŒŒ<õÔÓºÙæí!èKO'(—%f.—›Ò’SrR&1eZÊ©ó%‰;Éšs,iOºþ æטϵ} œƒ >c¹»”ž’¶iÁC°‡˜æÍ%èKý‹Šs}Ù’w—Ÿinmäíôe¸½/³Ö—DЗáëKR'Ø5³Ä¯/L]=Spò@þàÑ_탾„¾ž Zeƾ䥖Î_A_†¯ÇŠ”ïÞýzÎ)'1I¶ÍL–˜LbRN™Ì ?Äœ_íÞúC__ÁŸéìì°K^ŠÝ³Þ8õöö}€¶"5œbÄáeÈe³úÒ䪾ù³7¶¹µ‰”ЗQèKîô1Ë÷èËhô¥R¤kf 75 ¦Ù?‡?xôWo9–t} } \‰OÅuã >±/Ÿ~úYþÏtºõ¾îuЗA?Š%æñãÇß{o¿W‚#}@­@_·À`€ÿXÇ‹,ÑîYo¼Äh›ŽË`#RåÔäôšãÓ©"ôeKkÓ_Ý}gsk“"¡/#Õ—JY¾(@_Fª/å‰ÖúÂ<º©Áþ)P˜Ð—~ëK†8˜åý»ŽØ—zž~úþO'y{úÒ'Š%æþýûÇÆÆ}©èK `0ÀMéc;Ž;œt:ÝÝë 17‹ / ¸êÚË:où¬â"¡/•Õ)"}i÷ÊàC_*·ODúRùªu»©-¨qÒ3k÷€¾„¾.ÄŠ'âºqµ¾dʰ㑑Q>uôeøzÌPbŒ"—$ÝŸ|XLî#É)§‰‰©‰IÌûñA_‚`€Á€ ŸÅ·%¦ô±6¤Ö n'3„¡±o¶V–¾$¢-/í´-pçÍŸ½jùeЗñÒ—êBfõ: }I±Ð—òvZ«Ùæ:’˜?€¾„¾¥K¯Åuã ½/Õ`‰»wkR`C_F¢ÇÚÚÒí+–+Ž;vL§)•?sæ©{Šgó'ÝûñA_‚À€Á€ .¥ÙÒÐú?Zi§°:„ƸÙZqúòáŸö ´¹lnþlKº ú2núRõbV×ôeŒô¥²›æH ªmnêŸþ:;pðOèKÓðN\Æ: úÒ]̮շQ’2múL{ûrþω‰ãžëß{K°´µ /A)À`@PšÒ‡ˆ¬Ç’—¾}' 5¹¯M§ä÷¾Üºú2áúÒöꀾŒ¾”ëä¦9S ª%ë“·õµa§OèKÓðN\Æ¿ÙЗÆìÞýº2N·"uO´zŒï†944ä­þ™ÏèéŽðá’ƒ¾À`@€šÒÇv,yÏú !#$¦OM§ÄëËýÃ[^zÍæŽ€¾T^ â§/™ýkôeLõ¥<ó¦Y6spø“ /¡/[—çÞ£¸n„¯/uÈ™öéKD|Ìö+|/I´ñüD× sl|wTû²äÛ /AHÀ`@°šÒ‡ìºy†–KÇzÈ¡±m/Ÿt}¹ÕJ_¶´6]µü2èËäèKfßô‡¾Œ±¾”Wÿüyg,NãÖ£EgúÒlèË2þ͆¾ô©&#²B7=¶mûŽ ÷24t`ӦǷmß^þ7D7ô%ðLƒ@SúÝXõ0½¡õ‘Rˆ}Bcßò+}IDÖ0;où,ôe™èKõŠ‚¾äôVüô%[P•ûXíY‹Ó¨é†©¾d…Cþ#1îT(HjމÏ;¡™V2Kh—‘Œ•ïH·€Í2/äL“êBŸ …™ãÜi@…§^Ñ¡/ÍW‘ø–yÅHò‡IR.'Ùî(B}94t ´ÝmßþòÆMÇðJ–rÒ¹©sÜgJùLå¦rüGÊ)Ió‘rR®0ÅÔĬ/èKƒ åIÐ)}ˆÈ:!xhÞ°RS“W°¾´ì€Ùy3ôe3èËDëKùÏ«k&[RS¦Sé†}ïK·"ÉMa$»WY“^–šÞ—̧'?ˆÏo6z_úT“QX¡Á¡¡žõBÔ—ùc:°qÓã…å•};@_‚ˆ¨B@8tw¯ûò]_1û¶ws_ggG)Û—%i¶¿ßlžõ~ôЃ!©,1{Öo°X¦gý†î{ïÉd2eÒöó¶|òõ%YvÀlimjnm²Ö—Ì]‚‡>òþÿs·bE¯žvöW§L[û[_¹îª&“?ÚÁãIЗLÂÅ–ØßlèK¿Ê¾Üj_HÍ1 Ø´é¿V¯¾Ù{.¸Ý ú$ ôÁ€ð4¥Ù%÷e±Í/D!Fç ³iëbùòЗ–0¯¼ö2ÛÞ—ßßG•î™ Rç,»aŽ˜ÜøÐ—Ð—å{@_–PQ|÷ÃHôå¶í;"Ô— ›6ýW´=1ýŠþ }  &„GÐ)}Èn,¹/»ðå`©LR“W´¾$¢ýƒÃf;hnmjnmr2xàëcI3ºüêê37ûÀð‰¢ߣ¾dÄdd|H“à?¶Þpãܲ&±,5‹HÅ×ïT]F2‰q)q…׿LñLw±,™Ctñ.ì‘yÌÇ¡äP®7ô¥³KARBs0ÖÔØ¨|§ ÜŽD_nÜôØöàóö8<›6ý×ÆÑ„ÅÔÈÓyó¦¦rÊ'Wˆu™ËMMM©A1u!15ÏLIý˜?˜Ã›ú &„JÐ)}l;?†ƒÒIjò$K̊חÃæóªk/ƒ¾ ¢‡+„Z¥qªEœ2»Ù·îõK_:x¹Ý³{¦©Ë0_2»> ¬ßlèK/+ÍŸÏÇš mÛ¶‡¯/‡†BÎÛCöŠ  ý|ãcá_É|ÌéÓ§{»ÚÑû$L•RúØv~ sø¶‰Zžôˆ_|xŽ‘¾$¢›˜ÎR÷ü}بúRþßǪO›Ýìƒù>˜O’¨ô¥í3-r}‰X%ÜJžìýÝjdúRæ²\ªLo µ$Q!ðeÜô¥<14tàçÓuŠ ¶6‡øª04˜Eé¾qÂÆ:Zeïfô¢uOÏ¥¡ua(Ä<é¾Ù½ —ÒžŽF_’eŸ+¯ýˆÃÌãx Äk¦¢/‰hAjªEf§Ñ¡*är¹\.'YPˆmWÚ%dS…³Üo"èËÂI'žŽÊ IDATƈ5ÍoäMÙöí;ÂÉc³qÓc:}Y[[;þüºÚºÀÛlæÔÖÕ6Ío,*êãÛ¶.v¾Q3”¾Q QªÆæåŸÅr5ÏHãØ—^.¨déKd’L40˜!¤ô±®NáŽ%¯`‰Y¶úÒ" ù×\êP_âßÈðÿ¡S¤/‰±¥N›Ýì[w2z’Ø÷IdŽzhÚ>²¼Ç¾tÒ‰’yOqÎÏ–Jx€ƒ$ÞBžìýÝjô½/.½tÿguŠ_ÖÖÖ67ϯ«« ¼Íf~:X¡$ µéº‚nÛ¾#а˜ÅãÇu0‹æÚ€¾aƒ ÑtJ²®rÛC®T ç5ÈŲëöZ´½/­²ÝÎ_ÐäP_¢ @½¾”oÇáœã¿¨ô¥í3-Vú¯r½}<ÿØû»ÕéKÆhÞ¼yK—^¢Ì:°qSP¶nph¨gýC}F›Íñé˜?¿I'1å°˜AnÛ¶µÝQ¹˜[‚Зhv–0˜ !¤ô!"ëž¾tötŽmŠ¡„§&7ij•§¾¤-/í2Û_së ¢Z /A$À`@”Ò‡Há‰fªô ^ƒŒšSe¥/÷ ›í²¹µÑ¹¾d„ @ !½¾dŒµiÌá“Ew½›Á㌘âÍ4ZOɨ#§¨‘ŒrHeÔaÅI]̉…ÌIyZ4­Ý‘d´G&I9íœBJnyÛL>áw¤ÿSÒ¤$âé¶ 1¤^.ƒ,=ôˆ†¾tFccãÿøÄõº™›6=^zvòÁ¡¡âÀ—uuu]t¡ª/EQžW–I§[ýi³9C Ѝ««›_ÔEtÛö¥tÆ”ÝåÆMé˶éÓ§ ‚@òG.\°‚¾,|'$@_ôeyƒ QBJ²‹¹¾1´.…›*Ý÷× £æT¹õ¾45˜óóYÈéK´(à!d /óÝ0ɸæàÈI¿b_2“‡†Éíîrð8³©RWG]vúRÓ^t¢/Ñ €`EúÒµÙ0Œ}©ì€©OV4_ žÉÔuµ!6%åÁ"=7z‘* ™«Ô=ä$Õñ“1óÇ Ó­—…i„ü”‡{+s”i"ÊårÓòfAAÙ@$æ3Æ”µcR.'ÿ)B.'1Ir}Õ0~?<^’W)O}© ¯(Âu×]÷Ö[oíÛ÷–nÁ¡¡CC¶oßAD+L<ævóÞšõõõsçέ«¯ÓìŽ?š|h³•€@‚R-JxιsçL¯¯?vlìôéÓ†5ãaG ódŸËGÕDÕÎW¾’’žA¼\„З r`0 zº»×}ù®¯˜}Û»¹¯tƒi»¿T©sl{†Rè^Õ‡¦peèËýæ£È]éËÊ4˜9R©TUUU]]ÝŒ3ššš.¹ä’›o¾yÕªU¾§-Ðñî»ïþíßþíæÍ›mK 霌Ǒ¡¾´™¸?™Ç].ÃMKÌ4â$c6S3ÓÌWŽEÊårª‰ Iƒ)‚ºÆr¹\þOAÈI9æ%“rË3c·Q†7 ô¥ÍEFB±>»øâ‹/¾øâ^ØrìØ1ímw9®¼¡¡¡a^ƒu±üø*a ‚`¨AÅ|€¾úéõÓ§O?uêÔѣNJ=¦Ë ™;¯QÍÿ. ¢òLVv§û¥–C…r…,Πóö A_‚x€8˜ BHéCDÝ÷ÞcñmøÑ'­Ã€RbR“W\ïËÁÃùM æôe är¹ÉÉÉñññ¡¡¡;w>òÈ#×]wÝÁƒÚã™3gî¿ÿþ¥K—öõõ¡þË3}ÉX ›4[ipø„±,(¡Ã£¹Ut20<îú#É~›@_z;D®¾æcW_ý± /üp)'d^㼥˖ëË€ÚlS__ŸN·¶¶.hh˜ë~ݺ¶¶ô’‹3Åb/æ"]NЗ `0 „“ÒG±n±€õ°î p’š<Þ³›–dþ‚F—úML{¶mÛÖÞÞ>::êû–ûúú–.]úï|çÌ™3¨ç2ÂX_±4éêÍúú²\o‘~ìýÝjˆú’XɇhZþ9sç\xÑ…ú¹Ï.Z´pÑ¢…ÎWojj\tÁ¢¥Ë–666†Úf ‹úúú†††%K2K–dæ64Ì­×çkhhhkk[rq¦ma[=òÒÅI ¬ÚX©ô%ðŒ"€¸BJ²KND==tw¯ óÀ»»×õô<`£3üî®›ÂЗÜfëKtÃtÈÀÀÀŸÿùŸ?óÌ3þn¶££u[~˜éËü 'ØËõ%P+Íb_Jš8˜Œ1uƸdŒ”¸–’Ä c_2. ¥ö¡Ë¢ÃŒÞT¦9ˆ})¹|ç§s9Ó‡˜•2•J)Ñ*s…P•ù?§¦”é©7=5Åtãx™ú•ÿ‰H¯ªlM„œ$IòŸ‚›ÊIórÙvÉ4c‰Ÿ ï~ìýÝj¬ô¥=üÃCm̘­Uíõ¡Hùï{ï¾ÇEQPcG0Ʀ××O?ozá+ÉQåpAvKm³ù?F[DÓñÚb~z^ã<ŒƒWêªW»” Ÿ9.¦¸éü|Fò(ò‘E."÷¥%–,} Å™h`0 .„“Ò‡ˆ¬SèÈý=CΟcmo)ôléîšÂЗú%œêK HîtH’tîܹ'N ÿêW¿úþ÷¿?66¦,öì³Ï>ÿüóŸøÄ'PcÀÁMh¢/ü „Ñ ž¾ÃãS[†žÚ2„š&¢ª*ã—©m;^IâucèRÊì[bÕø·ÕêKòPù#7ˆ¨(ÿÉ Ž×j)1¥L/Z´HñìSœÁ—¤o-Í‚3ò"^SW›?í Á$ö%orùºbÄ4Ú‘³½ƒ©ùw *–¡r hª:•â¦Eå_¨DQ-•ÈÛLAP¦•ªfŒiOlÑ¡¶÷ úøF‘@Œ°îühôÆ9r ‹zÖo9 ¦œšÜz™ðKåþu¢Òõ¥¼˜}‰v$×&ÅiÓ¦Í;÷ÒK/ýÖ·¾µmÛ¶¹s5Á³}ôQÔpæ+Lô%sôq6x€ÄÝЗ>•$ÂïHõ¥ï•ï×Õâñ$Ú ANô%𹵌*€XNJÛ:½½a§õH¤Ä¬P}iQ*èK߸øâ‹¿ùÍoòs^yåT pú\2Ö—n_𘛧ew3¹j¸¸G®/ƒùÇ ÆÊQSæú2 Í2>Š„×} |âE8)}ˆ¨³ÓJ•ʃÖC>vÛΡ…ZuÖœ‚¾ÌD—.ktq*‡†òãv>\]]-hii1«IÆXKK‹²duuõûï¿OEãeë‚ýö·¿]»ví¢E‹jkk›šš>ñ‰Oüð‡?}Zžhllüô§?ýÔSOÉŽŒŒ¼òÊ+×^{mñ^~ùå‘‘åÏ?ýÓ?7o^)Eúàƒþâ/þâ¿ø…2çðáÇ~á…~ðƒ<ùä“mmm†+J’ôàƒÞÿý‡æçŸ8qbß¾}ûöí{ôÑG[ZZþå_þeíÚµ¶Å$é»ßýî÷¾÷=^V=zô7¿ùÍo~ó›õë×÷öö^xá…•ë]Ìô¥ågßûR½“qw–J\BA”H}"sÔÙE(ÓÛ¨4©B~‹®ã©ükñÿ8e–ÁH’(*› å—¿¹mñÑ`s’š®J1WȘ¥\JY†ƒiš×KûbFËøÝi#–òá5Q$V¸T2ûMèKèƒ ±CNécö­½#­wDuxìî^g]*Ybš~›íïÝÜ'zÖoøò]_¹ëþÕ]ÿó¯üŠ"Zrû¸lõ%¿ô¥/ðò‘ˆ•é;ÿêW¿ú•átóï¼ó΋ô‰O|‚×—<{÷î½á†¦¸ Ë 'NœèèèøÚ×¾¦Ó—:†‡‡ï¼óÎ;ï¼Ó¶;gWW×·¿ým³¾–ûöí»ñÆ'''+ôºñ¤/ô¾Ä *årr“y¹5ãÓû2¬òûqŒ^ =±˜?Ar"k"1Ê} ‚âH8)}lwdí =|[‰éVãZo0À†ž£å¥/™MÙœëKHLk~ùË_ò.X°@™þüç?Þyç)šÌ_ÿú×Êô¬Y³tÃÒ=°k×.‹oß|óÍŸüä'º™¹\îÏþìÏþû¿ÿÛá.~ò“ŸÜqÇÖׯã?n½‘þþþÿøÇ•zá˜ëKæîfÇ *òþ±[ú2ÌÓ‘¨Áã~yǨ½HÝâ &Ä”pRú‘uþ#oº;üN»€˜›û\,“ñÝ`B_ïѾDËÒ”7ß|óÿñù97Üpƒ2]__ÓM7©÷i6ûÖ[oé¶°oß>þ¹å–[jkkÕÓTt±1‹‚]tÑE¿øÅ/Ž;666öÿñuuuü·O<ñ„nù 6<ûì³üœ¥K—>òÈ#<{öìððð£>ºlÙ2~Ç{ì?øm}èCúå/ùÁLLLlذgX’JÁB_2ææf7» $ܹ lïžR¥ 9ø-vºŽ§ò£/ ú2¨SØf©5èK90˜SBKéc;–Ü"Vfp$25¹—öZ™éKr¥/ÑÃKw¾&''GGG_~ùåûï¿ÿšk®™˜˜P¾M¥R·ß~;¿¼n y±°ÓÍq_Ò–ÆÆÆ—^zé _øÂìÙ³gÍšõ¥/}éŸþéŸøöîÝËÿ9>>þÝï~—ŸÓÕÕõ»ßýníÚµ---ÕÕÕÍÍÍ·ß~û®]»V¯^Í/ö­o}kllÌ¢$---Û·o¿é¦›Î;ï¼óÏ?ÿî»ï¾ûî»ùöíÛW©•¤/#Ƙ$ås1Úf,‡Û¶,‘$&ÌN¯ I¦%ÿásLÐÿßiwvÆæ·ØÅmì¾üõ¥æd êßÊùHE1•R?URʧZKMiÓ¦Õ¨«««¯¯¯+PË1­všúáVÑQÚi,ÍžBJ•BJSU)ùSUU¥­õ3­fšòáµvÚ´ÚÚü§®¶Vó©ã?juÕ8 ººš;I)QStAùȧY( Ì)hïVþú1|@_‚€Á€øbÝÑÇ •¶£¶#K^F³"ô%·g§úíKþe^ÅÚÚÚæææåË—ç;ß9yò$¿Ø½÷Þ«KMó©O}ª¹¹Yù³x 9?ç‚ .X¹reé¥ý»¿û;>'}îsŸãÿóÝޢѩS§ø?7oÖ<7î¾ûîiÓ¦nyÚ´iºN”J²uC®¿þzÝ]ô3gÎTèåb¥/Y OÄÇe}ÓX/€Ø—>ÖvÙé˸\¦O‡Ë$K_â,ÑÀ`@¬ -¥­+twÒ/¬GÓSQº¡l¬ fEéKÙ™@_úIMMÍ?üÃ?<ñĆâHÎãóöüú׿毄ҳË\pÁº9õõõüŸ¹\Žÿóõ×_çÿüä'?i±qÝ·o¼ñ†Å­­­º9çŸ~(/ÉñÇT_Úçwêú”Ý c·@EêK‚¾ô£ò£=>_® èK+ªPs:;VY˜Êžž¬-§‹uvd³ý°··¯»;~ tw¯ëéyÀ¢`²Äô«‚oo–§¾äǹ¾DCRƒ(ŠÓ¦M›1cƼyó.¸à‚ë®»nÍš5étÚlù+®¸ââ‹/V>>ñÄ_ýêW•ie±åË—ëF {Fg ‰(•JY,¯TΧS/F÷íûï¿ïª$ÕÕÕ¸„òw•©¾dV÷Ñãˆ1RË11QÁe‰ÒE[Q O$AuZù‡•º®(šl3^a.½y•ÊÓ—B~  B~}IøÎOü9OU©û˜Æñœ$I’TX]ýñ—$IbjH ©J2¬7~>Åœ²M7§ÑÏ[E(Ü‚(¤Ä”òS¨üŠ)‘ÿe¬ªª2üâo(ÝÍ¢N $‚rìü2U)u³bJÔPT7«ÞÂbŠ;m©TJÙ¬(¦¸ÛÙÝõ} Ân*£ æ„–Ò‡ìº|F5–œDê´.[‡ÝPô_‹ÊW_r/õ%li3€çr¹S§N:thïÞ½›7o¾ï¾û,ô¥ ß ó…^“ÿŒoÙ²E™ïKŸâ7±âªb\YEÝõ`½åâ`šñw%¡]RùÿëKf~#›Þ‹RIR€8ß+În&Ou·­ëòäkX@õÑi ê·;œ=#ö%A_LH¡¥ô!"ëä9Q%·­„hËæ¸½Vú’Y·‰ëK4&KçöÛoWÌݹsçž|òI"zòÉ'Ï;'Ϭ©©Ñ¥ù“yóæñò‘:‹9pàÿ§.QøJ›5C}ÉÜêKÃGn\Pw‰ƒÛÈÓÍ}é­¾“õˆEìKèK 0˜ÂLéc½/"êY¿!ªJp’š<ƯE /É­¾D‹²T.\È'—óóYÈo¼ñÆ9sæDU¼K.¹„ÿó·¿ý­ÅÂÏ?ÿ<ÿçå—_ŽóëñÝÔL_>yLovIýú”Ù]âà6òtó%=öe0õRnX–è‹ú$ÄÁ€dÐݽîËw}ÅìÛÞÍ}áì‹| ¾é YbÆCSºm¯•¿¾,|íB_¢¦/ÜqÇ[·n•§Ÿzꩉ‰‰§Ÿ~ZùÖ"‡]‹ˆ$IEŸÿmûSŸú_˜ïÿûwÞy§aV¢ÉÉÉïÿûüœU«Váäz{½ænDÒèKfýlÐéË¢GVaâÃm3þdÅ|åû\Ž iÇ$&)áê˜Ä…®ãóårª•¤â4AL÷¤dÄ,Këò¥™¹xNš¿³áÃçž2ü®¥y>‰¢¨ÜS)1Ňƫ®Q,ðÁø@ ¢( ‚(LMèImh<.È©Zwºܼˆ¢¨tjE.ÌŸ @™6ŠÓg>XÃ}Y8›DŒ”‹A÷ó¡.ÍÇs$Q „V¸€YaAI0\q)r}­˜Ä”°˜¢q,Ô¨±|YeZD³ ±ütŠ>ÉÇÁH„üéÐV»“ÍŠÜ­ª‰ƒijÓ¯ëÇý“úxCh)}ˆÈZÊÁ73™²úÈ9Ó}LÂÊkQEèKîÝÙ©¾D³Òn¹å–¯ýë“““Dtâĉ¿ÿû¿?qâ„üÕܹso¸á³«««Ïž=«üyúôééÓ§û[¶5kÖ|ó›ßTö²oß¾/~ñ‹?ü°.ŠåÙ³g¿øÅ/òQ æÎ{Ûm·áä–ð(2Ô—ÌüÙ`§/9>ÜvÞ–:e³çÔôS¹\Ah²©)IÎIk)ѹs’²ÓsS’²#IR;~òOþ)¬_nÂÌúYÇ,W<;9ùÞФáþæÎ™MDUUUÕUÕÊíÆ_öõÓëÕézuº®®N©T*U%—!%ª¹AA‘:Œ‹É§Ô•l0µÿJÁ8AüŽÔùœuå ¬À™MÙ†ª63dé }Y\í|HGsfL ¡hµüÀÿ‹ÿO_‚vaARoç‚Ü$Ž×\¢9ɰ´|ò£L>á!pUÇkG~ZÔþ{å§YÑ}§Ý_ü™Ò%ÿQç›fò1(­²(´X gý†èbvXsиysöºÓW¦Èõeá-Å…¾DÓÒfÏžÍkÊ|P™^½zuqÆ…3fð>üðÃgΜٽ{÷Ë/¿ìWÙ,Xðµ¯}Ÿ³qãÆ+¯¼òÑG9wîÜèèèÏ~ö³«®ºjãÆübÿú¯ÿzÞyçáäz}™éKæàž¶Ñ—Œßsù„á7î=Èf,ô%Hề行ÁãacD[µ"nU¦9ôUÈ"|Â`ð8ˆ=0˜$ÂLéc+ ýÝ+lS“ëÞxB_š“îô%Z—þÀg$ç/ ë,ä^x!ÿç_ÿõ_×ÕÕ}ô£ý÷ÿwËö½ï}ïŠ+®àçìÝ»wíÚµ---555ÍÍÍ·ß~ûž={øþò/ÿòK_úN«÷|3}iï mYÑèK=¾>ÿN—è;’®/KRæñHœT[[:¼«0¨_…¡®*ú F‘@’Óìdûû ¿•Súø³³s•õXrw§£·w³2Z<³xq&³X[‹å28«·@ &ô¥¦Aé\_b¹_Üxã³fÍçg.^¼øÚk¯µXësŸûÜ+¯¼R<ÿÝwßõ±lõõõÏ<óÌM7ÝôÒK/9Yþ«_ýê¿ýۿ᜖ö†o¢/™õ=틾tû$q /)žú2äÇ—`ý¥ÀݲÅcAEÑxˆ·ÈrÅ”2Š<%*£SS܈rJ`ìË’¥ 9ø-pO‘éKþ43frp× ùÍ ‚(ŠÊ*Ð@&ßnš1ìú½ç7ýå¦ })(󸘚ˆ“ÊÃÓ¬,úÍ ñ“[•¯³Í ‚ÀyÌJ¥­]bÌæSRsØÍ• } ¬Á€„fJÛÌ9½›û2™Å!ÄÌö÷;”•á¿õxs®7búÊ}ɕͩ¾dèÍäÓ¦M»å–[zè!~¦uL"úú׿¾iÓ¦}ûöéæûk0‰¨¡¡áùçŸÿÁ~ðÏÿüÏï¿ÿ¾Ùbmmmëׯ¿å–[pBK}(™éK‡÷œi¢C}É\.cø´¾´°%‚*H˜ë„ BA]0X‘x!ÒÔKé fáÏTJÍ>$¦R)Þ’8°™úÐ~q¾g /¯=å #bŒw‡ü rݫ˟n~û‚¤LÆL ¦&:'ñó%).—U‹…#Õ\íÚ ±rÆÆ F11•›… ÒïXÜ;êf‹þq¢xY¶zˆ}©yà9Y¦´+úØ‚Qä}É’%wÜqÇã?þÎ;ï@_úóX2Õ—¥äé]_š.Si½/AÀw ô¥Ûßú ÃÈÓ?^Ƹ¹àäJp0T?¬‹ÐîËoð8áI}0 ytvvd³ýcÉýMnÝë3ÛßïoôDyWoÎ^7bÚZ™¾Ìú2”WŽ"”üã2+V¬¸à‚ lך1cÆ·¿ýíoûÛÁ¾›¨©©¹ùæ›o¾ùæÈÞ+ã K¬¾dÚG‰ä©´ív… IDAT} \Ý.!=Æ+@_ú¢¨|;ÈØü Û.#wÏ\Žâ¶ß,Ù ‡¾I}0 ‘„™Ò‡ˆºï½Çâ[Ó 'ýmÈMKªÌôeÞ—8×—ðM~qæÌ™o~ó›üÛ˜ |BæúÒú޳՗ַ¾ü¶Ì˜ x&ˆ qŸ¤L“ ¢(¨0!ˆ‰"‰¢¨Îá?B~Œ¦ñ·úíͨ°Ëià j6ž?ÃŒ(¨Åͯ "~ÌHÿ•*|ø/ЍJUø?Œ'«ªR¦ð;å*q÷J@ÒÊ—=ÅY_ üG9õÚ [ä.zù®1ºfÍÉß4T|gİÅGÜ#ΨzŒî{µr¬îvã\_Uö<¥$Z|Ž4a‹£ÜñsD‹Á—Òß,TŠ‹})gQ°J}‹¬â¦uñ1ûÒå½})8q?‚ (A*¹ëB¤RdL´Ý”$å¸k^=Æœ¤þã”ËIêŸqEª»ð5÷š 2––êbbòq0ÕU”P›–Q)S¿Ô>þú±})›«Åã• } <ƒ É&ä”>DÔݽîËw}ÅìÛ öè½f2 ößê+R_]¿ò£[¶í¶xYr¨/á0Káå—_F%ã·:#}iñ"·òŠ9ÜÎõ[dfO7}õ¾dkëKç ¸3<­…Þ—KDͱ„‡ŸñÚûÒ‡+½/Ay€8˜xBNéCDÝ÷Þc½Çl6[ oC~4 “§/­yçõ7ú­KüEVn9^_îªkðúLóE_Ú fú’<â /A7ôe˜¥ ¢jcÕj D_"u×6ˆ?0˜P„œÒGîøiµGóíeë ¼4£’«/ÙÂô|Ëãu¤/Ѽ  ‡’¾÷¥åÝÖÖR„¾Ôƾ´IË“@}‰XùßF´Èæzóm«Ð—!_.¥Ö›Çs}éßµ F‘@9rJ²KND==Xw-›W¯Í¨ëK"ZÔ6`hÔpýw^øðGÒÎô%Ú˜øÿP2ЗŒíªŸg¶F[³j0•(•…›]*üÉRj7‰©Q5±/E!UUè‘“DIRuaÕ” ¼¦J9u¾Ä´ý7†RÓUÓÖ0zuîH_2FD5U©”YÌšj"ª®®Vâ`ÖÔÔðq0k¹x—uuÜ´.f!Ä$’RàBæ‘@|ìK⦋Úñq0¹-¤tÁ+ ò±ùøø˜DTU]m¸)³À 1Œ‰i{ÐÌ×x(¿£ÍŸ²|ìKób«ç™?áÅÅ*.ú­Ä­ÂÇ’Ý=l“ZОe~~JLqÇ'ðq0QP*MsSknvÁ‘dfç‰[±/Ar@L(:;­ºaöö†=–\Ö¦eo ¼6£’­/‰ha›iÌc‡&êK$ó ˜Ç’^_›çðik®u—yÜAIfÜûÒAÊrÓ¬+ †¾e}A_zYµÙd·<&d÷£S§?=[ËN_¢ñ™h`0 L°Ù-'Øñ}6£××o(g‰YÁú’ˆµ5™mdìÐÄ±Ñ ‚¾ Ê÷eU_£]õfK·µÔ=Ó|Ñ—d¼¢#5éyðxhú’¡«O™þ¶C_zY-°Áã¬â.'èË K‹ÆgÒÁ€ò!ü”>Ö1ƒèû™‰àf~²ô¥<}}ûef;t܉¾D+€`ž=}ID#5ÓÍ¿îÊ9Eo‡¾÷¾4{ M´¾åx÷@_zZ úÒÇò3_„ ôe9^Q€ʌΎU¦2ˆØ”«,òöÈ}?;;;*P!8ŸŸD}Iò@òm¯.óîž¡E—.°Õ—’ÆS|zþhõå®é0k‰H F‚ÙãH‰OǸl’(rÑܘº²™˜_Q’ÔªH1q …ª%õÞçãc2FD’û²èŸ9¸eŒž™¬}ér™üDMM•˜JV¬ò²ººZ‰}©‹ƒ9mÚ4uš‹}Y[W§¾§UUÇÁÔÆ”µQ,uÓfî“à•üÆùiQ|SF“«]ø?³Ø—ñŒ‰ }é|5> ##¦ ªèìŒ3–_Kï’Œ¯Abîž”:ɇ -XDG(D&í],…ÔìâVòUex«A_‚ä‚>˜PVXwŠ "6e&“±ˆÙ»¹¯üb:jF••¾$¢EšP˜ú¿»gÈ^_¢5 @O N_±]Ó-‡;è}©}LIvo™ÌÁàq³'ŒÇÞ—áëKPn÷ ô¥§Õ¼yô%=vy]N>Õ6z_úzF@ì€Á€r#ü”>Ö!8©²Æ’Wо”) $7Øøþ=¡/ˆäÕÙ¡¾$¢ë®˜m{³»Ô—&OGO©éK<ÁÊ馾ô²ô¥Õ}\i¡/Ë L(7ÂOéCv!8³ýý==TÂK«ùI×—DtýÊË-û÷´Ó—hSàûsHÕ—Dda0Wú¯/™ù£ÌM¨J³Çô%䎾ô²ô¥Õ}\i¡/Ë ÄÁ€2¤»{Ý—ïúŠÙ·½›û‚LÙ}ï=Ö1³Ùl&“)ã— WóË@_Ê󦛆~·ïðÌÆ3g˜éK ÍJ‚z1"Ú<ë‹åä ˜žÚç+ĪcD$SST_AÁ04›@D¢Èˆ1ÒFŽË¿ßæ¶/S08`ÓÇÓmD?Ílç,³xŽr˜ÈT*¥Ä‹E‘i6­¾$¢ªTªªð•&¦(:ƒÉ£ 1h¶ÈÚ: dÏ`—>Ø èËõeÉ%‹øékW:!˜ÍB_yÉXƒ åIø)}侟Ùþ~Ó®ßð£‡,ogàp~ÙèK"º~ååüü³zÙ¿wø²O.6Ó—hWÜi¸zºy rÖÖ\»°¥Nwƒ³¢Þ— %‘ª&å¼ù$‚¨lD F‚b-™jÔeˆ$ß‘¤¼Ök &7ߨ<2Ýû03ÐŒŸï§¾$¢TJ4“t²mä­%¯ ‰(¥1•Ü´Î`fòÑ%ÕáË`m0-}«áb†Y}ȱ6¹Ä„¾ô¶šÅáXœpùiQ\lAÐm׿šâûص¯nG꟫Á|³fÉ”¼_`З `9”'á§ô!¢îîuÖ1Ët,yåêK"ZÔ6aºÉ¬j&°Ï0™êK4.æ‰Ä¨oö ¬¼bŽîgŽ»fž6f 8HÝc÷Èbf`F¯3æàèÌJ‚'X’ïèKO«yí}IÁ”6>]ÕfC‚З À`@Ù~JÛ…3zYàx~™éK™ëW^nQAƒoŽŒþÀP_¢} @@¥Í–ú²Ð3@}Ɉ1³¸™¶O†¾I¾E /=­V™ú’99,ˆ«4¬ ú$L(["Ié“Éd:;,Íéæ¾ ºƬY[Aú’ˆµÍ/$%7fÏóoêK41â±´kú<“ñãù;îöU Lõ%¹NÝ#ÈÃóƒA ù# *"ÿ‡ ˆ¢ Š$ŠBác0J )QH‰báC)Q(úéü¥RB*%p‚van¾nu¾Ñ§°y ¹ñGy°·¸ƒÔÎW¹gL¶rRFAK²oèKO«A_úv99Ø.ô¥çÒ–}¶¨ q0 œ‰$¥OggG6Ûo³··¯»» Rú@_ª||åe[¶½nQY{_xgÙÿøp‘¾dOîÙĈ“³ú¨ Ê SòšÌ?L™­,ÃÔLAj&fù R‡«3&1Rw¯ìHbñ9Ó §‹©Ý*˜f£ù³Éò½¼Ôƒâ Ï¸Õ ›b…Bq úÂjö«3µèê$õ@´ sÙÞÕróY!# ã.ÎÉÓS“§¥šCuÓÕëÎ`ã¬ó‰hݧ.&¥ö¸­W¥üÌÂ1Í)«+ °BYò_q¯2Ê9$µªHàjK=ùÛaÅçþT«‹¿Y5ŒÁÑ)µ®w¹1¦=½¤ß5a2í®ÕJ#]y ¶¦Í|ìK‘ŸöÓàlÚ}å${à ?1ñžÐ—ÞV‹B_ ‘û&èK'‚¾a‚>˜PæX÷ˆ (0¥uš rKnÓ0*o})7L×®ùŒE½ÿäÞþ¨Ó—Š>ƒ¾Œƒ¾4¹0Š.eèËxë˾9²¸yÛšk¯»r¶ñ[%sð`vq-½J.q’] L3WËX,à$ú'ˆë/4ô¥§Õ"í}_}é±h~œCd÷zƒã¡x`0 Ì‰$¥uß{Å·å5–ܦaT ú’ìRúÑñ÷Oþa˻З|á)fú’;@³kú2Áú’Ô>Eo•Þõ¥Yr³eÊF_‚¤ýBC_zZ úÒüçù¾]µñRBÝB_Æír>ƒ åO$)}¬£pQÏú eörä EU¶úRæÎÛ>k-1?xÿä¾-ïA_Æ\_Zv®ƒ¾¤äêËÛnlYØRkpóºÖ—šeò/‰bJLÝGÌO0åOQ QŽ)¢ÀÔ9¤|K¢À p25+:›6^WÙ©ÁS(*¹ ¯LÛ„ƒÜÀÉr“”CÔ9‚{̰Œ•'ô%ô¥WYæe-'›…¾„¾„ÁPæD’Ò‡ìÆ’S`cØ¡/#Ñ—2_y™uM}päÔ[/@_ÆW_2oЗ Ô—m͵.õ%§#‰m+1wþò­ã¾L˜¾ÔšJè˘èËͳ/p¢/o_ÕâJ_-caÀlx5¿qf’ÖœTnütrYZGÉ%£ÙîŽá­¸L¾ L_:{]úÒ—ºõã úÄL¨"Ié“Édlö»~CÂ%&ô¥~ùEmó?ÞþÛŠ{û¥¡‘·ŽB_ÆM_Ú¼u@_ÆI_WOß<û‚‘šé¶7oÄúÒô¢J²¾å ôe úÒÇb{Îuù]¯e~ÚyÝúTµÐ— ÞÀ`@¥UJëýR`8#lîV²¾”'®_ù뀘2£o{û¥ƒ9MЗ±Ð—ŒqŽÍôµú2úrW}cŸ3}yÛÍ7¯÷ä6º×PèKPF@_B_–TþÎ2ô¥÷ªÃ³º Á€ "’”>¶û .g$Í]èKùÿî¼íÓN$æ‰#§ßyéàÈ[G¡/)öúR)ôe´úr¸zúæYì:¯ÑÉÍ{ÛÍ [êÈ}©OtCLÎÌ£Y†ø¬8‚@Lž£ ïÈ¥èѤîI‰”…”nÒ0& ¥YÖ>‚§n¾”‘KD\þŸÂ‡tóùÒ*YwLŸl…З|tImhM®äšc0©–ÁkKcb¬“°˜Ð—З¾ïúúú@yUJ۱佛ûÊ" &ô¥.5¹#‰ID‡Þ{£÷ÝCÙ1èËÈõ¥Õ9f}­¾®®ß<ëó®—êK£l{h:‰}i°}æ g¥ù¸K³®’Ë£³/¡QQV@_B_FÙbô\·>!ô%H0˜PYD•Ò§¬Ç’¶¨*]_ÊÜyÛ§?Þ~©Ã <œßÓ»ÿpvú2"}™ÿšYgèËhôåpuýæY‹,Ý%éR÷„¥/ÍÊ} ’ôûíÉ|A_F¯/‡J:ó±h1z,ôeÌÎ*L¨8"IéCvò4ÛßÜ®CoŒB_ªõr}ûGÖvý‰óš|¿â›÷o?|¤ÿ8A_†ª/ÕÊ´Û¾ E_W×犯÷ÃÆ¥vî’Š3C_Zîú¸2+Áøvú’èâ‹—(ÓCC¼ŸùX´=úÒóVb'L¨8¢JéCDÝ÷Þcñm »±1 }©o±.jkr%1‰èÔ±É#oëɃ;Þ?úöñSG'K×—gǦN¼wöÄ{g¡/õ%cÎ^« / ôå0MÛ)œ¿S<ß}¹«®aW]Ã.雹h×ôFWo~²¾4¸yÓ—Fñ1Õ@ˆÜG†’›4Ñ—‚V“›&Æ…¼´Ø¸õNMhêWäÈhƒyŽÿ(8„¸C/Š÷é*$¥E©„Ò€¾„¾ B_2-üÁøâŸX4—“˜Ä66FbÚðÇF¥·ýH¬ôQèЗ nT¡  "%檞õ̾íííëîα_9g¶¿ßlžõ~ôЃЗå¤/‰ù­ûnäçÏ vU½§Mž>6©üY;§¦nN WíªTL‘FxoÒäœ1®¤Œq3µóˆtƒ¨#Y)æ\7OÝu¡k,³‚œQg »#bL4صºÙâó­{Ç#£Ì'ÏLÑŽ¼žÓ_#¼ÿâÎ +š£]‘éW·ªH~wBaŽ@êY0;zV|½Õ®¶œüúEµ¥mDÃ4mX˜FD;Å™ò-Ò™féŒZ`îŠÕÔW»êJ|¿íÆ–…-µ7o°½/=ïˆ\õ¾ô¼ñ¢£óœyÜɳ$é÷ÛÓý}YïKæ“¿Œ¬÷¥#ÅF6ÿ.À|º¶¡/AÁ€JÄÚ$Ê)}:;;‚Øuw÷ºžž¬$fÏÖãÍcöú}é¢Åzçš?Ù?xè'ÿç·Œ3ÇΞ9v¶Ä³vrÿ9§`Š`2툒Vò-uò´pê´áW§&ÏÑËGx·…U…Ìˉ,bX¬k-o?KËw½$èK«£ƒ¾V¿Sv÷ôe¤ú2ª_Göë¤Zì~Ò /½nú²<À(r¨P¢JéCDV8ƒK‰Àëô¥«+£|gÌÛ¦Ý Œ ú­"Nï8‰~A+ƒ#b!mÐW}yÛ-ЗЗÀÕï·§û úúÒó~™eƒ¾ô\sЗå &T.Q¥ôÉd2Ö»îÝÜÿ€˜Ð—.[¬šÂܹæSk»>ié1¡/ÃAcq=pèKÓ­¬¼bö?Üõaeä8%N_2ÆE¤$íG‰¸¤`XSûÒ`Ú‚ü6¸iðMë“nW¨À@–ž~¿=ÝoЗÆÇ¨ W)1ÛøŒº]ëÂ]2. ²öljÙņŒò·„‘qÔN"’˜$1©x>D¦¿¾Œ1©ðÉIf»°:CŒé>…“TÒµ} ¢*—SúXz{û’S‘З¶-VƒÂ,jk2÷˜Ð—á¿ ±²;¢Èw¬¾”ÝåuWÎÑäéNš¾Ô; ã§ŠmæqÛ8,-s~Dx%Nì3ú2¨Þ—Ž„”‡Þ—Iû- æJ`¾\ÛŒÐû$L¨h¬tªËe,9ô¥m‹Õª0Fú2ü4èË$éKÎ]ô%ô%ps_B_B_ÆîŠƒ¾ôX-З &T4rJ³oÕˆ™L¦ûÞ{,HÂXrèKÛ«ƒÂ0¶(Ýxg×'¿ÕÝõñK…È èmú²<Ž(ò¢/W^9û¶U-ÿpׇ®»rvñ¡/¡/­Ìðt3C_B_xÅA_z¬èËJ¹È Òéî^÷廾bömïæ¾€’’“]Jt"êY¿áG=Û—!7-*èK'ë²ëÛ—ÉS[¶í%¢·ï ïÔA_–ÉE¾#?õe[s][K][sí–ºâûÅøæ…¾ O_âõ¸Ì¾TŽÑªÌ΄3ôhXŸ ЗŒ+¼ìØÏ‡¾$NÁ•qw  /”úúúÒö\K.DH ЗÄžT°üadê‡Cb3†_$,ÿaÿÑl7¨ºt\9†w¿|üEÕÀô•"I¦ß­®ÔÄ$¦~Œ*Xb’EQÌ® íÉaЗ nÀ`ÈcnÒ"P¦/Xw Z¡–ô²c?ú’Ü&èKèKèKèKÛêŠZ_‚rú’Jë}É›I3ª7d¼™4•žª–c¦ÏŠÒêÒ ‚‘ Ñ}:Xøðv’Ÿ_t¨Ê|20•yÉi¸‡üz²æeŒÌ\¤£Þ—~¤I‡¾þƒ @Åf4wÏÁíÚv,yïæ¾ø%‡¾tYèKûs} } }i[]З ˆ_tèK{8$æOá"TTaÅgd~˜GèKX`0¨Xæ–“êDµwŠÝXrèK—…¾´?ЗЗЗ¶Õ} ‚øE‡¾„¾ ò K_ôeÒ® à L"LéCv#Ù³ýýöõç%ÈøÝÞÁ2¶Û¾tÔ…¾t¾0A_B_B_:8×’‹+ $èK²‹}™îM6ùH–EóMQƒ;Ïf’”Ó|Á˜oué­¦ò0uƒÜ-B_Jêøo³ÃUÈI9uv.'Ù’“Cˆr£ÎÕiÒÆ!5 rʘÁ±2æòv€¾Áƒ @C´)}ˆ¨ûÞ{,¾ ºh©/AÆïö–±Ýô¥£æ(ô¥ó… úúúÒÁ¹†¾,s /É}GÌ֘É:­ì3úÈ6“OtãW]úØè+<ÎXq2ó˫ɧhsRNùèõ¥¾Z d°œ¿‡ Fª/YÆEïKs`0è‰6¥µB%¢žõbúdünï`Ûí@_:jŽB_:_˜ /¡/¡/œkèË2ú’|<î¡¢˜OÛMÖ ùÎÛïÈŸl¶_þô÷÷g³ýGŽñ~9ùpí8ªlèK9U¨Åtv¬²0•==X[Îéî^×Óó@¶¿?ª”Ö0„¾$NÁ•!WG }頴ЗЗЗ¶çú²Ì¾¤ÀôåoìÑM(,[¶,?qéÒ’ —€rlllll|lll|lÜb±þl¾Ñ›Y’™×ÐÐ0¯Áéåäð4 BãÛ IDAT‰• } â &:;;²Ù~3‡(åÎd2A`•E_Ky0{ggG\^‚Œßí,c»èKGÍQèKç ô%ô%ô¥ƒs-¹Ø8H З䷾=4::Z¬,uìÝ»—ŸX¶licã¼ÆÆFN@œœ˜˜˜˜8îj­ì[Ù,e‰hÉ’Ì’‹—$”|­3?)ý"uvƒ@_K`0cí{{ûº»4˜™Lƺhïæ¾Lf±™EÍf³Ùl(ŠúúúÒ[uô%ô¥ƒêŠ­¾ ú’?F³bóó™öÎåÖ%Ƥ7ÞØsèСC‡{(ÆÞ½oQCCÃ%—\<·a®²;¾Pr&ŸÂ´¤LG{ñÈ‘;ó¥$AÈÉÓ¹?þÁÞüC‰;Ú·ï­}ûÞZ¼ø¢Å™|l%ÞfŠ)Q¹4Q/‚(ˆÊ´²?ŸˆD.œ ýð›Ò_€B©×6ô%(LÆÈñ(-ºaÝ Òº(YZÔÞÞ¾l¿,@/^œÉ,&¢ÌâÅ‹_äs;ÖøÝÞÁ2¶Û¾tÔ…¾t¾0A_B_B_:8×Зeô%ù×ûrtôÐsÏ=WúI9räÈ‹/nmhhXrñ’†‚ÇL(ûþðÖ‰'̾M§[Óé´næÐÐÐÐÐÃË©¿ÿíþþ·—/¿vnqµØ_q̾ §íóò<ŽGx’Á`Jw÷º/ßõ³o{7÷ÝÉÑóXrÞ{ö÷÷÷÷÷Ñf¢>øï~¾¿Û;XÆv;ЗŽ›Ð—Î&èKèKèKçú²Ì¾$ÿôå³Ï>wèÐ!³ÚW,'¢tºµ­MvÛ¶íÈOlßQ¼Ê‘#G^Úú’&¦ö‰+ðÌùâ N¯mèKà/"ª€5«,¾ííí º¶Ý<{zàÿÌfûÃ{!2nA_B_ž èKèKèKÛꂾ€Þ—Kk¨/»Vß²Fkë\ÑÞ¾ü¾î{ÒéV~æ±cÇ^}õÕ£G¹¨í(8yòäþ÷öëôe:Ýzß}ë<ëK…5kVwuݪ›¹óµ¥^r~\ З `0Ø ÷‚4ûVFtºï½Çâ[¹+¨íF:;Vùû¾cÒ0‚¾„¾4<ЗЗЗ¶Õ} "úÒai‹õe:Ýz_÷=žÝ%Ïš®[»Vߢ›ùꫯ¾ýö;c}9°@§/»ºn]³fµ»KÅœ¶¶ô}÷­ÓéÝ×^Ýiv–}pyЗ ÆÀ`°Çº¤E L¿Èd2Öþ±gýEb†Pó†ô%ô¥á¹€¾„¾„¾´­®déK¼— ЗŽKËŠõ嚢‚¥ÐÖ–.îŒùÎ;ï¼úÊk1¼rN<5°@7³«¨/ª/îuÍšÕ|µŒÉb×õ•ìÏ•àÇ } <ƒ À6Q;Ž;tvd,Sñ„0žÝ®a} }ix. /¡/¡/m« úDñ}YtÉ3 $$Æž}ö7üJ¾ëK…5]·ê$æØØØ«¯¼69yVþLåra^$ºÌàr½:044¤«€ôe¾Z´ówÞÌsvròl1çΛ*Ë妦¦¦rù”“Ôcò'Ç”I‰1Éð‚Ì_%Þ Ð—À+0˜a-Žã.¹ VÕz<{‡ÏCÈ‹FЗЗ†çúúúÒ¶º /A@_:,-ctøÐáÇ+s‚Ó—2kºnm_¡Ih>11ñâ–ÇÇÇÿNxSYWWGD<}ú4¿L:ݺfÍêàô¥L{û þÏ÷ÞÛïãuËü²ŠÐ— H`08%ò”>¶cÉ{7÷…ÐÔ¨a} }ix. /¡/¡/m«+¹úïÇ úÒaiå<÷œ¦f úR¦½}yqXÌ7^c‚“˜,Џ´ï¼ýŽN_¶·/×¾¤`wʩە?ÇÇÇÇÆÆ|±ŠÐ— )À`pJRúØŽ%Ïö÷‡^1ЗЗ†çúúúÒ¶º /A@_:,­¼‘ÑÑCüÌb±PÓ*Ý–^]´¯={öNLLPD¹}tî’ˆººnÕu ´lº}—~ÝB_‚ƒ À‘§ô±-ƒAŒ"wúB }éj§ä@@@_úúúÒö\C_V:ЗE?ŒŒÿî/9à¡Ä˜ôÆo( §Ó­¾dwXº¶¶twQnŸãÇc’š¼8ð%¯Vùn˜û÷šÊåøO.'qŸ\N* ‰rY@âÿЖœqAQIÿ•~èK0˜ÜyJ"ê¾÷ž˜¼9h1A_ºÚ)9З†…¾„¾„¾´=×З•ô%9íO§ZªC‡Ô>˜ºð”A7­dº´¹}â /ÓéÖûî[¾¾¤¢n˜ÇŽËqH’ª,¥œ±µTR4Éú’³Ö¦—BÉWTŒôeLô7ð &w„™Ò§·w³üÑÍ·ÏÚ{ƒô¥«’}iXèKèKèKÛs }Yé@_’c}©ÀëK" º¦YẺn•G”ÇD_¾ ³l¼Ï=~üxpW•ÝàqèˤS…*à–ÎÎU=ë7˜}ÛÛÛ×Ý)}/ÙlV–®L(=@»»×õô<à0êeGPCÈ­[LЗ®vJô¥aa /¡/¡/mϵïúR‚ÍLЗä^_Ñ¡CšäÁž#ËoÛÚÒ«Wß²qãcÑ^HííË‹_R¸j¬­-=4t è+ˆ %/} ü€kä.föPNéÓÙÙQâ^²Yƒí;ÍÈ߉̇¾ôXuЗJ } } }i{®ƒÐ— ü)C}éRléþlKØÓÉ1·µ¥»ºnݶm»µ¿ ÃÀ—©“$)—Ë™&E‚ªnZd¢aáÕ¸–²˜ä×örÒ /Ï`9/Ä!¥OÞṅ¾ôXuЗJ } } }i{®¡/§øŠ×—qhNÓÖ–^³fuнA‹I§[c¨/‰HbL’£_æ$)'MMMi¢bš`ûRž!§õñrÒ /ÿÀ`ðHÐ)} û`QT¿ÛÛЗ®vJô¥aa /¡/¡/mÏ5ô%ðô_‰ƒÇ /B–˜ràËêË ãx€y©gúx£Èx¤³³#›í·KžÍf3ï1ƸŒÛ[Á4ô¥£Æ&ô¥ó… úúúÒÁ¹T_╸l¾ŒªÙäŠ5kV…PÂööíí±«·€ } bú`ðNg§U߯ÞÞXŒ%_^ÖrèKW;%úÒ°0ЗЗЗ¶çúxú!¯D})q#Š™ Ôÿ”¸í¶¶tйÑK¬Û0Š!±œ”›Êåäqäãþ°€#yáÒí$ô%L%\JŸ=ôàz°³c•ü‰ySÖ`úÒQcúÒùÂ} } }éà\‡¥/ñz\^@_񯯬Lû˜úÒ3º±óóç7…uDЗ z@©tv¬²0•==X[N›wvè&”~õh&ðQäЗ®vJô¥aa /¡/¡/mÏ5ô%ÆàT¤¾$¢¦¦FþÏmÛv´·/÷±ÙT†—JÀlÛ¶]™ž>}z8GĈ $”xV¡/AéÀ`(• SúÈf³rÖr³2<ŠúÒÕNÉ€€¾4, ô%ô%ô¥í¹†¾ÁœJÕ—2Ë–-Û»w¯<½m{©ú²‡øž°çw^GÄ„òÑ— ‘@’ &èì\Õ³~ƒÙ·½½}ÝÝ™€vÉdtzÔÖiúØ”5˜†¾tÔØt­/;ɯäØ)gïANNuI¾G²Ò àÈ©y. c¶W×TNúÿÛ{ó ;®úÐÿôr·Ù7ÍŒFÒh3–4’ÌnC ø‘Àà`ã ^/¢âú…ªB±$P»lþ/¤àÀ‰eSl^À6yØÎSØfó‚Kyž±-Yy$Yž™;3÷žßw¦ï龽߾ûçSÜÓ·ûÜÓ§ûœîþèœï)¤Ï9ͤõ®éÆ;~Tå1„…tM°ªÊ^^Î¥EW¦¼¾+#º²ú} õ}éûkR1:ºN][M7Lôe•ìß»úçȺ‘•BAÓm·æ’l,å¶Üq²ô—ãXdðYI"8&úƒ PšÒǧæwÜiõ¬Gf§Y³'Nôe¤!„íèJÊÒ&.eÔ÷ ô¥Ë2r÷ÏHF_F,)òb!_^yrí¤ç2¢kõƒ¾ l=ЗëF޾ ÁèèºÑÑuÇŸ(ýy࡟mÚ´1Æ$àèË*¹õÖÛÔ?×ÙÍräÜ&"ƒ»g¢/!I˜É’¡vSú4ÿcmF#ÌVGèËÙÏN?7÷»§Ï¾ø2ú²Éôe¤‹$ñ+°=õ¥Ï yqò´œ9.ΈÙÓb~1ÌùB_†)ŠDôe‘IÉ[þæ¾ ýÃSSSêºý·}Û1ŸLµ…Òê—Jí¹õÖÛÔñã]Ý]ê$K‘s›¾L¤èЗ &$†ÿŒá7ÞxS[¾%a4Â<`µ¹¾œ}ñåéçæ~÷ô ³/¾<¿°ì›ú2JfЗ¡.¤¦Ó—Žb9yFÌœk*}Ù úZü澌òÃëÖ­Ûµk—úM$‰‰¾¬‡¾ö9â#væ} ƒ ‰±oß^Ÿ‰¿KSú´ÙPF#ÌV;ëËÙÏ–z\Î/,…H}%3èËPR³ëK•“gäÌ 1}\Ì/¢/úbÞ¼;O_V/p¦¦vŒŒ¨kBJLôe•TêË 6ärÙb±PúÈb±ô) ²h»£)ÛõT”ÒúÄ8jô%4 &$ɾ}~Ý0︣ƒ£/#=lºˆÙÏ–:]úæ0ê{úÒeôe{èKu€ùÌq¹ê1惾LT_z4ýÍ}®”*S¾ôÒß‹*1Ñ—Õ0==sà 7UêË®®\Ü+Þ—ÐÚ`0 IJSúx}[šÒ§½Þ„ª1a°ÚS_Î/,)îR /í /]·A_:·YÈ‹™bú8ú2L‘¢/!œXA_ú&ë*1øY’eÙ*—J™žžqÌ<.ЗQsKCÝv`0 a:hJôe¨‡M›€˜_Xš~îÅéçæBä0ê{úÒeôe»êK‹…¼vpF›=-ЗÞÅ…¾„pb}"ÙK/ý½¡á!u͇~vk…kC_VÃ9ôe6›­J_&qBЗÐpLŠgßÞË}Lå7Þäo9[äñ¶£æ«]õå\¨ƒôe6›ÊdS>>}jÞëÔõög­‘¾E-Õ£^}X?L^ æm‹Bž†‹†’BhΑn¿$+t“G…¢VÅe­¸¬9ŠÔ0t!ÄÈ`Oˆ Ãú)éyìnjRVn)+‘.×FàϹl\tù]gʲ¼‹³Ì&raY.ܯƜ!r†#綬¾¸¤E¨_žñ1Å|^LŽzVpô%ú²ÃA_F>MS5míO)å/¹äÀ‡NžkúÚ²®~¥ëZ-Š} µƒ ɳoßÞƒ;vìh›×¢]èK9ýÜ‹“Œ‡Ò—Ùl:›K !»•ôæËVbRо.kåââr~aY ™_\É/.÷õgíå/…[¯LU_J})…°‹PÛSµBLz¹i;UG:/Y¡¡¤kÛ[yÓVs[±¯t“GRV\ ) RÎ ±ì<¦a!Ö õºHåO)U}Y¡ú²XYnBÉ´’ˆ¬(5}év%ÇìÊîE!t»÷´_RÚ~Ú¡/eYbÊÒ‚”'¥BäÌÕ‡3Ž*#ÒV=Þ¡téפr¡ -4!Ä‹ËzÔ:¸×ΈMëŠ]Qqý /×—¼·èËÊ&Ó÷§/¹äâÙÙÙ‡*/IÌK.~æÉM-|!4«¾Lð‹W.ª]tèK¨F‘@Mè˜)}ЗÁë_žÏÇЗƒƒ=ë'†¶n_?148ØI_:Š1“1{²}¹uc=7ÚË¿yô¥¬¸0*õ¥´ÿ´PôåZníýû"èK+‡þI;èKW_*™±r ¥b8«mìÖ†3b8£EÒ—k¥µú‹9C¥‹C©Â9]Ëçt-¥ 9½©ΜÐgÏhžG‡¾D_B,ic·–Ó—QÍÑÈÈÈÅ¿ÁÖþÌ<r‚ò&½ª/§§g*õåàà@èËdŠ} ÕƒÁ€šÐSú /Cê˹ðú2›MölÛ>>0Ø“]'nsWõeYṌÝF_ÚŽZ:5úÒ^úÒž™Šaò¡õeEu“CfaC¦°!½v„òQÖ&מ¡/¡ž`0 †ìÛë7–üÆojë ôeX}™Í¦'&†'&†r¹´}i/ûºéË@Mƒ¾l€¾ÊèË&Ò—¥trzaC*ŸÕVükýB^›ÏW©ôe$})™Õ§îÒÕîÖúR‰u鳑:‹æ…=Úbùc膡ÃCC¯zÕEŽü7›Äl¸¾t¬ËårŽâÕÖ‚\–â^ z4矚ÐT›Y‰°ÏûŠC_BÀ`@=èä)}:J_N?÷âü²ÿŽù\}ÙlúÒí{›&ô¥sKQ}ií»!•ÏiÿZ?s˜ϣ/ú²£¡÷eÈÜV-}ßüæKûûma1÷ï¿ýÖ[okŠ ¡qVëÀ‡ú²4r¦Ù‡ë«±/uŸ‘ã.ñ0]Ç‘kš® GèRÇ òµX§Î˜˜öaäèKh0˜P':mJŸŽÒ—BˆŠð—èËÓ—êéºØÑ—M§/Kl°õÄtil1í§ }Q_òzÜötŒ¾,ǾԄÍOª2K”õ¥‡b³ô¥µrë¶­\xúƒš ¼QVkzzæ†nrèËññ±ñññÊТjìK»À´ÙÊòc5ühù³¶Tò›Î¸¥ºû ?•ñ1…V>ÙèKh0˜P?ÚqJô媾´G_¶ª¾ôœÑ§Râ /Û4Z_–6p‘˜ö²š=£Wž&ô%ú"¹–ê})ÿ©ô÷÷íÙ³Û±²Îsû4P_VÎÛ3:6êøR„<jÜw"ƒÇ`޾„$À`@]i¯)}З«ËöñãÎ{Ñ—¢%ôeÀÕ%Ñ—ö#m:}Yúrƒ±àÓ2,äµÙ3ú} ±] ú2Fþûûû÷ìÙÝ××§®Ü¿ÿöªÇmÕªœ·G /cç} L¨3m4¥úÒÒ—g}vÌfÓCC½èËV<¨/ú²ùõeé%‰éÑ2œ<£…h=ЗnG¦A†Ö}Y«ü÷÷÷ŸwÞ‡Ä,fýŸ.¦§gn¸á&‡¾ML_Ra IDAT&rɉ:Mq޾„:@Lh0ûö^îc*o¼ñ&ËÙ¬oDm®/ý÷ËåÒèK€F´@N}¹Z%ÅÊbù±Ÿ©{¼Ú·H¥Q¤/HKW• ¯Ñ—B‰š¸Z?¤¥Uš^¾øu£¼WJ”ƒ3Š…b±XÚWZ9¶£^( ®زuËàÐГO>VŠj¾®éåÏšn+Êã5ŒÒzŸ21 èÜÝQÀj64¤åSXõµQÍõ¾„H`0 YØ·×o,ù7ÞÔÜoD¤/ç–ÜÒ“bu9ú !‘§¾BäV ¦K¥žÏkÞ­ úÒ-èË6¬>«/æÑ5oš76w¦kš® MøÙ:Ý(i̦òQÿ*íµ{Ï”#,æÌ̳7ÜpÓôôLýõ¥ë¼=•åà0¶º±ª/må`:^Ñ—kkMkÛøQ:egª¸T‡_öR϶S¯ºêè×-úƒ ÍB NéÓ¡½/ç–+Ò[‹µ—M‡Ó—»;¢pº×±/ú²õÁ`@sÑRSút¨¾\EîCôe< …B>Ÿ?uêÔÌÌÌ#<òÍo~sß¾}ozÓ›ž{î¹ýââââg?ûÙÝ»wßu×]”ë˘ØúR …‰¾D_vLA_VPþ—*ß±²ÍêSšÙG¡ ]Ó¬¦ü¡êKÿ‰jL5dù³Êž=»+çö©‘Äœžž©Ô—ë×—Ìc¦¢rÀOCW¤ò˜Ja2 Ã4ÍÔÚÇ´‡Ç4,¿éUh«úÒ*íjb_jíû}Ù`0 éh‘)}:·÷åšÁt®ÏfS¡õ%‘a9pàÀ%—\rìØ±ÄS¾ë®»vïÞý¹Ï}nqq‘rn'+ã¥/³ÒóßæóúÚzô¥[ЗmXQЗqó£XÉ¿”Bˆ]»vöööª«k!1K/úr||lpp ÉS–ˆ¦cðx¸Ü¢/Û &4-8¥OàÓhG ÏæÒèËZðÌ3Ï|àH<Ù½{÷9r„âm+|z_J×8˜ŽÑ—np×—’¦¬•+ ú2nþ£K¸¬Éð?³sçGXÌ™™go¼ñæ¤ÂbzM;rÞ‘œ¾ L}2·è˶ƒ ÍHKMéø4Ú!±/ú2ÖU³J¡PX\\œýÍo~óùÏ~ppPÝì¾ûî»ÿþû).zgóÔ—!Zô¥[ЗmXQЗqóïöÓ¶1ÇêàòÒðqkd³WÈHcmäµî9žÜÒ4 ØÚ=åÈ"”WÎÛ“ÍfׯÏårV4Out¼rk#¦mx¹a¨IXƒÐ=.rìKÍ'.úZ &4#­7¥OÇëËŠçT}ÉÓdÅ3™®g2™áááóÎ;ïÓŸþô†‡‡Õ n¹åJ |‰«/%ú}Ù) /#äß;¢%Èü¦îYÕ—«¾­œÑ¡ût/ÁgMmcšÊ_¦a–œæààÀîÝS¥åVþ«”˜®óöLlXŸëʹjDEZZÁ/K Rç麨’Y9yU.Λk5ö¥øÒ+ö¥ãdÙ® ŸÀ—} Mö´L@sÒJSú /Ï©ú’çÉ@víÚõ©O}J]óóŸÿœbp/oîúRÊ &}‰¾lóú¾Œ›ÿ8?„´òý¾·¯·2,æþý·ßzëm1ŽÈK_&y­$T¶ ™[7Û &4/­1¥úÒùœ¬/% {÷îUÿœ™YíWrüøñT*eõ¤˜˜˜ðzF—RNLLX[¦R©'N«ûŒ÷Þnüä'?¹öÚk·lÙ’ÍfÇÆÆÞò–·üÓ?ýÓòòràåóùýû÷_{íµ»wïîëë3M³··wjjêÚk¯½í¶Û–––|ö­ÌÞ3Ï<ó×ýׯ}íkûûûMÓyÛÛÞöõ¯Ý?v}YW}‰Êl¹ú¾Œ›ÿ?-“øépúlçΕsûD’˜ÓÓ37Üp“C_®Ÿoˆ¾ sȉlƒ¾„Vƒ ÍK L郾ô{½G_VÅÆÕ?J £££o{ÛÛ¬õGõêžùðÃ=zÔúóþàÖ­[WM–^zé¥÷¾÷½—]vÙ-·ÜòÌ3ÏäóùãÇ?ðÀùÈG.ºè¢ééi¯‹ÅâW¾ò•ÉÉÉ«®ºê–[nyòÉ'_zé¥B¡pöìÙ§žzê–[n¹òÊ+·nÝú­o}+L6ŠÅâç?ÿùsÏ=÷ _øÂ/~ñ‹3gÎ …“'Oþû¿ÿû‡?üá /¼ðé§ŸîÔ«Æ__JÏ&H·<èKôeÇU'ôe¼ŸNÂ|…}èB±k×Î &ÔoJ3̈r×y{ÖOŒçr¹8MoÒ¥#ô¥@_¶/LhjšzJŸÎÔ—R„n˜¶nèåâRbbª±2…&Ê“ÿ8&öQ¯ b_Bk‚Á€¦&ö”>wÜqçÿø“ü?ùÈwÞU“ ™èKß £/«ç»ßý®úç† ¬å?üÃ?ìéé±þô2˜?øÁ¬åǰô<úè£>ß>ñÄ•( …ÂýÑýð‡? ùßúÖ·®¹æÿ×oûÛþ‰:tèë_ÿzÇŠ—èú2`}‰¾ì¸Z„¾Œ—lùw€.Ûôõõ9$¦ðÛçÖ[o;pàgêš\.·qㆮ®\”K¥^ž.¡ÌÐûZ &4;ñ¦ô9xðPiáÎ;ïºóλþäþ¯?ùŸÿëΚÏÿƒ¾D_&ÃO<ñ7ó7êšw¼ãÖrWW×»ßýnåj?ø»ßýΑÂSO=¥†Y¸âŠ+²Ù¬u>*ñ¥‚OÆ^ñŠW|ç;ßyñÅçææ¾öµ¯9ÆÙ}ï{ßslóÍ7ßwß}êšÝ»wó›ß|î¹ç–––žþù[n¹eÏž=ê·ß~ûßÿýßѶmÛ¾ûÝï¾ôÒK§OŸ¾ùæ›u]÷ÏI‡ˆ?})¥o„¾Œ¢/%*³}kú2^² Õ—%J³¯Ï9·Ï9¶¬œ·§¤/#^*è˸WBãô%±ŒZ &´1¦ô9xèPåÊ;ﺻ¶î ð} ÞOóù|þرc?üðg?ûÙ×½îu§OŸ¶¾5 ãꫯV·w $¯vŽ5×^{mõ™ýéOúž÷¼gppp``àCúпøEuƒÇ\ýóÔ©Sû·«®¹òÊ+{ì±k¯½vbb"•J­_¿þꫯ~ôÑGß÷¾÷©›}úÓŸž››óÉÉÄÄÄC=ôîw¿»§§§¯¯ïú믿þúëÕ žzꩼˆÐ—uԗЮMqæ}k)ø¥}}}»¦v©Ã„üÌšÛgzz¦R_EÖ—¡<]ý!¸,Ñ—Ðô˜4?ûöí=xð«”kSúìØ±£¼Æc†ŸsÏ}E ÝAà3`›èK?yR_òi8ñw‰¿ø‹¿8çœsÔ5—]vÙúõë­X™ßÿþ÷?ùÉOª¨CË·nÝúÆ7¾±úÜþùŸÿ¹ŽSñö·¿]U‡³³³ê·ÿú¯ÿzæÌëÏ;wþË¿üK:v$›N§¿ñoüú׿¶z’ž9sæ[ßúÖÇ?þq¯œ|âŸS×¼ÿýï¿é¦ò?fœçXiÆ•W^iýù‹_üâù矷þ|î¹çyäëÏk®¹&¤*õç÷ÿ÷kÖ¯_¯þ9??¯þyç¶0µ×_}&“qM9“É8:QþèG?òÉÉ¥—^êX399©þ¹¸¸Ø¹}Üð¢/Á÷.í[µ"‰•D¬JË÷¾L"oQÕîÝS7nT¿-ÍíãØeëÖ-ÝÝÝ TÔcNª=‘Ilƒ¾„ƒ ­A¤)}¼ æ¹;ja0Ñ—î?ä«/yŽ E:þä'?ù½ï}ÏUü©É¥”ê¼=?øÁÔ  úYÈ×Þô¶:Ö8ú­ õÏ_ÿú×êŸo}ë[}w|û›ßüÆgcÇ{©¢¯¯/ùפÖ}Y7}IÌ6«9a«V$±’ˆUA_ЏŠjÓ¦SSS^ÛwwwíÙ³»yõ¥LÆ<¢/ú²À`@Ë~J¯ñæ5胉¾D_&÷X¦ë¹\nttt÷îÝ—_~ùßýÝß=ýôÓ_üâMÓ=ìÏE]´kWyÞU5ð¥ºü†7¼Á1=6K(„X+çcP¹:z%ŽoOœ8)'êè¼N¥}éõ6ˆ¾D_vBÍ [µ"‰•D¬ ú2tù»oÓßß·Ãí_¯»{º·mßžLAŸê£&ú2t%‚8˜ÐJìÛ{ùÞó‰ßxãMŸøÄÿã}%ñÈúÒöûþúR23¡gúk®¹æSŸúTiù8}útÿ©S§|ðAk›DæðY}p¬p©þƒÓS©ÔÊÊJ¼¢ðO¹2˜f"Ãä[üZ²ªú} Q+Nu­tëKGLLײÕ4e+)„Ô¬- ݽOUJKYù×u½¸ï²X,ZGTŽƒ)¥°‡†Ô•dGFF†‡‡ü +,fooï®];‹²œ”,ÊbѵM†i¸&ëûR×uõßöÌ”©Þ­²U·Ñt%ö¥Ð¬Û™¦i¶[›f¿åi!N"±/i®Û ú`@+±oß^ÿ±äô ‚™ðrô%ú²ñ\}õÕÖëÍòòòÝwß-„¸ûî»———K+Óé´cšïz²nÝ:õO5Rg%Ï>k›Ö1Q|¥÷Ëš·¾”- ú2š¾¤%kƒ ƒ¾Œ—l"Óp׸÷¥#‘={v—:ïïܹc×®qJ;cN¨Ý ÷%ú²SÁ`@‹8¥OC§ñ!ޢø€H›Í£/ú²ÎlÞ¼Yd¼4ÿ¸: ù;ßùΡ¡¡FeÏ€ì'?ù‰ÏÆ÷ß¿úç…^ÈùÜ¡/ë§/iÊZ¿Â /ã%Û‚ú²Äž=»_ÿú×U!©Óõî”É' Ë} ­ ZŒÀ)}<ƒ`Ö|â-:Œ ˆô°ÙDúR /:ŸÏ~ô£Ó§OßsÏ=ÖŸ9|ݽÐUÃe—]¦þù¥/})ŸÏ»n™Ïç¿ô¥/©k.¿ürNnœ÷gô¥Зî¢/ãç¿^úRFÑ—µ¾ ê”[ô%ú²³Á`@ëá?¥+çž‹¾LH_z?8FÑ—vìØ¿ýÛ¿½úÕ¯Þ¿¿ºÙ 7ÜÐÓÓÃÉee¼õ¥ |ÉD_FÒ—4e­\Qü6@_ÆÍ›"'½RqUuª¥tÕ—zkNQ1’ å <´cÉfš†¹ú1ÍT«ZÓ0ÕÍ*õåš F÷BÓWR[]v±™Åës5oщ¾dö±}ìcBˆë®»îõ¯}Ryû¾ðÿñ=ö˜µæñÇ÷ÏØ‡?üá}èCœÖª_øÐ—èKð½KǶèËxÛÈd ªú³.‘:y:)¥Ð„ÿÌâÉ\–èKôeëƒÁ€&¥²ßåjŒË5›)µÚÁôydî8})¥´¶A_Ö™w¾ó§NRWž{î¹þ"òíoûÏþóÊõGŽI0o]]]÷Þ{ï»ßýîŸþô§a¶ÿÓ?ýÓ/ù˜Ӫ_øÐ—èKð½KǶèËxÛ´£¾ôì ™ô%pY¢/Ñ—m£È IñšR|õÛC:`ž{.úÒ;ñÈúRú?†Ò—þñïÚµ«r}²S122rÿý÷ß|óÍëÖ­óÙlrròöÛoÿ‡ø]çµê—?W})½Zô%ú²jú2^²¢~a55@¦ý¯açÖÈkÇ@s¯qÜj LSùË6TÜ0½Bg*ƒG‹;wƈ}©¡/C'‹¾ìèƒ MŠ¿ÁŒDÍ;`¢/OèËzsÍ5×üó?ÿsù=PÓ®¾újÿ]þóŸß|óÍßùÎw>,„ššò™ü'þ§i^ýõûØÇî¸ãŽ»ï¾û‘G™žž>{öl.—Û´iÓ«_ýêw½ë]ïz×»“ AÌ—?ôey5úBÞÆÑ— çßêžh«å.›iJ-+wiÔôò².…”Z)‘¢^nÖô¢.u©l¦»Šu€v¡PÐÖþ‘¬X,êVás J?KC7¬Ò.ÙÉÕŸÖu]7¬#ÒuMJYú]cm}i3[®ÖÛB[jÂZÕÍSúNì#ЗÉå š &4)á»XRÛ ˜èKç×Áú²“%ktìÖüã%.¾øâ­[·îÕÛÛû™Ï|æ3ŸùL5¹ Détú½ï}ï{ßûÞ•¯(ñõ¥@_FÓ—R±™Õ,£/cm*kµ)ÿÝ#¹…Q“ô¾èK¨€A:ÐŒTÁ¬†öÁìD}é/BéKž%“eqqñSŸú”º&°&´#±{_Úªµwk€¾´V¹ÚÚ§Ú /k–ÿ¤ô¥l)}*nx}.Kô%ú²í &4)ûö^~ÇwUŸN ƒ`¢/ÃåÐM_ò@Y-Ï<óÌøøx>ŸÿíoûWõW¿úÕ¯¬¯z{{¯ºê*ЍãÒ—ÂK_†j ЗÖjôeUôeÍò_¥8²†~—†cKMZëË_éRJiåD/–»g©£Ë…Q.(MÓ´µe]ÓäÚÀsé}@å1ÝBêš2 \E®iúê²”¥•»¯.¯ ú¶õÁÔÊ#Á‰}>Yôe‚Á€fdÇŽ;vìØ·o¯âŽ;îBĶ™µê€‰¾¬Øà¥Ó‹½ýÇzôeزe‹×Wög600@u*žúrÎè*oc¯Ó]™"ú}Ùi /k—gÖ´P9±I½ÕÇ…Òhër¬GM k)¥,Ê¢*4Ýs(m?aL©ëVF} ³”«ÕØ—š-R§b05]Ó¥¥_PE§OìKµ|ÂľÔÔ"ö%ú²#Á`@³Sò˜±m掚ODÞqúrx ;;·&çèËúóŠW¼Â1¢: O})Ëá\z_ve Þ­úÒZ]´ÿ(­Y ƒ¾¬]þà O 0e½ê`2BMHMhIüPe‹¾„Öƒ ­D ›Yã‰È;N_†›–}ÙvîÜy×]wuwwS‰Ÿ¾,o­5@_Z«‹G ­ú²vù¯›¾²N3™š Aô¥@_v6LhUÂØÌwÀD_:É/®d²†Ö—<\VËë^÷ºÃ‡Ÿ>}Z×õ‘‘‘ .¸à=ïyϵ×^›N§)œ%H_.êfÄÖ}i­F_¶QEA_Ö,ÿuÕ—ujVƒ;N&sm /Ñ— &´^6³–0Ñ—.,åW2Y}Y~øa ^ìm½/增ŽÒ /­Õú’–¬%ëú²Vù©/m±/¥õCŽXåš”ru/] !•À”¦²\(/õ¢½QTb_J¿‚­Ì‰àÒö•ÈRšcöËzÍð6öeÕ×-úZ &´ªÍí]‘Ù}&¡ÄJ;2šÛÇ}­40=r©$‘ ú2±ó- ¹Wž õí§/K¬MJî¹ÍéÙEô%@Í› )ôô)³×»i’ÃåñãèË*ô%­Y[W¤D¶A_†I(jn5Íåã¦i𵑲›«­¯KiøÊMÇñÄ4¡èË†æ šfòHè•'h}»êË“=ÓÏ¿äµÍÊRñôl¾$mÅsÏßS–.nî@Vä\Zÿ]ÓR:IZ‹k»—÷­8ºµ*ŠK:WIÜJÞ}c¹z¢¥šš3«%uR±FØR°]0j.WTE)¹^uÒý ”J‰ !V ²PÆr.µÜå¸42º&„8wĶ]dùmT _= Û•g¨·¥t—JŽ"u®´ýéZ2ÒÍ@ÙΡׯ )ŠJzê‘e_q¤RÚW:ŽÚYóÜ~HØÉZÕÄqB¥\ÐÓá/mÛäÒ…‘ÞåŠõèKô%Äj± ú²ž¹•I’”2™RI"ôeíòÍ }0jðˆüzïÿnï‘x³êKû”>îÛ¬,OÏ.9Š}Ù<úÒûW\èKÑ’úR”'ðA_&¢/yn¿{9ú}?ô¥@_BÁ`$ýˆüzïÿnï‘xëËÒ&7ôve Ÿ’*õÄT}éÌ@ãô¥\])®4ô¥hU}¹ix¡+SD_¢/!¾ôA_ŠÓ—‰.ú2dnÑ—Pk0˜µxêí8}YJ_™ÒÇ}㕥♓K}éV¼Õ—e+Зí¤/séú²âGÑ—Eú /EsëKx“•,#’Ï)S“Õ"ÆÇŒW¶èËÚå Z &@rHÁ¯÷þïö‰·ˆ¾BtåR“ë{ü7^Y*¾xtq9_¨tèËÆêK»’ó;éèËæÑ—GÓCaôåäÈ"ú} ñ¥úR´ZïK™@ᆲr‰ôÐD_¢/!L€„‘‚_ïýßí=o}YZêÊ™n=1‰œ}qeñl}ÙDúrµˆÐ—¥­Z@_.h©£é¡E=ã]×Ö"< /?о„(Ò})ЗUl“DnÑ—µË´ÌEè«çë½ÿ»½ÇóW«éËÒF³ó +ó‹+þ‰,ž]^YÒ²=†™ÖÑ—M /_0ЗM¤/çŒîSf¯ïÛÛêò¦áôe ôe›Ù÷lô%ú2þ)C_ ô%Ôú`$÷*äùzïÿnïñüÕšú²ÄäDÿÔä%¡°²$Ͼ¸²pÖé:Ñ—nWTõ¥T{bºŸtôe3èË-u45^_ve ŽzÔP /… Ø‹Z÷ž¾ì8}©DÃÔ|?¶è™®ˆDJ}‰¾„(`0zò|½÷·÷xþje}Ybr¢Ç{jrÛûþlñô±åų{ªèKõŠª¾ ¼¼Ñ—¢úrAKM s9.ЗèKˆxÏF_vhïËg„Þ—¡r‹¾„úÃ(r€$Ð—êÆ“=³s‹³s‹öõîïÿù³ÅüÙb¦[ÏôèèKûU})¥Ç5ãvÒÑ—õÕ— Zú”Ù³¨§B¼½­NÝ3Ò»„¾D_B|郾èËø§})ЗP0˜‰=:¡/+7Ìveé£/‡|ÿÏ¿\ÌŸ-)a¤µL†¾¬Ÿ¾ ~3¥±uè˺éË-µ¨§O™=a+&3×O_ò†Üº÷lô%ú2~*èK¾„ÆÁHæÑ }éµqWÖœïž>ör¨îKR ! Ë¢°,—^.–T¦‘Òô´¨R_—EqY¡]Ž}Ñ—aÕ˜½TЗåŸ]æ¢f !‹ó.EQ_ÎÝBÈSFO´Š‰¾D_BÕVE /Ã_ãè˘©$[ôeíòÍ  G'ô¥ÿÆ]9sçÖÞé£gç ¡ßTÊ*ÓJSO #%G$]ŸGK½åʼ^‘¿byƒ²(Ô*Bºµtl#+Ïô-%MÈÊ#ÕìÛK)4x®½þP‹Hz\j²R¥iE¡¤^LUžÇ•¢Bœ\ôWÚÚŽšûu¡¨º™´gEF¯ÒÑ[Wz\]Ò§¸l‰K¯ 8—RBˆEa.j)¡‰SzW)‘¬\ÎÊåÕ5ëG¥KÙ”D»§ÌÅ$ŠhSH±ø²ˆ¾D_B|郾èËø§})ЗPK0˜Õ>:¡/C*’Éõ=ó +ÓÇ^ñ¦ân ŠK¢¸äú ªÙwÔÖtQi›ÓtÝ&JŸD·£ÐÂnòåL=»{8ÏYÅ¥k"ZAèE÷óXBñbÞëÊÔ¼ËSSN®ú‚ >2ÐF…ºº"ÕA©SÀ-j©EÍ > w“ùj\ëz)ЗuÔ—¼ ·Ö=}‰¾ŒŸ úR /¡ `.r€ê¢Ð—áÉjgÌ~—9ÊCèËÏ aŠ=ðe¢Z}z›æÒ—Q®:¯«WF<­©/«ü!ÏÖ#¾¾Ü4¼€¾tûÑšêK¦ôi»»9úR /ãŸVô¥@_Bí¡&@QèËðŠDar}ÏüÂòì©üê rô¥ç¢/}¯êŽ×—ýK#½KnÛ£/Ñ—I‰ /Ñ—ñO+úR /¡.`0â>D¡/Ã+’Ší»rædΜ_X™[T‚c¢/ÕDЗ¾WugëK»»èKô%T¡DЗèËø§})ЗP/0˜±¢Ð—á‰÷ö]Ycr}÷üÂÊì©üüâŠÿ« ú}‰¾«îrÙû‡Ð—uÓ—¼·ÅÝ})ЗñO+úR /¡Ž`0’z¤B_FÓ—êLå“9C1;—Ÿ_\)wÉD_zæ±!úÒã7£ ôeL}9ܻԕ^éÊ}}Y/})y+nû7ú}ÿ´¢/úê  ‘G*ôeL}©®Ì‘BÌÎå…³sù gPô%ú2Òyi1}™Kº2Å®L¡+]‘1ô%ú"ݿїèËø§})ЗPw0˜Õ?R¡/З*#i!äÈ@Z1;—/maëž¶Ø_&З/ôe¤ò|ãrחý˥¥’¸T~}i­F_B"÷oô%ú2þiE_ ô%4 &@•TèË„õ¥].”:f–HGLÐUÍȸåïÈd1J:¹Šq¾•J9ö¥zDšËrd}áxû5Ï‹'DqEªkÑ\›÷¹.F, ô¥µºyô%¯Ç-}ÿF_¢/ãŸVô¥@_BƒÐ)€dž­Ð—5Зq‹K /k¬/½6q/ôe`‘¢/Ñ—Èm}‰¾ŒZÑ—¢Åõ¥¤}+ƒÁHâÙ }‰¾D_¢/ƒÏ5ú} ½m£/Ñ—ñO+úR /¡¡`0ª~¶B_¢/Ñ—èËàs¾D_BcoÛèKôeüÓŠ¾èKh4L€*³Ð—èKô%ú2ð\£/ÛJ_J)yn±{5úR /ãŸVô¥@_B€Á¨æ1 }‰¾D_¢/Ï5ú²Ýô%w¿v¼¡ÞðÑ—èËæ}Ùìyƒ&ƒ û1 }‰¾D_¢/Ï5ú} ÍC¼á£/Ñ—5Ì-ú²ÙóÍ Þcú}‰¾D_žkô%úšÿ†xÃG_¢/k˜[ôe³ç š &@m^ƒÐ—èK÷hô%ú2°Hїͯ/%³ú´ß};I‚¾¬YnÑ—I]êèKh9LŠÀ¦iÖRÌç/ô%úÒÓzD:_èKô¥k½C_¶¸¾ ws±nFZ´›Ä÷&ÉôeÍr‹¾LêRG_B+BL€¤_ƒÐ—èKOëé|¡/Ñ—®õ}IïKHø¾¤A_Ö,·èˤ.uô%´(L€DŸ¿Ð—èKOëé|¡/Ñ—®õ}‰¾„„ïÛIôeÍr‹¾LêRG_Bë‚ÁHîù }‰¾ô´‘Îú}éZïЗM£/yCn—ûv’}Y³Ü¢/“ºÔÑ—ÐÒ ¡ç/ô%úÒÓzD:_èKô¥k½C_¶Ž¾$le‹Ü·“4 èËšå}™Ô¥Ž¾ [A Y¡&@Ï_èKô¥§õˆt¾Ð—èK×z‡¾¤÷%$|ßNÒΠ/k–[ôeR—:ú2l&ƒ Põóú}éi="/ô%úÒµÞ¡/Ñ—ð};I;ƒ¾¬YnÑ—I]êè˰š &@=ý¼Ûóú}û|¡/Ñ—®õ}ÙœúRÞûØ:n…­úR /c§’@nÑ— Îú²-À`xrÎöí^_:ü|Åóú}û|¡/Ñ—®õ}Ù¤úÒçmxû¶­¥MAÏȘöÍ 1 /ú²–¹E_68oIëË0í¶f‡f60˜žœsÎöÐÏCèKôeìó…¾D_ºÖ;ôeóê˧vs‹lЗ}YËÜ¢/œ7z_¶L€8Üuï£á„Ex¥âõþD_¢/‹ })‚Š}™dïK fÛ€¾èËZæ}Ù༡/Û &€[·n‰òp„¾t• ±‹K /Ñ—!Š })‚Š}™ðàqƒ¹}Û6n­úR /k™[ôeƒó†¾l;0˜~ló~½ëÞGЗèË*Îú}éZïЗMûRÊÃû`jš3&škp4â¦ÅC)²…†¾èËZæ}Ù༡/Û &€Û¶nõúju2Ÿ°Ž}‰¾AJE„Ó(n ú}é^¼èËäõ¥oÌ­Ü7[ô¥@_Ö2·èËç }Ù¦`0üضÍÇ` íЗèKá^ÔèËà  /Ñ—EZ?})„¸ç±QnŽ- úR /k™[ôeƒó†¾l_0˜lõî†y×½¢/=ä‚> ô%ú28èKôe`‘ÖU_ !ëñº)lžÜÄM³ÉA_ ôe-s‹¾lpÞЗm  Ÿn˜wÝ÷h#@_¢/…{Q£/ƒ3€¾D_i½õ¥OÌþþ>!4M•!jÙ /ú²–¹E_68o!6™›{1ü¡”Ûp-ÄF/®=Lè âM’ðß.{«Ï·‡ž~Þû­}‰¾AJE„Ó(n ú}é^¼èËZéK!Ľ¿óº ô÷{ÏØ#œ¹}ª¼¯q—¯‰A_Ö,·è˸—r¼B@_ !ÄÜÜœ×Wc£Div0˜Áø $¿ï1ô%ú2ÄùB_¢/]ëú²•ô¥Ì-›'¹]6-èK¾¬enÑ— ΃Ç; &@0>Ý0>ê6¥ú})Ü‹}œô%ú2°H /…(útÀìïïã^ÙÌ?~¼zÕ¼ô¥g:èËX /cWh 0˜~”Bßlß¶u›w7Ì›¿rw8¥âõþD_¢/‹ })‚Š}Y[}yÏcc>7‹­›7{Ž wTî>ŒÜú@ 6l˜ðúêĉU꘨æE /=ÓA_Æ}»‚(øŒ"ýj@ Ý00˜Ð愌øºé3Ÿ°M郾D_Š ¥"Âi· }‰¾t/^ôemõ¥âÞ_Ž{Ýúû|ï!ºýŸêïwm̆ ’I}Y³Ü¢/“º&Ñ—a+ˆ/ƒÙÝÝíÓrjB”ÿ¥I£±mL€P¼í¿]æóí]÷ýòÐá£èKô¥ô%úÒµÞ¡/[O_~ùîí>·"`6?O<ñDlÕ¼ô¥g:èËX /cWÐÉötwÓN6?L€°øOJ~×}>ì£/Ñ—öÑ—Å…¾AÅ‹¾¬¹¾|úh÷ác=^©&·È&aÃÄD²:&ªyèKÏtЗÉ\“è˰¤"Ù#GŽÐH¶4L€°\zéïmÞ¼ÙëÛC‡Ýü•ú<ì£/Ñ—!t ú}‰¾l:}ù¿xŽÏ­aëÖÍU W?P5>Ƀ»a¢/k–[ôeLЗ±+ˆ[²>A0{zJ}0Ëáˆí#ÄiŸ› &€¯wKMÓÞ|éïùìxèð±CO ñþD_¢/‹ })‚Š}Ys})„¸ç±qŸf¿¯¯ohpP‰o©»~t]S?ö?<ï8ÄY«+èËšå}™Ô5‰¾ [A<’=uÊÓ`ööôTv4ÏåEÏ·¨1L€lÙ²Ù§¦âæ¯þèÐ᣾OlèKô%ú2°¸Ð—"¨xÑ—õЗ_¾{»Ïøq!Äy»§¸36]ôJ¯¯üú`¢/k–[ôeLЗ±+ˆG²>CÈ ‚Ù*`0¢ñÁ\ë¿ÁÍ_½G•˜èKôe]‚¾D_¢/[O_nÚ¸‘{b²~½g·Yw‰‰¾¬YnÑ—1A_Æ® ÞɆBÍ 2¸.¬ÄD_¢/Cèô%ú}Ùzú²¯¯or³Y¿~½×WÇêDЗ±s‹¾Œ ú2vñMÖgùøØXåJÏAášPC~Ø¿) `0J)etÃŽi¦aœ³}Û–-›ý¼ù«÷´ 'G_¢/Ñ—Å…¾AÅ‹¾l })„xå癦ašÖÁ4 Ó4ÍTJý¬bšöºòQÑíj-<¯ºè"¯¯Nœ8a“˜èËšå}ôeì â›lÈ!äÞq0•HÅ"ä4m6¡é±‰çË-¹+L€8\{õû7õ¾ùÒWï9tø˜¯é@_ÆNPx8ôe”s‡¾D_¢/+£//8o÷Áf&Ô@rôeÍr‹¾Œ ú2v Jö¿þËÛ`2„¼uÀ`ÄäýW¾/p›/}õž»îûU°ñzRC_úÒsôeà¹F_¢/m‰?}´ûú¯]¨/§víèç&ØÌø $_톉¾¬YnÑ—1A_Æ® AÉútÀCÈ¡9Á`Äçª÷ýqà6wÿøW7å_ë!‚¬ú}‰¾ôÚ}éZ¼èËÈúòžÇÆþ÷Ï lÒûz{ûûú¸ý59>É…ÿ¤äáÌ‹@_z¦ƒ¾Œú2v JvnnΧ溑̃ K˜ 6ºúÑuÝ0JÃ4ÍTjÛöm×\ýþÀúÏ#/üé'¾±6¢\ /«HPx8ôe”s‡¾D_¢/•ÄŸ>Úýå»·ßûËñÀƼ¿¯ï‚ó÷˜f9Àe*•J{J§SéTù£ FÊ´n+ºaèö›N¸Hkm~wu×V‚ÍY¥!f i…¾Œé¤Ð—¡r‹¾ [AB$ëß³§»ËsXùèºn”>ší %&¦&Ô…úÔ &@UlÞ+íy¹A6M3•2­ö<•ZÝÌL™êí KiÃÓJ®&ÛÉmx²˜@RLnÚ4ù¾Mÿúo·>ôhø½îþñoîþ±B¼bÛè¹ÛÇÎÝ6vîöÑ€Ç:ô%ú2Ò¹C_¢/;U_ÞóبâÞÇFãµê–¾„e||||lìØ /xmðÀƒ\qÅIØ™8b(FBèËÚå}Ùà¼%§/ýçBŒ®£ylE0˜ ó®}{Ÿ{þù;î¼+êŽÿyäø9~·ø­µæo;ÏC>îE\ÿm&j‚Q× 13qG×Ûˆ9‰òª­ª)ÆHE—xq‰ˆþT$r½ r"ç%Lâ2DQôª®È‹V± •eGʺ’ŠåˆÜ–eÈ# Ü>ðJ–«ƒ ñÖêÜæé£]‡V;6°¯¯÷ü=»Õ^3Т\xáþÝ0xà7¿ù;-;ú2°ÆL})Зá+H¸dçææüÃ_vuåÆFG´ L€äÙ01±oïå<òh¤Î˜•ÜýãßR˜í‹æ± fÏÔ®þ¾>Ê¡=¿ð‚ ~õë_{mpâÄ ‰‰¾ôH} ôeì .Ù@})„X7Â>­  B*x„¤ÑuÝz`R#Ú†Q4ÊcMÓBlžœÜ<9yäÈ‘»tE Ð ô÷[ÁÑ‘ËJm»Ë²-•2­¯ÔeÓ4¬ –¥”]—wb¢…¼Ykº^îÀ,¥Úo¶T¼]ôÊc/¼pìØ1¯¤¼$&úÒ#ôe,З±+H¸dÃèË­›'»»»u½Ü †©,«m¾aš«Í»aš†¹º‹i¦Úž{´í^ 8m{UÍ>3ù@çàs×+*ÿÕeuVŸB¡`Íä³²²b}µ¼¼¼²\Þl~þekùå—_B<òèc>öKÊ iéï뛜Ü840h½‚ªséû´ ™lyº†\W—µœJ¥ÒkBSÉGB‘ë*Ïü Nä5óƒúÆëóöÛi3ù¨wmu&õ&žÏç­å¯ÿ¿ÿâÿ[ëÖ­›šš]^оŒú(>ô¥@_†¯ á’ £/»»º¶nÙ,„°Ô¤°O¡–UÚv¯Ù{R¦m&Ÿ¬:“Ò†«ÛhšæÚžwBž,v¨9¯~ÕEù“ŸÞŠ Ùèïë;oÏ“ö´=o}ë[ü78qâă>xüøq¾ôL} ôeì .ÙG}4¼¾„Ö…QäuâüóÏ;ÿüó~ó›ß¾ðÂñާ@˦ûûúúèùÒ ŒŽ¾õ­oùÉOî÷ßìÁœšššÚ=å³ ú2v*èK¾ _AB$;77wäÈ‘S§æwG_¶Œ"€"ä(ru3Ç(rk3uùÊÊŠmùÂ|yùåy×õGŸ?úüÑc¿ù-õÔ›þþÁÁÉMW߈”1}¶Ǻ¡+1Ñ2éòèBÏQäf*¥Œ"O¥S•ë…Ù\yä¸:Š\ÝÆ…“Qä"Ü(òb±hÅ{B,--YË BˆãÇÿûÿùIàï–F”¯]ç–¥0ùulAi /k•[ôeƒó–¾ùä“O>ù¤å1Ñ—±SA_ ôeø â›l$w)Ö&§Ýk0˜MÄ®;¬®;wœ»¼¼l}¥T/ËCäd±ÜEJi=_út2hc:ª‹"Äcݺu!%¦P<殩©€MÑ—1A_†<­­/£ºK¾l¿û;ï3Й„E®†ÓRG‘ ë«B¡ Ž"_X§&Ö¢n•XT–——ma4 uÙ=€—#"g9·ÞS=Dï;>OЪï2.«4Ûz]W–•>˜ÎQäº2ŠÜŠƒiaF‘ç” –¦išÊÈqÓm¢"í1êÐ+nš`¹Ûz×QäêÚqÍçóŽ»ó‰ÙÙx0R~FÖ­+…ÈŒy;E_ÆL%Ü¢/œ·*ôeøx—Ý]][·l±nޏ^a=²50±½=WF‘«mµ:Š\MÖT–EÇ2Nø®Á€Î$Ƭ>ƒé/ÎüÜ ¿IDATj3…yÕ`*˪Á\YYYYQÂhF4˜ê²úW±âxy€V}• ~ ´LåõÕ±\Á´ÅMó°–†òö+ìÖÒöÆkš¼ñ†¼Y{ÝûŠÅ¢T ¦2”A5˜VLÌãÇ?þøÇOœˆš±’Ê,ÖΠ/c¦’@nÑ— Î[ôì9rXJU\–èîêÚ¶u‹¦ÙÚvÈf0ÕXÆ>í¹W,㔇Áôi·1˜0Š:‘ÑÑÑ7½iðÿãäÉ“‘vœ=qböÄ k¾‘‘5•éIJŠš¨"—4d¸ì$pD‰l”È5ù]Y·#Œù;2‰”dÑÍÍÍÍÍÍU“ÂÖ-›{9Þ¦`0 syãÅÏÎÎøÙÏb§0;{bvö% Ð@ººº¶nÞ¬v½‡6ƒ -Œ:â^zŒÖô #`†,ʱ±±÷üá>ù»§~÷»ƒ”'@˱më–î®.5憮ëºnXËê(rCî­F 1MeY $b†n-ÛCj2w\=Á`@§¿í”Pƒøx=èºn{GZ‹´¥Ûç[ðúC/?­¬¬¬¸Å¾t„Ô\Vâ`.«s‘ÊËê ^qÁ„}ˆQ° ›tµ ÷‘VÊöî±Òt{L5x¥øL›f†õl†õ’¬ëºšTÊãåÙq÷!VZ¤û¸µ2¤µ´Ý×n‹»vîqÝc«±ŒMÓ4=âÛBj*Ë´çµ€>˜Álß¾M±iÓF!Äÿý¿Ï! ÅÂÌ̳” @Rôtw÷ôt !zzzz{z)°À`DcË–ÍBˆåååÉM›JkŠ…¢5BÊòøŒ¢}¬F±¨v&e´t"áº(Ò?l0g4/ôÁÂþo¿jǯð4ºÑLêºW_ uw5hNQ‰wY(–»l8â`2å\^q0Õ(œjø/G–в­DèýÍÑ:GÛ\h^11­àh‰Ò+¦¦kjS¯ÆÁL©Ï”õšš”²¬)áØw¯ÜúÜMÀ¿|l7t5’©Ç¾º®×63”Ø—¦a Eå:Qâ¨*q÷Lõæ®ÜЋÅb±¥Úѳèz³µÝ©¹ C'µíšp©ÂšæhÃÕ–S©†m½­=7ÊË^q0½– Ã×X×£6ڴ籡&4/Lh^0˜Ð¼ š¦y…ËÔÔà8^ñ1Õ(?ºn…ÄÒ•eaº¥©ÜÔ8˜RÊò̧ž™Jážf;€Öm‡=ØÈq0µrðJ] ^i‹ƒi*±/Õxj겦•£pªÉªë!‘3î±ZýJ=;^Û¨q0uÃðŠL­+±ö S ¯¹\°©îRTb_Jû\ä5¦-&¦×¼äœ|h*Ü€ ¥µtÄ5öjÃÕ¸´j»­¬Tc !Ri%®qÊ}Y×uפ4¥mÞq¡`0â¼#I)ÃLò㕬¦iše-ï¨NàHJ/”—ÕmŠö™|DyÙþ¶†Á€öm«mëmSY¯«3?¸kGu*uYØ ¦.)×eÁì=Ña94/Lh^EP->11½F”«›Ie¹cТWR^£È0=~š‘ãÐ~í°ûzu9úÐo¯Ñâºw´#Ä¡>CŒ»³WRj¯Û¨ÌÚ'¦*fù6ÎMÚ¥n7Ú ÄÁ´‚뺵™ãXazÄ2v´íå8˜j®t¶½Q`0ª}ò‰‰é³^ ^i-ëºîó+ÖrÑþÂãÓû 0˜ÐÆmµm½ºQ;: f˜eŸ{˜MÞŠ¿BL×’w\Žû¨ë¾E£ÌZµ–Å¢´Le:>Û Ý=çRp׆¶¨ž"L3¨|eoQÕù- ef-Ýþ/R®Ó'®±×Ìlº=,²æ"™¸Æõ„Qäм`0 ya9@„x.<â`J{ K•0£ËE4¼¶õê²÷¸?¯Qä^㎽††Ì4ÕY ^©+‘öA®mËÊT5¶_Ñ6rܳhE^¾~tÊNm¢’ì¬Óî°ŽíÃD¦vÄ»ô\ï•7zèÔ†WóN×õ'B¯ŒÈ2äQp;hôÁ€æƒ Í š“"€öÆ+tWøHÇöêŸê.^ë}R€ê)bEœ¬CRÐ(0˜-ðÇ‹a@£ - £È yÁ`@óÂ(r€j j“à!ël×Ñ‚T|S®r†€¶§1ÚB0Šš &4/ÄÁh0Œfðk$‰µ^¨šþU Í`94/Lh^0˜Ð¼ Áðn +! ôÁ€æƒ Í šâ`@óBLh^0˜Ð¼`0 yÁ`@óòÿuŽ~2ŠÑIEND®B`‚python-cx_Oracle-8.3.0/samples/tutorial/rowfactory.py000066400000000000000000000014261414105416400230230ustar00rootroot00000000000000#------------------------------------------------------------------------------ # rowfactory.py (Section 8.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import collections import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute("select deptno, dname from dept") res = cur.fetchall() print('Array indexes:') for row in res: print(row[0], "->", row[1]) print('Loop target variables:') for c1, c2 in res: print(c1, "->", c2) python-cx_Oracle-8.3.0/samples/tutorial/soda.py000066400000000000000000000027361414105416400215570ustar00rootroot00000000000000#------------------------------------------------------------------------------ # soda.py (Section 11.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) soda = con.getSodaDatabase() # Explicit metadata is used for maximum version portability metadata = { "keyColumn": { "name":"ID" }, "contentColumn": { "name": "JSON_DOCUMENT", "sqlType": "BLOB" }, "versionColumn": { "name": "VERSION", "method": "UUID" }, "lastModifiedColumn": { "name": "LAST_MODIFIED" }, "creationTimeColumn": { "name": "CREATED_ON" } } collection = soda.createCollection("friends", metadata) content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}} doc = collection.insertOneAndGet(content) key = doc.key doc = collection.find().key(key).getOne() content = doc.getContent() print('Retrieved SODA document dictionary is:') print(content) python-cx_Oracle-8.3.0/samples/tutorial/solutions/000077500000000000000000000000001414105416400223065ustar00rootroot00000000000000python-cx_Oracle-8.3.0/samples/tutorial/solutions/aq-dequeue.py000066400000000000000000000017711414105416400247220ustar00rootroot00000000000000#------------------------------------------------------------------------------ # aq-dequeue.py (Section 10.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import decimal import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() BOOK_TYPE_NAME = "UDT_BOOK" QUEUE_NAME = "BOOKS" QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" # Dequeue the messages booksType = con.gettype(BOOK_TYPE_NAME) queue = con.queue(QUEUE_NAME, booksType) queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT queue.deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE print("\nDequeuing messages...") while True: props = queue.deqOne() if not props: break print(props.payload.TITLE) print("\nDone.") python-cx_Oracle-8.3.0/samples/tutorial/solutions/aq-enqueue.py000066400000000000000000000023171414105416400247310ustar00rootroot00000000000000#------------------------------------------------------------------------------ # aq-enqueue.py (Section 10.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import decimal import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() BOOK_TYPE_NAME = "UDT_BOOK" QUEUE_NAME = "BOOKS" QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" # Enqueue a few messages print("Enqueuing messages...") BOOK_DATA = [ ("The Fellowship of the Ring", "Tolkien, J.R.R.", decimal.Decimal("10.99")), ("Harry Potter and the Philosopher's Stone", "Rowling, J.K.", decimal.Decimal("7.99")) ] booksType = con.gettype(BOOK_TYPE_NAME) queue = con.queue(QUEUE_NAME, booksType) for title, authors, price in BOOK_DATA: book = booksType.newobject() book.TITLE = title book.AUTHORS = authors book.PRICE = price print(title) queue.enqOne(con.msgproperties(payload=book, expiration=4)) con.commit() python-cx_Oracle-8.3.0/samples/tutorial/solutions/aq-queuestart.py000066400000000000000000000031231414105416400254600ustar00rootroot00000000000000#------------------------------------------------------------------------------ # aq-queuestart.py (Section 10.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import decimal import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() BOOK_TYPE_NAME = "UDT_BOOK" QUEUE_NAME = "BOOKS" QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" # Cleanup cur.execute( """begin dbms_aqadm.stop_queue('""" + QUEUE_NAME + """'); dbms_aqadm.drop_queue('""" + QUEUE_NAME + """'); dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """'); execute immediate 'drop type """ + BOOK_TYPE_NAME + """'; exception when others then if sqlcode <> -24010 then raise; end if; end;""") # Create a type print("Creating books type UDT_BOOK...") cur.execute(""" create type %s as object ( title varchar2(100), authors varchar2(100), price number(5,2) );""" % BOOK_TYPE_NAME) # Create queue table and queue and start the queue print("Creating queue table...") cur.callproc("dbms_aqadm.create_queue_table", (QUEUE_TABLE_NAME, BOOK_TYPE_NAME)) cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME)) cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,)) python-cx_Oracle-8.3.0/samples/tutorial/solutions/bind_insert.py000066400000000000000000000020321414105416400251550ustar00rootroot00000000000000#------------------------------------------------------------------------------ # bind_insert.py (Section 4.3) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() rows = [ (1, "First" ), (2, "Second" ), (3, "Third" ), (4, "Fourth" ), (5, "Fifth" ), (6, "Sixth" ), (6, "Duplicate" ), (7, "Seventh" ) ] cur.executemany("insert into mytab(id, data) values (:1, :2)", rows, batcherrors = True) for error in cur.getbatcherrors(): print("Error", error.message.rstrip(), "at row offset", error.offset) # Now query the results back cur2 = con.cursor() cur2.execute('select * from mytab') res = cur2.fetchall() print(res) python-cx_Oracle-8.3.0/samples/tutorial/solutions/bind_sdo.py000066400000000000000000000051721414105416400244460ustar00rootroot00000000000000#------------------------------------------------------------------------------ # bind_sdo.py (Section 4.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() # Create table cur.execute("""begin execute immediate 'drop table testgeometry'; exception when others then if sqlcode <> -942 then raise; end if; end;""") cur.execute("""create table testgeometry ( id number(9) not null, geometry MDSYS.SDO_GEOMETRY not null)""") # Create and populate Oracle objects typeObj = con.gettype("MDSYS.SDO_GEOMETRY") elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY") obj = typeObj.newobject() obj.SDO_GTYPE = 2003 obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject() obj.SDO_ELEM_INFO.extend([1, 1003, 3]) obj.SDO_ORDINATES = ordinateTypeObj.newobject() obj.SDO_ORDINATES.extend([1, 1, 5, 7]) pointTypeObj = con.gettype("MDSYS.SDO_POINT_TYPE") obj.SDO_POINT = pointTypeObj.newobject() obj.SDO_POINT.X = 1 obj.SDO_POINT.Y = 2 obj.SDO_POINT.Z = 3 print("Created object", obj) # Add a new row print("Adding row to table...") cur.execute("insert into testgeometry values (1, :objbv)", objbv = obj) print("Row added!") # (Change below here) # Define a function to dump the contents of an Oracle object def dumpobject(obj, prefix = " "): if obj.type.iscollection: print(prefix, "[") for value in obj.aslist(): if isinstance(value, cx_Oracle.Object): dumpobject(value, prefix + " ") else: print(prefix + " ", repr(value)) print(prefix, "]") else: print(prefix, "{") for attr in obj.type.attributes: value = getattr(obj, attr.name) if isinstance(value, cx_Oracle.Object): print(prefix + " " + attr.name + " :") dumpobject(value, prefix + " ") else: print(prefix + " " + attr.name + " :", repr(value)) print(prefix, "}") # Query the row print("Querying row just inserted...") cur.execute("select id, geometry from testgeometry") for (id, obj) in cur: print("Id: ", id) dumpobject(obj) python-cx_Oracle-8.3.0/samples/tutorial/solutions/connect_pool2.py000066400000000000000000000025731414105416400254330ustar00rootroot00000000000000#------------------------------------------------------------------------------ # connect_pool2.py (Section 2.5) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import threading import time import db_config pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn + ":pooled", min = 2, max = 5, increment = 1, threaded = True, getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT) def Query(): con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF) #con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_NEW) cur = con.cursor() for i in range(4): cur.execute("select myseq.nextval from dual") seqval, = cur.fetchone() print("Thread", threading.current_thread().name, "fetched sequence =", seqval) #time.sleep(1) numberOfThreads = 5 threadArray = [] for i in range(numberOfThreads): thread = threading.Thread(name='#'+str(i), target=Query) threadArray.append(thread) #time.sleep(4) thread.start() for t in threadArray: t.join() print("All done!") python-cx_Oracle-8.3.0/samples/tutorial/solutions/db_config.py000066400000000000000000000001761414105416400245760ustar00rootroot00000000000000import os dirName = os.path.dirname(os.path.dirname(__file__)) exec(open(os.path.join(dirName, "db_config.py"), "r").read()) python-cx_Oracle-8.3.0/samples/tutorial/solutions/query-2.py000066400000000000000000000012451414105416400241660ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query.py (Section 1.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute("select * from dept order by deptno") res = cur.fetchall() for row in res: print(row) cur.close() con.close() python-cx_Oracle-8.3.0/samples/tutorial/solutions/query.py000066400000000000000000000012241414105416400240240ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query.py (Section 1.3 and 1.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute("select * from dept order by deptno") res = cur.fetchall() for row in res: print(row) python-cx_Oracle-8.3.0/samples/tutorial/solutions/query_many.py000066400000000000000000000013241414105416400250510ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query_many.py (Section 3.3) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute("select * from dept order by deptno") res = cur.fetchmany(numRows=3) print(res) print(res[0]) # first row print(res[0][1]) # second element of first row python-cx_Oracle-8.3.0/samples/tutorial/solutions/query_scroll.py000066400000000000000000000015771414105416400254150ustar00rootroot00000000000000#------------------------------------------------------------------------------ # query_scroll.py (Section 3.4) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor(scrollable = True) cur.execute("select * from dept order by deptno") cur.scroll(2, mode = "absolute") # go to second row print(cur.fetchone()) cur.scroll(-1) # go back one row print(cur.fetchone()) cur.scroll(1) # go to next row print(cur.fetchone()) cur.scroll(mode = "first") # go to first row print(cur.fetchone()) python-cx_Oracle-8.3.0/samples/tutorial/solutions/rowfactory.py000066400000000000000000000017761414105416400250720ustar00rootroot00000000000000#------------------------------------------------------------------------------ # rowfactory.py (Section 8.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import collections import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() cur.execute("select deptno, dname from dept") res = cur.fetchall() print('Array indexes:') for row in res: print(row[0], "->", row[1]) print('Loop target variables:') for c1, c2 in res: print(c1, "->", c2) print('Rowfactory:') cur.execute("select deptno, dname from dept") cur.rowfactory = collections.namedtuple("MyClass", ["DeptNumber", "DeptName"]) res = cur.fetchall() for row in res: print(row.DeptNumber, "->", row.DeptName) python-cx_Oracle-8.3.0/samples/tutorial/solutions/soda.py000066400000000000000000000041301414105416400236040ustar00rootroot00000000000000#------------------------------------------------------------------------------ # soda.py (Section 11.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) soda = con.getSodaDatabase() # Explicit metadata is used for maximum version portability metadata = { "keyColumn": { "name":"ID" }, "contentColumn": { "name": "JSON_DOCUMENT", "sqlType": "BLOB" }, "versionColumn": { "name": "VERSION", "method": "UUID" }, "lastModifiedColumn": { "name": "LAST_MODIFIED" }, "creationTimeColumn": { "name": "CREATED_ON" } } collection = soda.createCollection("friends", metadata) content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}} doc = collection.insertOneAndGet(content) key = doc.key doc = collection.find().key(key).getOne() content = doc.getContent() print('Retrieved SODA document dictionary is:') print(content) myDocs = [ {'name': 'Gerald', 'age': 21, 'address': {'city': 'London'}}, {'name': 'David', 'age': 28, 'address': {'city': 'Melbourne'}}, {'name': 'Shawn', 'age': 20, 'address': {'city': 'San Francisco'}} ] collection.insertMany(myDocs) filterSpec = { "address.city": "Melbourne" } myDocuments = collection.find().filter(filterSpec).getDocuments() print('Melbourne people:') for doc in myDocuments: print(doc.getContent()["name"]) filterSpec = {'age': {'$lt': 25}} myDocuments = collection.find().filter(filterSpec).getDocuments() print('Young people:') for doc in myDocuments: print(doc.getContent()["name"]) python-cx_Oracle-8.3.0/samples/tutorial/solutions/subclass.py000066400000000000000000000024201414105416400244750ustar00rootroot00000000000000#------------------------------------------------------------------------------ # subclass.py (Section 9.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config class MyConnection(cx_Oracle.Connection): def __init__(self): print("Connecting to database") return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn) def cursor(self): return MyCursor(self) class MyCursor(cx_Oracle.Cursor): def execute(self, statement, args): print("Executing:", statement) print("Arguments:") for argIndex, arg in enumerate(args): print(" Bind", argIndex + 1, "has value", repr(arg)) return super(MyCursor, self).execute(statement, args) def fetchone(self): print("Fetchone()") return super(MyCursor, self).fetchone() con = MyConnection() cur = con.cursor() cur.execute("select count(*) from emp where deptno = :bv", (10,)) count, = cur.fetchone() print("Number of rows:", count) python-cx_Oracle-8.3.0/samples/tutorial/solutions/type_converter.py000066400000000000000000000016341414105416400257340ustar00rootroot00000000000000#------------------------------------------------------------------------------ # type_converter.py (Section 6.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import decimal import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale): if defaultType == cx_Oracle.NUMBER: return cursor.var(str, 9, cursor.arraysize, outconverter = decimal.Decimal) cur.outputtypehandler = ReturnNumbersAsDecimal for value, in cur.execute("select 0.1 from dual"): print("Value:", value, "* 3 =", value * 3) python-cx_Oracle-8.3.0/samples/tutorial/solutions/type_output.py000066400000000000000000000017311414105416400252630ustar00rootroot00000000000000#------------------------------------------------------------------------------ # type_output.py (Section 6.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() print("Standard output...") for row in cur.execute("select * from dept"): print(row) def ReturnNumbersAsStrings(cursor, name, defaultType, size, precision, scale): if defaultType == cx_Oracle.NUMBER: return cursor.var(str, 9, cursor.arraysize) print("Output type handler output...") cur = con.cursor() cur.outputtypehandler = ReturnNumbersAsStrings for row in cur.execute("select * from dept"): print(row) python-cx_Oracle-8.3.0/samples/tutorial/solutions/versions.py000066400000000000000000000012131414105416400245250ustar00rootroot00000000000000#------------------------------------------------------------------------------ # versions.py (Section 1.5) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) print(cx_Oracle.version) print("Database version:", con.version) print("Client version:", cx_Oracle.clientversion()) python-cx_Oracle-8.3.0/samples/tutorial/sql/000077500000000000000000000000001414105416400210465ustar00rootroot00000000000000python-cx_Oracle-8.3.0/samples/tutorial/sql/create_user.sql000066400000000000000000000031061414105416400240700ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * create_user.sql * Creates a database user for the cx_Oracle tutorial * * Then run this like: * * sqlplus -l system/systempassword@localhost/orclpdb1 @create_user * * Substitute your actual password and connection string. * For Oracle Autonmous Database, use 'admin' instead of system. * You will be prompted for the new username and the new password to use. * * When you no longer need this user, run drop_user.sql to drop the user * *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ whenever sqlerror exit failure set verify off feedback off accept user char prompt 'Enter new database username: ' create user &user; grant create session, create table, create procedure, create type, create sequence, select any dictionary, unlimited tablespace to &user; begin for r in ( select role from dba_roles where role in ('SODA_APP', 'AQ_ADMINISTRATOR_ROLE') ) loop execute immediate 'grant ' || r.role || ' to &user'; end loop; end; / accept pw char prompt 'Enter password for &user: ' hide alter user &user identified by "&pw"; prompt prompt Database user &user has been created. prompt Now you should run setup_tables.sql prompt quit python-cx_Oracle-8.3.0/samples/tutorial/sql/drop_user.sql000066400000000000000000000026261414105416400235770ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * drop_user.sql * Drops the database user used for the cx_Oracle tutorial * * Run this like: * * sqlplus -l system/systempassword@localhost/orclpdb1 @drop_user * * Substitute your actual password and connection string. * For Oracle Autonmous Database, use 'admin' instead of system. * You will be prompted for the user to drop. * *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ whenever sqlerror exit failure set verify off feedback off prompt WARNING: this scripts drops a user from the database accept user char prompt 'Enter username to drop: ' begin dbms_aqadm.stop_queue('BOOKS'); dbms_aqadm.drop_queue('BOOKS'); dbms_aqadm.drop_queue_table('BOOK_QUEUE_TABLE'); exception when others then if sqlcode <> -24010 then raise; end if; end; / begin for r in ( select username from dba_users where username in (upper('&user')) ) loop execute immediate 'drop user ' || r.username || ' cascade'; end loop; end; / prompt prompt User &user has been dropped prompt quit python-cx_Oracle-8.3.0/samples/tutorial/sql/setup_tables.sql000066400000000000000000000073401414105416400242650ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * setup_tables.sql * Creates the tables etc used by the cx_Oracle tutorial. * * Run this like: * * sqlplus -l pythonhol/welcome@localhost/orclpdb1 @setup_tables * * Substitute your actual password and connection string. * You may want to run create_user.sql before running this. * *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ whenever sqlerror exit failure -- EMP/DEPT tables begin execute immediate 'drop table emp'; exception when others then if sqlcode <> -942 then raise; end if; end; / create table emp (empno number(4) not null, ename varchar2(10), job varchar2(9), mgr number(4), hiredate date, sal number(7, 2), comm number(7, 2), deptno number(2)); insert into emp values (7369, 'SMITH', 'CLERK', 7902, to_date('17-DEC-1980', 'DD-MON-YYYY'), 800, NULL, 20); insert into emp values (7499, 'ALLEN', 'SALESMAN', 7698, to_date('20-FEB-1981', 'DD-MON-YYYY'), 1600, 300, 30); insert into emp values (7521, 'WARD', 'SALESMAN', 7698, to_date('22-FEB-1981', 'DD-MON-YYYY'), 1250, 500, 30); insert into emp values (7566, 'JONES', 'MANAGER', 7839, to_date('2-APR-1981', 'DD-MON-YYYY'), 2975, NULL, 20); insert into emp values (7654, 'MARTIN', 'SALESMAN', 7698, to_date('28-SEP-1981', 'DD-MON-YYYY'), 1250, 1400, 30); insert into emp values (7698, 'BLAKE', 'MANAGER', 7839, to_date('1-MAY-1981', 'DD-MON-YYYY'), 2850, NULL, 30); insert into emp values (7782, 'CLARK', 'MANAGER', 7839, to_date('9-JUN-1981', 'DD-MON-YYYY'), 2450, NULL, 10); insert into emp values (7788, 'SCOTT', 'ANALYST', 7566, to_date('09-DEC-1982', 'DD-MON-YYYY'), 3000, NULL, 20); insert into emp values (7839, 'KING', 'PRESIDENT', NULL, to_date('17-NOV-1981', 'DD-MON-YYYY'), 5000, NULL, 10); insert into emp values (7844, 'TURNER', 'SALESMAN', 7698, to_date('8-SEP-1981', 'DD-MON-YYYY'), 1500, 0, 30); insert into emp values (7876, 'ADAMS', 'CLERK', 7788, to_date('12-JAN-1983', 'DD-MON-YYYY'), 1100, NULL, 20); insert into emp values (7900, 'JAMES', 'CLERK', 7698, to_date('3-DEC-1981', 'DD-MON-YYYY'), 950, NULL, 30); insert into emp values (7902, 'FORD', 'ANALYST', 7566, to_date('3-DEC-1981', 'DD-MON-YYYY'), 3000, NULL, 20); insert into emp values (7934, 'MILLER', 'CLERK', 7782, to_date('23-JAN-1982', 'DD-MON-YYYY'), 1300, NULL, 10); begin execute immediate 'drop table dept'; exception when others then if sqlcode <> -942 then raise; end if; end; / create table dept (deptno number(2), dname varchar2(14), loc varchar2(13) ); insert into dept values (10, 'ACCOUNTING', 'NEW YORK'); insert into dept values (20, 'RESEARCH', 'DALLAS'); insert into dept values (30, 'SALES', 'CHICAGO'); insert into dept values (40, 'OPERATIONS', 'BOSTON'); commit; -- Table for clob.py and clob_string.py begin execute immediate 'drop table testclobs'; exception when others then if sqlcode <> -942 then raise; end if; end; / create table testclobs ( id number not null, myclob clob not null ); -- Sequence for connect_pool.py begin execute immediate 'drop sequence myseq'; exception when others then if sqlcode <> -2289 then raise; end if; end; / create sequence myseq; quit python-cx_Oracle-8.3.0/samples/tutorial/subclass.py000066400000000000000000000015061414105416400224420ustar00rootroot00000000000000#------------------------------------------------------------------------------ # subclass.py (Section 9.1 and 9.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config class MyConnection(cx_Oracle.Connection): def __init__(self): print("Connecting to database") return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn) con = MyConnection() cur = con.cursor() cur.execute("select count(*) from emp where deptno = :bv", (10,)) count, = cur.fetchone() print("Number of rows:", count) python-cx_Oracle-8.3.0/samples/tutorial/type_converter.py000066400000000000000000000012211414105416400236650ustar00rootroot00000000000000#------------------------------------------------------------------------------ # type_converter.py (Section 6.2) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() for value, in cur.execute("select 0.1 from dual"): print("Value:", value, "* 3 =", value * 3) python-cx_Oracle-8.3.0/samples/tutorial/type_input.py000066400000000000000000000057661414105416400230370ustar00rootroot00000000000000#------------------------------------------------------------------------------ # type_input.py (Section 6.3) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() # Create table cur.execute("""begin execute immediate 'drop table testgeometry'; exception when others then if sqlcode <> -942 then raise; end if; end;""") cur.execute("""create table testgeometry ( id number(9) not null, geometry MDSYS.SDO_GEOMETRY not null)""") # Create a Python class for an SDO class mySDO(object): def __init__(self, gtype, elemInfo, ordinates): self.gtype = gtype self.elemInfo = elemInfo self.ordinates = ordinates # Get Oracle type information objType = con.gettype("MDSYS.SDO_GEOMETRY") elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY") # Convert a Python object to MDSYS.SDO_GEOMETRY def SDOInConverter(value): obj = objType.newobject() obj.SDO_GTYPE = value.gtype obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject() obj.SDO_ELEM_INFO.extend(value.elemInfo) obj.SDO_ORDINATES = ordinateTypeObj.newobject() obj.SDO_ORDINATES.extend(value.ordinates) return obj def SDOInputTypeHandler(cursor, value, numElements): if isinstance(value, mySDO): return cursor.var(cx_Oracle.OBJECT, arraysize = numElements, inconverter = SDOInConverter, typename = objType.name) sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7]) # Python object cur.inputtypehandler = SDOInputTypeHandler cur.execute("insert into testgeometry values (:1, :2)", (1, sdo)) # Define a function to dump the contents of an Oracle object def dumpobject(obj, prefix = " "): if obj.type.iscollection: print(prefix, "[") for value in obj.aslist(): if isinstance(value, cx_Oracle.Object): dumpobject(value, prefix + " ") else: print(prefix + " ", repr(value)) print(prefix, "]") else: print(prefix, "{") for attr in obj.type.attributes: value = getattr(obj, attr.name) if isinstance(value, cx_Oracle.Object): print(prefix + " " + attr.name + " :") dumpobject(value, prefix + " ") else: print(prefix + " " + attr.name + " :", repr(value)) print(prefix, "}") # Query the row print("Querying row just inserted...") cur.execute("select id, geometry from testgeometry") for (id, obj) in cur: print("Id: ", id) dumpobject(obj) python-cx_Oracle-8.3.0/samples/tutorial/type_output.py000066400000000000000000000012051414105416400232200ustar00rootroot00000000000000#------------------------------------------------------------------------------ # type_output.py (Section 6.1) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) cur = con.cursor() print("Standard output...") for row in cur.execute("select * from dept"): print(row) python-cx_Oracle-8.3.0/samples/tutorial/versions.py000066400000000000000000000010571414105416400224740ustar00rootroot00000000000000#------------------------------------------------------------------------------ # versions.py (Section 1.5) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ import cx_Oracle import db_config con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) print(cx_Oracle.version) python-cx_Oracle-8.3.0/samples/type_handlers.py000066400000000000000000000062041414105416400216210ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # type_handlers.py # This script demonstrates the use of input and output type handlers as well # as variable input and output converters. These methods can be used to extend # cx_Oracle in many ways. This script demonstrates the binding and querying of # SQL objects as Python objects. # # This script requires cx_Oracle 5.0 and higher. #------------------------------------------------------------------------------ import datetime import cx_Oracle as oracledb import sample_env con = oracledb.connect(sample_env.get_main_connect_string()) obj_type = con.gettype("UDT_BUILDING") class Building: def __init__(self, building_id, description, num_floors, date_built): self.building_id = building_id self.description = description self.num_floors = num_floors self.date_built = date_built def __repr__(self): return "" % (self.building_id, self.description) def building_in_converter(value): obj = obj_type.newobject() obj.BUILDINGID = value.building_id obj.DESCRIPTION = value.description obj.NUMFLOORS = value.num_floors obj.DATEBUILT = value.date_built return obj def building_out_converter(obj): return Building(int(obj.BUILDINGID), obj.DESCRIPTION, int(obj.NUMFLOORS), obj.DATEBUILT) def input_type_handler(cursor, value, num_elements): if isinstance(value, Building): return cursor.var(obj_type, arraysize=num_elements, inconverter=building_in_converter) def output_type_handler(cursor, name, default_type, size, precision, scale): if default_type == oracledb.OBJECT: return cursor.var(obj_type, arraysize=cursor.arraysize, outconverter=building_out_converter) buildings = [ Building(1, "The First Building", 5, datetime.date(2007, 5, 18)), Building(2, "The Second Building", 87, datetime.date(2010, 2, 7)), Building(3, "The Third Building", 12, datetime.date(2005, 6, 19)), ] cur = con.cursor() cur.inputtypehandler = input_type_handler for building in buildings: try: cur.execute("insert into TestBuildings values (:1, :2)", (building.building_id, building)) except oracledb.DatabaseError as e: error, = e.args print("CONTEXT:", error.context) print("MESSAGE:", error.message) raise print("NO OUTPUT TYPE HANDLER:") for row in cur.execute("select * from TestBuildings order by BuildingId"): print(row) print() cur = con.cursor() cur.outputtypehandler = output_type_handler print("WITH OUTPUT TYPE HANDLER:") for row in cur.execute("select * from TestBuildings order by BuildingId"): print(row) print() python-cx_Oracle-8.3.0/samples/universal_rowids.py000066400000000000000000000037031414105416400223600ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # universal_rowids.py # This script demonstrates the use of universal rowids. Universal rowids are # used to identify rows in index organized tables. # # This script requires cx_Oracle 6.0 and higher. #------------------------------------------------------------------------------ import datetime import cx_Oracle as oracledb import sample_env DATA = [ (1, "String #1", datetime.datetime(2017, 4, 4)), (2, "String #2", datetime.datetime(2017, 4, 5)), (3, "A" * 250, datetime.datetime(2017, 4, 6)) ] # truncate table so sample can be rerun connection = oracledb.connect(sample_env.get_main_connect_string()) cursor = connection.cursor() print("Truncating table...") cursor.execute("truncate table TestUniversalRowids") # populate table with a few rows print("Populating table...") for row in DATA: print("Inserting", row) cursor.execute("insert into TestUniversalRowids values (:1, :2, :3)", row) connection.commit() # fetch the rowids from the table rowids = [r for r, in cursor.execute("select rowid from TestUniversalRowids")] # fetch each of the rows given the rowid for rowid in rowids: print("-" * 79) print("Rowid:", rowid) cursor.execute(""" select IntCol, StringCol, DateCol from TestUniversalRowids where rowid = :rid""", rid = rowid) int_col, string_col, dateCol = cursor.fetchone() print("IntCol:", int_col) print("StringCol:", string_col) print("DateCol:", dateCol) python-cx_Oracle-8.3.0/setup.cfg000066400000000000000000000021061414105416400165600ustar00rootroot00000000000000[metadata] name = cx_Oracle description = Python interface to Oracle long_description = file: README.md long_description_content_type = text/markdown keywords = Oracle, database author = "Anthony Tuininga", author_email = "anthony.tuininga@gmail.com", license = BSD License url = https://oracle.github.io/python-cx_Oracle project_urls = Installation = https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html Samples = https://github.com/oracle/python-cx_Oracle/tree/main/samples Documentation = http://cx-oracle.readthedocs.io Release Notes = https://cx-oracle.readthedocs.io/en/latest/release_notes.html#releasenotes Issues = https://github.com/oracle/python-cx_Oracle/issues Source = https://github.com/oracle/python-cx_Oracle python_requires = >=3.6 classifiers = Development Status :: 6 - Mature Intended Audience :: Developers License :: OSI Approved :: BSD License Natural Language :: English Operating System :: OS Independent Programming Language :: C Programming Language :: Python :: 3 :: Only Topic :: Database python-cx_Oracle-8.3.0/setup.py000066400000000000000000000047021414105416400164550ustar00rootroot00000000000000""" Setup script for cx_Oracle. """ import os import pkg_resources import setuptools import sys # check minimum supported Python version if sys.version_info[:2] < (3, 6): raise Exception("Python 3.6 or higher is required. " + "For python 2, use 'pip install cx_Oracle==7.3'") # check minimum supported version of setuptools pkg_resources.require("setuptools>=40.6.0") # define build constants BUILD_VERSION = "8.3.0" # setup extra link and compile args extra_link_args = [] extra_compile_args = [] if sys.platform == "aix4": extra_compile_args.append("-qcpluscmt") elif sys.platform == "aix5": extra_compile_args.append("-DAIX5") elif sys.platform == "cygwin": extra_link_args.append("-Wl,--enable-runtime-pseudo-reloc") elif sys.platform == "darwin": extra_link_args.append("-shared-libgcc") # define cx_Oracle sources source_dir = "src" sources = [os.path.join(source_dir, n) \ for n in sorted(os.listdir(source_dir)) if n.endswith(".c")] depends = ["src/cxoModule.h"] # define ODPI-C sources, libraries and include directories; if the environment # variables ODPIC_INC_DIR and ODPIC_LIB_DIR are both set, assume these # locations contain a compiled installation of ODPI-C; otherwise, use the # source of ODPI-C found in the odpi subdirectory dpi_include_dir = os.environ.get("ODPIC_INC_DIR") dpi_lib_dir = os.environ.get("ODPIC_LIB_DIR") if dpi_include_dir and dpi_lib_dir: dpi_sources = [] include_dirs = [dpi_include_dir] libraries = ["odpic"] library_dirs = [dpi_lib_dir] else: include_dirs = ["odpi/include", "odpi/src"] dpi_source_dir = os.path.join("odpi", "src") dpi_sources = [os.path.join(dpi_source_dir, n) \ for n in sorted(os.listdir(dpi_source_dir)) if n.endswith(".c")] depends.extend(["odpi/include/dpi.h", "odpi/src/dpiImpl.h", "odpi/src/dpiErrorMessages.h"]) libraries = [] library_dirs = [] # setup the extension extension = setuptools.Extension( name="cx_Oracle", include_dirs=include_dirs, extra_compile_args=extra_compile_args, define_macros=[("CXO_BUILD_VERSION", BUILD_VERSION)], extra_link_args=extra_link_args, sources=sources + dpi_sources, depends=depends, libraries=libraries, library_dirs=library_dirs) # perform the setup setuptools.setup( version=BUILD_VERSION, data_files=[ ("cx_Oracle-doc", ["LICENSE.txt", "README.txt"]) ], ext_modules=[extension]) python-cx_Oracle-8.3.0/src/000077500000000000000000000000001414105416400155275ustar00rootroot00000000000000python-cx_Oracle-8.3.0/src/cxoApiType.c000066400000000000000000000062141414105416400177630ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoApiType.c // Defines the objects used for identifying types defined by the Python // Database API. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoApiType_free() // Free the API type object. //----------------------------------------------------------------------------- static void cxoApiType_free(cxoApiType *apiType) { Py_CLEAR(apiType->dbTypes); Py_TYPE(apiType)->tp_free((PyObject*) apiType); } //----------------------------------------------------------------------------- // cxoApiType_repr() // Return a string representation of a queue. //----------------------------------------------------------------------------- static PyObject *cxoApiType_repr(cxoApiType *apiType) { PyObject *module, *name, *apiTypeName, *result; apiTypeName = PyUnicode_DecodeASCII(apiType->name, strlen(apiType->name), NULL); if (!apiTypeName) return NULL; if (cxoUtils_getModuleAndName(Py_TYPE(apiType), &module, &name) < 0) { Py_DECREF(apiTypeName); return NULL; } result = cxoUtils_formatString("<%s.%s %s>", PyTuple_Pack(3, module, name, apiTypeName)); Py_DECREF(module); Py_DECREF(name); Py_DECREF(apiTypeName); return result; } //----------------------------------------------------------------------------- // cxoApiType_reduce() // Method provided for pickling/unpickling of API types. //----------------------------------------------------------------------------- static PyObject *cxoApiType_reduce(cxoApiType *apiType) { return PyUnicode_DecodeASCII(apiType->name, strlen(apiType->name), NULL); } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "__reduce__", (PyCFunction) cxoApiType_reduce, METH_NOARGS }, { NULL, NULL } }; //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "name", T_STRING, offsetof(cxoApiType, name), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // Python type declaration //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeApiType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.ApiType", .tp_basicsize = sizeof(cxoApiType), .tp_dealloc = (destructor) cxoApiType_free, .tp_repr = (reprfunc) cxoApiType_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMembers, .tp_methods = cxoMethods }; python-cx_Oracle-8.3.0/src/cxoBuffer.c000066400000000000000000000043641414105416400176250ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoBuffer.c // Defines buffer structure and routines for populating it. These are used // to translate Python objects into the buffers needed for Oracle, including // Unicode or buffer objects. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoBuffer_fromObject() // Populate the string buffer from a unicode object. //----------------------------------------------------------------------------- int cxoBuffer_fromObject(cxoBuffer *buf, PyObject *obj, const char *encoding) { cxoBuffer_init(buf); if (!obj || obj == Py_None) return 0; if (PyUnicode_Check(obj)) { buf->obj = PyUnicode_AsEncodedString(obj, encoding, NULL); if (!buf->obj) return -1; buf->ptr = PyBytes_AS_STRING(buf->obj); buf->size = (uint32_t) PyBytes_GET_SIZE(buf->obj); buf->numCharacters = (uint32_t) PyUnicode_GET_LENGTH(obj); } else if (PyBytes_Check(obj)) { Py_INCREF(obj); buf->obj = obj; buf->ptr = PyBytes_AS_STRING(buf->obj); buf->size = buf->numCharacters = (uint32_t) PyBytes_GET_SIZE(buf->obj); } else { PyErr_SetString(PyExc_TypeError, "expecting string or bytes object"); return -1; } return 0; } //----------------------------------------------------------------------------- // cxoBuffer_init() // Initialize the buffer with an empty string. Returns 0 as a convenience to // the caller. //----------------------------------------------------------------------------- int cxoBuffer_init(cxoBuffer *buf) { buf->ptr = NULL; buf->size = 0; buf->numCharacters = 0; buf->obj = NULL; return 0; } python-cx_Oracle-8.3.0/src/cxoConnection.c000066400000000000000000002304311414105416400205070ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoConnection.c // Definition of the Python type Connection. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // structure used to help in establishing a connection //----------------------------------------------------------------------------- typedef struct { const char *encoding; const char *nencoding; cxoBuffer userNameBuffer; cxoBuffer passwordBuffer; cxoBuffer newPasswordBuffer; cxoBuffer dsnBuffer; cxoBuffer connectionClassBuffer; cxoBuffer editionBuffer; cxoBuffer tagBuffer; uint32_t numAppContext; dpiAppContext *appContext; cxoBuffer *ctxNamespaceBuffers; cxoBuffer *ctxNameBuffers; cxoBuffer *ctxValueBuffers; dpiShardingKeyColumn *shardingKeyColumns; cxoBuffer *shardingKeyBuffers; uint32_t numShardingKeyColumns; dpiShardingKeyColumn *superShardingKeyColumns; uint32_t numSuperShardingKeyColumns; cxoBuffer *superShardingKeyBuffers; } cxoConnectionParams; //----------------------------------------------------------------------------- // cxoConnectionParams_initialize() // Initialize the parameters to default values. //----------------------------------------------------------------------------- static void cxoConnectionParams_initialize(cxoConnectionParams *params) { cxoBuffer_init(¶ms->userNameBuffer); cxoBuffer_init(¶ms->passwordBuffer); cxoBuffer_init(¶ms->newPasswordBuffer); cxoBuffer_init(¶ms->dsnBuffer); cxoBuffer_init(¶ms->connectionClassBuffer); cxoBuffer_init(¶ms->editionBuffer); cxoBuffer_init(¶ms->tagBuffer); params->numAppContext = 0; params->appContext = NULL; params->ctxNamespaceBuffers = NULL; params->ctxNameBuffers = NULL; params->ctxValueBuffers = NULL; params->numShardingKeyColumns = 0; params->shardingKeyColumns = NULL; params->shardingKeyBuffers = NULL; params->numSuperShardingKeyColumns = 0; params->superShardingKeyColumns = NULL; params->superShardingKeyBuffers = NULL; } //----------------------------------------------------------------------------- // cxoConnectionParams_ProcessContext() // Process context for the connection parameters. This validates that the // context passed in is a list of 3-tuples (namespace, name, value) and // populates the parametrs with buffers for each of these. //----------------------------------------------------------------------------- static int cxoConnectionParams_processContext(cxoConnectionParams *params, PyObject *context) { uint32_t numEntries, i; dpiAppContext *entry; PyObject *entryObj; size_t memorySize; // validate context is a list with at least one entry in it if (!context) return 0; if (!PyList_Check(context)) { PyErr_SetString(PyExc_TypeError, "appcontext should be a list of 3-tuples"); return -1; } numEntries = (uint32_t) PyList_GET_SIZE(context); if (numEntries == 0) return 0; // allocate memory for the buffers used to communicate with DPI params->appContext = PyMem_Malloc(numEntries * sizeof(dpiAppContext)); memorySize = numEntries * sizeof(cxoBuffer); params->ctxNamespaceBuffers = PyMem_Malloc(memorySize); params->ctxNameBuffers = PyMem_Malloc(memorySize); params->ctxValueBuffers = PyMem_Malloc(memorySize); if (!params->appContext || !params->ctxNamespaceBuffers || !params->ctxNameBuffers || !params->ctxValueBuffers) { PyErr_NoMemory(); return -1; } // initialize buffers for (i = 0; i < numEntries; i++) { cxoBuffer_init(¶ms->ctxNamespaceBuffers[i]); cxoBuffer_init(¶ms->ctxNameBuffers[i]); cxoBuffer_init(¶ms->ctxValueBuffers[i]); } params->numAppContext = numEntries; // process each entry for (i = 0; i < numEntries; i++) { entryObj = PyList_GET_ITEM(context, i); if (!PyTuple_Check(entryObj) || PyTuple_GET_SIZE(entryObj) != 3) { PyErr_SetString(PyExc_TypeError, "appcontext should be a list of 3-tuples"); return -1; } if (cxoBuffer_fromObject(¶ms->ctxNamespaceBuffers[i], PyTuple_GET_ITEM(entryObj, 0), params->encoding) < 0) return -1; if (cxoBuffer_fromObject(¶ms->ctxNameBuffers[i], PyTuple_GET_ITEM(entryObj, 1), params->encoding) < 0) return -1; if (cxoBuffer_fromObject(¶ms->ctxValueBuffers[i], PyTuple_GET_ITEM(entryObj, 2), params->encoding) < 0) return -1; entry = ¶ms->appContext[i]; entry->namespaceName = params->ctxNamespaceBuffers[i].ptr; entry->namespaceNameLength = params->ctxNamespaceBuffers[i].size; entry->name = params->ctxNameBuffers[i].ptr; entry->nameLength = params->ctxNameBuffers[i].size; entry->value = params->ctxValueBuffers[i].ptr; entry->valueLength = params->ctxValueBuffers[i].size; } return 0; } //----------------------------------------------------------------------------- // cxoConnectionParams_processShardingKeyValue() // Process a single sharding key value. //----------------------------------------------------------------------------- static int cxoConnectionParams_processShardingKeyValue( cxoConnectionParams *params, PyObject *value, dpiShardingKeyColumn *column, cxoBuffer *buffer) { dpiNativeTypeNum nativeTypeNum; cxoTransformNum transformNum; transformNum = cxoTransform_getNumFromPythonValue(value, 0); if (cxoTransform_fromPython(transformNum, &nativeTypeNum, value, &column->value, buffer, params->encoding, params->nencoding, NULL, 0) < 0) return -1; cxoTransform_getTypeInfo(transformNum, &column->oracleTypeNum, &column->nativeTypeNum); return 0; } //----------------------------------------------------------------------------- // cxoConnectionParams_processShardingKey() // Process either the sharding key or the super sharding key. A sharding key // is expected to be a sequence of values. A null value or a sequence of size // 0 is ignored. //----------------------------------------------------------------------------- static int cxoConnectionParams_processShardingKey(cxoConnectionParams *params, PyObject *shardingKeyObj, int isSuperShardingKey) { dpiShardingKeyColumn *columns; uint32_t i, numColumns; cxoBuffer *buffers; PyObject *value; // validate sharding key if (!shardingKeyObj || shardingKeyObj == Py_None) return 0; if (!PySequence_Check(shardingKeyObj)) { PyErr_SetString(PyExc_TypeError, "expecting a sequence"); return -1; } numColumns = (uint32_t) PySequence_Size(shardingKeyObj); if (numColumns == 0) return 0; // allocate memory for the sharding key values columns = PyMem_Calloc(numColumns, sizeof(dpiShardingKeyColumn)); buffers = PyMem_Calloc(numColumns, sizeof(cxoBuffer)); if (!columns || !buffers) { PyErr_NoMemory(); return -1; } if (isSuperShardingKey) { params->superShardingKeyColumns = columns; params->superShardingKeyBuffers = buffers; params->numSuperShardingKeyColumns = numColumns; } else { params->shardingKeyColumns = columns; params->shardingKeyBuffers = buffers; params->numShardingKeyColumns = numColumns; } // process each value for (i = 0; i < numColumns; i++) { value = PySequence_GetItem(shardingKeyObj, i); if (!value) return -1; if (cxoConnectionParams_processShardingKeyValue(params, value, &columns[i], &buffers[i]) < 0) return -1; Py_DECREF(value); } return 0; } //----------------------------------------------------------------------------- // cxoConnectionParams_finalize() // Finalize the parameters, freeing any resources that were allocated. The // return value is a convenience to the caller. //----------------------------------------------------------------------------- static int cxoConnectionParams_finalize(cxoConnectionParams *params) { uint32_t i; cxoBuffer_clear(¶ms->userNameBuffer); cxoBuffer_clear(¶ms->passwordBuffer); cxoBuffer_clear(¶ms->newPasswordBuffer); cxoBuffer_clear(¶ms->dsnBuffer); cxoBuffer_clear(¶ms->connectionClassBuffer); cxoBuffer_clear(¶ms->editionBuffer); cxoBuffer_clear(¶ms->tagBuffer); for (i = 0; i < params->numAppContext; i++) { cxoBuffer_clear(¶ms->ctxNamespaceBuffers[i]); cxoBuffer_clear(¶ms->ctxNameBuffers[i]); cxoBuffer_clear(¶ms->ctxValueBuffers[i]); } params->numAppContext = 0; if (params->appContext) { PyMem_Free(params->appContext); params->appContext = NULL; } if (params->ctxNamespaceBuffers) { PyMem_Free(params->ctxNamespaceBuffers); params->ctxNamespaceBuffers = NULL; } if (params->ctxNameBuffers) { PyMem_Free(params->ctxNameBuffers); params->ctxNameBuffers = NULL; } if (params->ctxValueBuffers) { PyMem_Free(params->ctxValueBuffers); params->ctxValueBuffers = NULL; } for (i = 0; i < params->numShardingKeyColumns; i++) cxoBuffer_clear(¶ms->shardingKeyBuffers[i]); if (params->shardingKeyColumns) { PyMem_Free(params->shardingKeyColumns); params->shardingKeyColumns = NULL; } if (params->shardingKeyBuffers) { PyMem_Free(params->shardingKeyBuffers); params->shardingKeyBuffers = NULL; } for (i = 0; i < params->numSuperShardingKeyColumns; i++) cxoBuffer_clear(¶ms->superShardingKeyBuffers[i]); if (params->superShardingKeyColumns) { PyMem_Free(params->superShardingKeyColumns); params->superShardingKeyColumns = NULL; } if (params->superShardingKeyBuffers) { PyMem_Free(params->superShardingKeyBuffers); params->superShardingKeyBuffers = NULL; } return -1; } //----------------------------------------------------------------------------- // cxoConnection_getSodaFlags() // Get the flags to use for SODA. This checks the autocommit flag and enables // atomic commit if set to a true value. It also checks to ensure that the // connection is valid. //----------------------------------------------------------------------------- int cxoConnection_getSodaFlags(cxoConnection *conn, uint32_t *flags) { if (cxoConnection_isConnected(conn) < 0) return -1; *flags = (conn->autocommit) ? DPI_SODA_FLAGS_ATOMIC_COMMIT : DPI_SODA_FLAGS_DEFAULT; return 0; } //----------------------------------------------------------------------------- // cxoConnection_isConnected() // Determines if the connection object is connected to the database. If not, // a Python exception is raised. //----------------------------------------------------------------------------- int cxoConnection_isConnected(cxoConnection *conn) { if (!conn->handle) { cxoError_raiseFromString(cxoInterfaceErrorException, "not connected"); return -1; } return 0; } //----------------------------------------------------------------------------- // cxoConnection_getAttrText() // Get the value of the attribute returned from the given function. The value // is assumed to be a text value. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getAttrText(cxoConnection *conn, int (*func)(dpiConn *conn, const char **value, uint32_t *valueLength)) { uint32_t valueLength; const char *value; if (cxoConnection_isConnected(conn) < 0) return NULL; if ((*func)(conn->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (!value) Py_RETURN_NONE; return PyUnicode_Decode(value, valueLength, conn->encodingInfo.encoding, NULL); } //----------------------------------------------------------------------------- // cxoConnection_setAttrText() // Set the value of the attribute using the given function. The value is // assumed to be a text value. //----------------------------------------------------------------------------- static int cxoConnection_setAttrText(cxoConnection *conn, PyObject *value, int (*func)(dpiConn *conn, const char *value, uint32_t valueLength)) { cxoBuffer buffer; int status; if (cxoConnection_isConnected(conn) < 0) return -1; if (cxoBuffer_fromObject(&buffer, value, conn->encodingInfo.encoding)) return -1; status = (*func)(conn->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoConnection_changePassword() // Change the password for the given connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_changePassword(cxoConnection *conn, PyObject *args) { cxoBuffer usernameBuffer, oldPasswordBuffer, newPasswordBuffer; PyObject *oldPasswordObj, *newPasswordObj; int status; // parse the arguments if (cxoConnection_isConnected(conn) < 0) return NULL; if (!PyArg_ParseTuple(args, "OO", &oldPasswordObj, &newPasswordObj)) return NULL; // populate buffers cxoBuffer_init(&usernameBuffer); cxoBuffer_init(&oldPasswordBuffer); cxoBuffer_init(&newPasswordBuffer); if (cxoBuffer_fromObject(&usernameBuffer, conn->username, conn->encodingInfo.encoding) < 0 || cxoBuffer_fromObject(&oldPasswordBuffer, oldPasswordObj, conn->encodingInfo.encoding) < 0 || cxoBuffer_fromObject(&newPasswordBuffer, newPasswordObj, conn->encodingInfo.encoding) < 0) { cxoBuffer_clear(&usernameBuffer); cxoBuffer_clear(&oldPasswordBuffer); cxoBuffer_clear(&newPasswordBuffer); return NULL; } // change the password Py_BEGIN_ALLOW_THREADS status = dpiConn_changePassword(conn->handle, usernameBuffer.ptr, usernameBuffer.size, oldPasswordBuffer.ptr, oldPasswordBuffer.size, newPasswordBuffer.ptr, newPasswordBuffer.size); Py_END_ALLOW_THREADS cxoBuffer_clear(&usernameBuffer); cxoBuffer_clear(&oldPasswordBuffer); cxoBuffer_clear(&newPasswordBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_new() // Create a new connection object and return it. //----------------------------------------------------------------------------- static PyObject *cxoConnection_new(PyTypeObject *type, PyObject *args, PyObject *keywordArgs) { return type->tp_alloc(type, 0); } //----------------------------------------------------------------------------- // cxoConnection_splitComponent() // Split the component out of the source if the split string is found and // return the "before" and "after" parts. //----------------------------------------------------------------------------- static int cxoConnection_splitComponent(PyObject *sourceObj, const char *splitString, const char *methodName, PyObject **beforePartObj, PyObject **afterPartObj) { Py_ssize_t size, pos; PyObject *posObj; posObj = PyObject_CallMethod(sourceObj, methodName, "s", splitString); if (!posObj) return -1; pos = PyLong_AsLong(posObj); Py_DECREF(posObj); if (PyErr_Occurred()) return -1; if (pos < 0) { *beforePartObj = *afterPartObj = NULL; } else { size = PySequence_Size(sourceObj); if (PyErr_Occurred()) return -1; *afterPartObj = PySequence_GetSlice(sourceObj, pos + 1, size); if (!*afterPartObj) return -1; *beforePartObj = PySequence_GetSlice(sourceObj, 0, pos); if (!*beforePartObj) { Py_DECREF(*afterPartObj); *afterPartObj = NULL; return -1; } } return 0; } //----------------------------------------------------------------------------- // cxoConnection_init() // Initialize the connection members. //----------------------------------------------------------------------------- static int cxoConnection_init(cxoConnection *conn, PyObject *args, PyObject *keywordArgs) { PyObject *usernameObj, *passwordObj, *dsnObj, *cclassObj, *editionObj; int status, temp, invokeSessionCallback, threaded, events; PyObject *shardingKeyObj, *superShardingKeyObj, *tempObj; PyObject *beforePartObj, *afterPartObj; dpiCommonCreateParams dpiCommonParams; dpiConnCreateParams dpiCreateParams; unsigned long long externalHandle; PyObject *tagObj, *contextObj; unsigned int stmtCacheSize; cxoConnectionParams params; PyObject *newPasswordObj; cxoSessionPool *pool; // define keyword arguments static char *keywordList[] = { "user", "password", "dsn", "mode", "handle", "pool", "threaded", "events", "cclass", "purity", "newpassword", "encoding", "nencoding", "edition", "appcontext", "tag", "matchanytag", "shardingkey", "supershardingkey", "stmtcachesize", NULL }; // parse arguments pool = NULL; tagObj = Py_None; threaded = 0; externalHandle = 0; newPasswordObj = usernameObj = NULL; passwordObj = dsnObj = cclassObj = editionObj = NULL; contextObj = shardingKeyObj = superShardingKeyObj = NULL; stmtCacheSize = DPI_DEFAULT_STMT_CACHE_SIZE; if (cxoUtils_initializeDPI(NULL) < 0) return -1; if (dpiContext_initCommonCreateParams(cxoDpiContext, &dpiCommonParams) < 0) return cxoError_raiseAndReturnInt(); if (dpiContext_initConnCreateParams(cxoDpiContext, &dpiCreateParams) < 0) return cxoError_raiseAndReturnInt(); if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OOOiKO!ppOiOssOOOpOOI", keywordList, &usernameObj, &passwordObj, &dsnObj, &dpiCreateParams.authMode, &externalHandle, &cxoPyTypeSessionPool, &pool, &threaded, &events, &cclassObj, &dpiCreateParams.purity, &newPasswordObj, &dpiCommonParams.encoding, &dpiCommonParams.nencoding, &editionObj, &contextObj, &tagObj, &dpiCreateParams.matchAnyTag, &shardingKeyObj, &superShardingKeyObj, &stmtCacheSize)) return -1; dpiCreateParams.externalHandle = (void*) externalHandle; if (threaded) dpiCommonParams.createMode |= DPI_MODE_CREATE_THREADED; if (events) dpiCommonParams.createMode |= DPI_MODE_CREATE_EVENTS; // keep a copy of the user name and connect string (DSN) Py_XINCREF(usernameObj); conn->username = usernameObj; Py_XINCREF(dsnObj); conn->dsn = dsnObj; Py_XINCREF(passwordObj); // perform some parsing, if no password and DSN are provided but the user // name is provided if (conn->username && !passwordObj && !dsnObj) { if (cxoConnection_splitComponent(conn->username, "/", "find", &beforePartObj, &afterPartObj) < 0) return -1; if (beforePartObj) { Py_DECREF(conn->username); conn->username = beforePartObj; passwordObj = afterPartObj; if (cxoConnection_splitComponent(passwordObj, "@", "rfind", &beforePartObj, &afterPartObj) < 0) return -1; if (beforePartObj) { Py_DECREF(passwordObj); passwordObj = beforePartObj; conn->dsn = afterPartObj; } } } // setup parameters cxoConnectionParams_initialize(¶ms); if (pool) { dpiCreateParams.pool = pool->handle; params.encoding = pool->encodingInfo.encoding; params.nencoding = pool->encodingInfo.nencoding; } else { params.encoding = cxoUtils_getAdjustedEncoding(dpiCommonParams.encoding); params.nencoding = cxoUtils_getAdjustedEncoding(dpiCommonParams.nencoding); } if (cxoConnectionParams_processContext(¶ms, contextObj) < 0) return cxoConnectionParams_finalize(¶ms); if (cxoConnectionParams_processShardingKey(¶ms, shardingKeyObj, 0) < 0) return cxoConnectionParams_finalize(¶ms); if (cxoConnectionParams_processShardingKey(¶ms, superShardingKeyObj, 1) < 0) return cxoConnectionParams_finalize(¶ms); if (cxoBuffer_fromObject(¶ms.userNameBuffer, conn->username, params.encoding) < 0 || cxoBuffer_fromObject(¶ms.passwordBuffer, passwordObj, params.encoding) < 0 || cxoBuffer_fromObject(¶ms.dsnBuffer, conn->dsn, params.encoding) < 0 || cxoBuffer_fromObject(¶ms.connectionClassBuffer, cclassObj, params.encoding) < 0 || cxoBuffer_fromObject(¶ms.newPasswordBuffer, newPasswordObj, params.encoding) < 0 || cxoBuffer_fromObject(¶ms.editionBuffer, editionObj, params.encoding) < 0 || cxoBuffer_fromObject(¶ms.tagBuffer, tagObj, params.encoding) < 0) { Py_XDECREF(passwordObj); return cxoConnectionParams_finalize(¶ms); } Py_XDECREF(passwordObj); if (params.userNameBuffer.size == 0 && params.passwordBuffer.size == 0) dpiCreateParams.externalAuth = 1; dpiCreateParams.connectionClass = params.connectionClassBuffer.ptr; dpiCreateParams.connectionClassLength = params.connectionClassBuffer.size; dpiCreateParams.newPassword = params.newPasswordBuffer.ptr; dpiCreateParams.newPasswordLength = params.newPasswordBuffer.size; dpiCommonParams.edition = params.editionBuffer.ptr; dpiCommonParams.editionLength = params.editionBuffer.size; dpiCommonParams.stmtCacheSize = stmtCacheSize; dpiCreateParams.tag = params.tagBuffer.ptr; dpiCreateParams.tagLength = params.tagBuffer.size; dpiCreateParams.appContext = params.appContext; dpiCreateParams.numAppContext = params.numAppContext; dpiCreateParams.shardingKeyColumns = params.shardingKeyColumns; dpiCreateParams.numShardingKeyColumns = params.numShardingKeyColumns; dpiCreateParams.superShardingKeyColumns = params.superShardingKeyColumns; dpiCreateParams.numSuperShardingKeyColumns = params.numSuperShardingKeyColumns; if (pool && !pool->homogeneous && pool->username && conn->username) { temp = PyObject_RichCompareBool(conn->username, pool->username, Py_EQ); if (temp < 0) return cxoConnectionParams_finalize(¶ms); if (temp) params.userNameBuffer.size = 0; } // create connection Py_BEGIN_ALLOW_THREADS status = dpiConn_create(cxoDpiContext, params.userNameBuffer.ptr, params.userNameBuffer.size, params.passwordBuffer.ptr, params.passwordBuffer.size, params.dsnBuffer.ptr, params.dsnBuffer.size, &dpiCommonParams, &dpiCreateParams, &conn->handle); Py_END_ALLOW_THREADS if (status < 0) { cxoConnectionParams_finalize(¶ms); return cxoError_raiseAndReturnInt(); } // determine if session callback should be invoked; this takes place if // the connection is newly created by the pool or if the requested tag // does not match the actual tag invokeSessionCallback = 0; if (dpiCreateParams.outNewSession || dpiCreateParams.outTagLength != params.tagBuffer.size || (dpiCreateParams.outTagLength > 0 && strncmp(dpiCreateParams.outTag, params.tagBuffer.ptr, dpiCreateParams.outTagLength) != 0)) invokeSessionCallback = 1; cxoConnectionParams_finalize(¶ms); // determine encodings to use if (pool) conn->encodingInfo = pool->encodingInfo; else { if (dpiConn_getEncodingInfo(conn->handle, &conn->encodingInfo) < 0) return cxoError_raiseAndReturnInt(); conn->encodingInfo.encoding = cxoUtils_getAdjustedEncoding(conn->encodingInfo.encoding); conn->encodingInfo.nencoding = cxoUtils_getAdjustedEncoding(conn->encodingInfo.nencoding); } // set tag property if (dpiCreateParams.outTagLength > 0) { conn->tag = PyUnicode_Decode(dpiCreateParams.outTag, dpiCreateParams.outTagLength, conn->encodingInfo.encoding, NULL); if (!conn->tag) return -1; } // invoke the session callback if applicable if (invokeSessionCallback && pool && pool->sessionCallback && PyCallable_Check(pool->sessionCallback)) { tempObj = PyObject_CallFunctionObjArgs(pool->sessionCallback, (PyObject*) conn, tagObj, NULL); if (!tempObj) return -1; Py_DECREF(tempObj); } return 0; } //----------------------------------------------------------------------------- // cxoConnection_free() // Deallocate the connection, disconnecting from the database if necessary. //----------------------------------------------------------------------------- static void cxoConnection_free(cxoConnection *conn) { if (conn->handle) { Py_BEGIN_ALLOW_THREADS dpiConn_release(conn->handle); Py_END_ALLOW_THREADS conn->handle = NULL; } Py_CLEAR(conn->sessionPool); Py_CLEAR(conn->username); Py_CLEAR(conn->dsn); Py_CLEAR(conn->version); Py_CLEAR(conn->inputTypeHandler); Py_CLEAR(conn->outputTypeHandler); Py_CLEAR(conn->tag); Py_TYPE(conn)->tp_free((PyObject*) conn); } //----------------------------------------------------------------------------- // cxoConnection_repr() // Return a string representation of the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_repr(cxoConnection *connection) { PyObject *module, *name, *result; if (cxoUtils_getModuleAndName(Py_TYPE(connection), &module, &name) < 0) return NULL; if (connection->username && connection->username != Py_None && connection->dsn && connection->dsn != Py_None) { result = cxoUtils_formatString("<%s.%s to %s@%s>", PyTuple_Pack(4, module, name, connection->username, connection->dsn)); } else if (connection->username && connection->username != Py_None) { result = cxoUtils_formatString("<%s.%s to user %s@local>", PyTuple_Pack(3, module, name, connection->username)); } else { result = cxoUtils_formatString("<%s.%s to externally identified user>", PyTuple_Pack(2, module, name)); } Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // cxoConnection_getStmtCacheSize() // Return the Oracle statement cache size. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getStmtCacheSize(cxoConnection* conn, void* arg) { uint32_t cacheSize; if (cxoConnection_isConnected(conn) < 0) return NULL; if (dpiConn_getStmtCacheSize(conn->handle, &cacheSize) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(cacheSize); } //----------------------------------------------------------------------------- // cxoConnection_setStmtCacheSize() // Set the Oracle statement cache size. //----------------------------------------------------------------------------- static int cxoConnection_setStmtCacheSize(cxoConnection* conn, PyObject *value, void* arg) { uint32_t cacheSize; if (cxoConnection_isConnected(conn) < 0) return -1; if (!PyLong_Check(value)) { PyErr_SetString(PyExc_TypeError, "value must be an integer"); return -1; } cacheSize = (uint32_t) PyLong_AsLong(value); if (dpiConn_setStmtCacheSize(conn->handle, cacheSize) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoConnection_getCallTimeout() // Return the call timeout (in milliseconds) for round-trips performed with // this connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getCallTimeout(cxoConnection* conn, void* arg) { uint32_t callTimeout; if (cxoConnection_isConnected(conn) < 0) return NULL; if (dpiConn_getCallTimeout(conn->handle, &callTimeout) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(callTimeout); } //----------------------------------------------------------------------------- // cxoConnection_setCallTimeout() // Set the call timeout (in milliseconds) for round-trips performed with this // connection. //----------------------------------------------------------------------------- static int cxoConnection_setCallTimeout(cxoConnection* conn, PyObject *value, void* arg) { uint32_t callTimeout; if (cxoConnection_isConnected(conn) < 0) return -1; callTimeout = (uint32_t) PyLong_AsLong(value); if (PyErr_Occurred()) return -1; if (dpiConn_setCallTimeout(conn->handle, callTimeout) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoConnection_getType() // Return a type object given its name. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getType(cxoConnection *conn, PyObject *nameObj) { if (cxoConnection_isConnected(conn) < 0) return NULL; return (PyObject*) cxoObjectType_newByName(conn, nameObj); } //----------------------------------------------------------------------------- // cxoConnection_createLob() // Create a new temporary LOB and return it. //----------------------------------------------------------------------------- static PyObject *cxoConnection_createLob(cxoConnection *conn, PyObject *lobType) { cxoDbType *dbType; dpiLob *handle; PyObject *lob; // verify connection is open if (cxoConnection_isConnected(conn) < 0) return NULL; // verify the LOB type if (lobType != (PyObject*) cxoDbTypeClob && lobType != (PyObject*) cxoDbTypeBlob && lobType != (PyObject*) cxoDbTypeNclob) { PyErr_SetString(PyExc_TypeError, "parameter should be one of cx_Oracle.DB_TYPE_CLOB, " "cx_Oracle.DB_TYPE_BLOB or cx_Oracle.DB_TYPE_NCLOB"); return NULL; } // create a temporary LOB dbType = (cxoDbType*) lobType; if (dpiConn_newTempLob(conn->handle, dbType->num, &handle) < 0) return cxoError_raiseAndReturnNull(); lob = cxoLob_new(conn, dbType, handle); if (!lob) dpiLob_release(handle); return lob; } //----------------------------------------------------------------------------- // cxoConnection_getVersion() // Retrieve the version of the database and return it. Note that this // function also places the result in the associated dictionary so it is only // calculated once. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getVersion(cxoConnection *conn, void *unused) { dpiVersionInfo versionInfo; char buffer[25]; int status, len; if (cxoConnection_isConnected(conn) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiConn_getServerVersion(conn->handle, NULL, NULL, &versionInfo); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); len = snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.%d", versionInfo.versionNum, versionInfo.releaseNum, versionInfo.updateNum, versionInfo.portReleaseNum, versionInfo.portUpdateNum); return PyUnicode_DecodeASCII(buffer, len, NULL); } //----------------------------------------------------------------------------- // cxoConnection_getEncoding() // Return the encoding associated with the environment of the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getEncoding(cxoConnection *conn, void *unused) { return PyUnicode_DecodeASCII(conn->encodingInfo.encoding, strlen(conn->encodingInfo.encoding), NULL); } //----------------------------------------------------------------------------- // cxoConnection_getLTXID() // Return the logical transaction id used with Transaction Guard. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getLTXID(cxoConnection *conn, void *unused) { uint32_t ltxidLength; const char *ltxid; if (cxoConnection_isConnected(conn) < 0) return NULL; if (dpiConn_getLTXID(conn->handle, <xid, <xidLength) < 0) return cxoError_raiseAndReturnNull(); return PyBytes_FromStringAndSize(ltxid, ltxidLength); } //----------------------------------------------------------------------------- // cxoConnection_getHandle() // Return the OCI handle used by the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getHandle(cxoConnection *conn, void *unused) { void *handle; if (cxoConnection_isConnected(conn) < 0) return NULL; if (dpiConn_getHandle(conn->handle, &handle) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromUnsignedLongLong((unsigned long long) handle); } //----------------------------------------------------------------------------- // cxoConnection_getNationalEncoding() // Return the national encoding associated with the environment of the // connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getNationalEncoding(cxoConnection *conn, void *unused) { return PyUnicode_DecodeASCII(conn->encodingInfo.nencoding, strlen(conn->encodingInfo.nencoding), NULL); } //----------------------------------------------------------------------------- // cxoConnection_getMaxBytesPerCharacter() // Return the maximum number of bytes per character. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getMaxBytesPerCharacter(cxoConnection *conn, void *unused) { return PyLong_FromLong(conn->encodingInfo.maxBytesPerCharacter); } //----------------------------------------------------------------------------- // cxoConnection_close() // Close the connection, disconnecting from the database. //----------------------------------------------------------------------------- static PyObject *cxoConnection_close(cxoConnection *conn, PyObject *args) { cxoBuffer tagBuffer; uint32_t mode; int status; if (cxoConnection_isConnected(conn) < 0) return NULL; if (cxoBuffer_fromObject(&tagBuffer, conn->tag, conn->encodingInfo.encoding) < 0) return NULL; mode = DPI_MODE_CONN_CLOSE_DEFAULT; if (conn->tag && conn->tag != Py_None) mode |= DPI_MODE_CONN_CLOSE_RETAG; Py_BEGIN_ALLOW_THREADS status = dpiConn_close(conn->handle, mode, (char*) tagBuffer.ptr, tagBuffer.size); if (status == DPI_SUCCESS) dpiConn_release(conn->handle); Py_END_ALLOW_THREADS cxoBuffer_clear(&tagBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); conn->handle = NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_commit() // Commit the transaction on the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_commit(cxoConnection *conn, PyObject *args) { int status; if (cxoConnection_isConnected(conn) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiConn_commit(conn->handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_begin() // Begin a new transaction on the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_begin(cxoConnection *conn, PyObject *args) { Py_ssize_t transactionIdLength, branchIdLength; const char *transactionId, *branchId; int formatId, status; // parse the arguments formatId = -1; transactionId = branchId = NULL; transactionIdLength = branchIdLength = 0; if (!PyArg_ParseTuple(args, "|is#s#", &formatId, &transactionId, &transactionIdLength, &branchId, &branchIdLength)) return NULL; // make sure we are actually connected if (cxoConnection_isConnected(conn) < 0) return NULL; // begin the distributed transaction Py_BEGIN_ALLOW_THREADS status = dpiConn_beginDistribTrans(conn->handle, formatId, transactionId, transactionIdLength, branchId, branchIdLength); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_prepare() // Commit the transaction on the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_prepare(cxoConnection *conn, PyObject *args) { int status, commitNeeded; // make sure we are actually connected if (cxoConnection_isConnected(conn) < 0) return NULL; // perform the prepare Py_BEGIN_ALLOW_THREADS status = dpiConn_prepareDistribTrans(conn->handle, &commitNeeded); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); // return whether a commit is needed in order to allow for avoiding the // call to commit() which will fail with ORA-24756 (transaction does not // exist) return PyBool_FromLong(commitNeeded); } //----------------------------------------------------------------------------- // cxoConnection_rollback() // Rollback the transaction on the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_rollback(cxoConnection *conn, PyObject *args) { int status; if (cxoConnection_isConnected(conn) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiConn_rollback(conn->handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_newCursor() // Create a new cursor (statement) referencing the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_newCursor(cxoConnection *conn, PyObject *args, PyObject *keywordArgs) { PyObject *createArgs, *result, *arg; Py_ssize_t numArgs = 0, i; if (cxoConnection_isConnected(conn) < 0) return NULL; if (args) numArgs = PyTuple_GET_SIZE(args); createArgs = PyTuple_New(1 + numArgs); if (!createArgs) return NULL; Py_INCREF(conn); PyTuple_SET_ITEM(createArgs, 0, (PyObject*) conn); for (i = 0; i < numArgs; i++) { arg = PyTuple_GET_ITEM(args, i); Py_INCREF(arg); PyTuple_SET_ITEM(createArgs, i + 1, arg); } result = PyObject_Call( (PyObject*) &cxoPyTypeCursor, createArgs, keywordArgs); Py_DECREF(createArgs); return result; } //----------------------------------------------------------------------------- // cxoConnection_cancel() // Cause Oracle to issue an immediate (asynchronous) abort of any currently // executing statement. //----------------------------------------------------------------------------- static PyObject *cxoConnection_cancel(cxoConnection *conn, PyObject *args) { if (cxoConnection_isConnected(conn) < 0) return NULL; if (dpiConn_breakExecution(conn->handle) < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_newEnqueueOptions() // Creates a new enqueue options object and returns it. //----------------------------------------------------------------------------- static PyObject *cxoConnection_newEnqueueOptions(cxoConnection *conn, PyObject *args) { if (cxoConnection_isConnected(conn) < 0) return NULL; return (PyObject*) cxoEnqOptions_new(conn, NULL); } //----------------------------------------------------------------------------- // cxoConnection_newDequeueOptions() // Creates a new dequeue options object and returns it. //----------------------------------------------------------------------------- static PyObject *cxoConnection_newDequeueOptions(cxoConnection *conn, PyObject *args) { if (cxoConnection_isConnected(conn) < 0) return NULL; return (PyObject*) cxoDeqOptions_new(conn, NULL); } //----------------------------------------------------------------------------- // cxoConnection_newMessageProperties() // Creates a new message properties object and returns it. //----------------------------------------------------------------------------- static PyObject *cxoConnection_newMessageProperties(cxoConnection *conn, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "payload", "correlation", "delay", "exceptionq", "expiration", "priority", NULL }; PyObject *payloadObj, *correlationObj, *exceptionQObj; int delay, expiration, priority, status; cxoMsgProps *props; cxoBuffer buffer; // parse arguments expiration = -1; delay = priority = 0; payloadObj = correlationObj = exceptionQObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OOiOii", keywordList, &payloadObj, &correlationObj, &delay, &exceptionQObj, &expiration, &priority)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; // create new message properties object props = cxoMsgProps_new(conn, NULL); if (!props) return NULL; // set payload, if applicable if (payloadObj) { Py_INCREF(payloadObj); props->payload = payloadObj; } // set correlation, if applicable if (correlationObj) { if (cxoBuffer_fromObject(&buffer, correlationObj, props->encoding) < 0) { Py_DECREF(props); return NULL; } status = dpiMsgProps_setCorrelation(props->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(props); return NULL; } } // set delay, if applicable if (delay != 0) { if (dpiMsgProps_setDelay(props->handle, (int32_t) delay) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(props); return NULL; } } // set exception queue, if applicable if (exceptionQObj) { if (cxoBuffer_fromObject(&buffer, exceptionQObj, props->encoding) < 0) { Py_DECREF(props); return NULL; } status = dpiMsgProps_setExceptionQ(props->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(props); return NULL; } } // set expiration, if applicable if (expiration != -1) { if (dpiMsgProps_setExpiration(props->handle, (int32_t) expiration) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(props); return NULL; } } // set priority, if applicable if (priority != 0) { if (dpiMsgProps_setPriority(props->handle, (int32_t) priority) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(props); return NULL; } } return (PyObject*) props; } //----------------------------------------------------------------------------- // cxoConnection_dequeue() // Dequeues a message using Advanced Queuing capabilities. The message ID is // returned if a message is available or None if no message is available. //----------------------------------------------------------------------------- static PyObject *cxoConnection_dequeue(cxoConnection *conn, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "name", "options", "msgproperties", "payload", NULL }; cxoMsgProps *propertiesObj; const char *messageIdValue; cxoDeqOptions *optionsObj; uint32_t messageIdLength; cxoObject *payloadObj; cxoBuffer nameBuffer; PyObject *nameObj; int status; // parse arguments if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList, &nameObj, &cxoPyTypeDeqOptions, &optionsObj, &cxoPyTypeMsgProps, &propertiesObj, &cxoPyTypeObject, &payloadObj)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; if (cxoBuffer_fromObject(&nameBuffer, nameObj, conn->encodingInfo.encoding) < 0) return NULL; // dequeue payload Py_BEGIN_ALLOW_THREADS status = dpiConn_deqObject(conn->handle, nameBuffer.ptr, nameBuffer.size, optionsObj->handle, propertiesObj->handle, payloadObj->handle, &messageIdValue, &messageIdLength); Py_END_ALLOW_THREADS cxoBuffer_clear(&nameBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); // return message id if (!messageIdValue) Py_RETURN_NONE; return PyBytes_FromStringAndSize(messageIdValue, messageIdLength); } //----------------------------------------------------------------------------- // cxoConnection_enqueue() // Enqueues a message using Advanced Queuing capabilities. The message ID is // returned. //----------------------------------------------------------------------------- static PyObject *cxoConnection_enqueue(cxoConnection *conn, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "name", "options", "msgproperties", "payload", NULL }; cxoMsgProps *propertiesObj; const char *messageIdValue; cxoEnqOptions *optionsObj; uint32_t messageIdLength; cxoObject *payloadObj; cxoBuffer nameBuffer; PyObject *nameObj; int status; // parse arguments if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList, &nameObj, &cxoPyTypeEnqOptions, &optionsObj, &cxoPyTypeMsgProps, &propertiesObj, &cxoPyTypeObject, &payloadObj)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; if (cxoBuffer_fromObject(&nameBuffer, nameObj, conn->encodingInfo.encoding) < 0) return NULL; // enqueue payload Py_BEGIN_ALLOW_THREADS status = dpiConn_enqObject(conn->handle, nameBuffer.ptr, nameBuffer.size, optionsObj->handle, propertiesObj->handle, payloadObj->handle, &messageIdValue, &messageIdLength); Py_END_ALLOW_THREADS cxoBuffer_clear(&nameBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); // return message id return PyBytes_FromStringAndSize(messageIdValue, messageIdLength); } //----------------------------------------------------------------------------- // cxoConnection_queue() // Creates a new queue associated with the connection and returns it to the // caller. //----------------------------------------------------------------------------- static PyObject *cxoConnection_queue(cxoConnection *conn, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "name", "payload_type", "payloadType", NULL }; cxoObjectType *typeObj, *deprecatedTypeObj; cxoBuffer nameBuffer; PyObject *nameObj; dpiQueue *handle; cxoQueue *queue; int status; // parse arguments typeObj = deprecatedTypeObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O!O!", keywordList, &nameObj, &cxoPyTypeObjectType, &typeObj, &cxoPyTypeObjectType, &deprecatedTypeObj)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; if (deprecatedTypeObj) { if (typeObj) { cxoError_raiseFromString(cxoProgrammingErrorException, "payload_type and payloadType cannot both be specified"); return NULL; } typeObj = deprecatedTypeObj; } if (cxoBuffer_fromObject(&nameBuffer, nameObj, conn->encodingInfo.encoding) < 0) return NULL; // create queue status = dpiConn_newQueue(conn->handle, nameBuffer.ptr, nameBuffer.size, (typeObj) ? typeObj->handle : NULL, &handle); cxoBuffer_clear(&nameBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); queue = cxoQueue_new(conn, handle); if (!queue) return NULL; Py_INCREF(nameObj); queue->name = nameObj; Py_XINCREF(typeObj); queue->payloadType = typeObj; return (PyObject*) queue; } //----------------------------------------------------------------------------- // cxoConnection_contextManagerEnter() // Called when the connection is used as a context manager and simply returns // itconn as a convenience to the caller. //----------------------------------------------------------------------------- static PyObject *cxoConnection_contextManagerEnter(cxoConnection *conn, PyObject* args) { if (cxoConnection_isConnected(conn) < 0) return NULL; Py_INCREF(conn); return (PyObject*) conn; } //----------------------------------------------------------------------------- // cxoConnection_contextManagerExit() // Called when the connection is used as a context manager and if any // exception a rollback takes place; otherwise, a commit takes place. //----------------------------------------------------------------------------- static PyObject *cxoConnection_contextManagerExit(cxoConnection *conn, PyObject* args) { PyObject *excType, *excValue, *excTraceback, *result; if (!PyArg_ParseTuple(args, "OOO", &excType, &excValue, &excTraceback)) return NULL; result = cxoConnection_close(conn, NULL); if (!result) return NULL; Py_DECREF(result); Py_INCREF(Py_False); return Py_False; } //----------------------------------------------------------------------------- // cxoConnection_ping() // Makes a round trip call to the server to confirm that the connection and // server are active. //----------------------------------------------------------------------------- static PyObject *cxoConnection_ping(cxoConnection *conn, PyObject* args) { int status; if (cxoConnection_isConnected(conn) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiConn_ping(conn->handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_shutdown() // Shuts down the database. Note that this must be done in two phases except // in the situation where the instance is aborted. //----------------------------------------------------------------------------- static PyObject *cxoConnection_shutdown(cxoConnection *conn, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "mode", NULL }; dpiShutdownMode mode; // parse arguments mode = DPI_MODE_SHUTDOWN_DEFAULT; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList, &mode)) return NULL; // make sure we are actually connected if (cxoConnection_isConnected(conn) < 0) return NULL; // perform the work if (dpiConn_shutdownDatabase(conn->handle, mode) < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_startup() // Starts up the database, equivalent to "startup nomount" in SQL*Plus. //----------------------------------------------------------------------------- static PyObject *cxoConnection_startup(cxoConnection *conn, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "force", "restrict", "pfile", NULL }; int temp, force, restrictStartup; cxoBuffer pfileBuffer; dpiStartupMode mode; PyObject *pfileObj; // parse arguments pfileObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|ppO", keywordList, &force, &restrictStartup, &pfileObj)) return NULL; // set the flags to use during startup mode = DPI_MODE_STARTUP_DEFAULT; if (force) mode |= DPI_MODE_STARTUP_FORCE; if (restrictStartup) mode |= DPI_MODE_STARTUP_RESTRICT; // check the pfile parameter if (cxoBuffer_fromObject(&pfileBuffer, pfileObj, conn->encodingInfo.encoding) < 0) return NULL; // make sure we are actually connected if (cxoConnection_isConnected(conn) < 0) { cxoBuffer_clear(&pfileBuffer); return NULL; } // perform the work temp = dpiConn_startupDatabaseWithPfile(conn->handle, pfileBuffer.ptr, pfileBuffer.size, mode); cxoBuffer_clear(&pfileBuffer); if (temp < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_subscribe() // Create a subscription to events that take place in the database. //----------------------------------------------------------------------------- static PyObject *cxoConnection_subscribe(cxoConnection *conn, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "namespace", "protocol", "callback", "timeout", "operations", "port", "qos", "ip_address", "grouping_class", "grouping_value", "grouping_type", "name", "client_initiated", "ipAddress", "groupingClass", "groupingValue", "groupingType", "clientInitiated", NULL }; PyObject *callback, *ipAddress, *ipAddressDeprecated, *name; uint8_t groupingClassDeprecated, groupingTypeDeprecated; cxoBuffer ipAddressBuffer, nameBuffer; uint32_t groupingValueDeprecated; int clientInitiatedDeprecated; dpiSubscrCreateParams params; cxoSubscr *subscr; // get default values for subscription parameters if (dpiContext_initSubscrCreateParams(cxoDpiContext, ¶ms) < 0) return cxoError_raiseAndReturnNull(); // validate parameters groupingValueDeprecated = 0; clientInitiatedDeprecated = 0; groupingClassDeprecated = groupingTypeDeprecated = 0; callback = name = ipAddress = ipAddressDeprecated = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|IIOIIIIObIbOpObIbp", keywordList, ¶ms.subscrNamespace, ¶ms.protocol, &callback, ¶ms.timeout, ¶ms.operations, ¶ms.portNumber, ¶ms.qos, &ipAddress, ¶ms.groupingClass, ¶ms.groupingValue, ¶ms.groupingType, &name, ¶ms.clientInitiated, &ipAddressDeprecated, &groupingClassDeprecated, &groupingValueDeprecated, &groupingTypeDeprecated, &clientInitiatedDeprecated)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; // check duplicate parameters to ensure that both are not specified if (ipAddressDeprecated) { if (ipAddress) { cxoError_raiseFromString(cxoProgrammingErrorException, "ip_address and ipAddress cannot both be specified"); return NULL; } ipAddress = ipAddressDeprecated; } if (groupingClassDeprecated != 0) { if (params.groupingClass != 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "grouping_class and groupingClass cannot both be " "specified"); return NULL; } params.groupingClass = groupingClassDeprecated; } if (groupingValueDeprecated != 0) { if (params.groupingValue != 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "grouping_value and groupingValue cannot both be " "specified"); return NULL; } params.groupingValue = groupingValueDeprecated; } if (groupingTypeDeprecated != 0) { if (params.groupingType != 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "grouping_type and groupingType cannot both be " "specified"); return NULL; } params.groupingType = groupingTypeDeprecated; } if (clientInitiatedDeprecated != 0) { if (params.clientInitiated != 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "client_initiated and clientInitiated cannot both be " "specified"); return NULL; } params.clientInitiated = clientInitiatedDeprecated; } // populate IP address in parameters, if applicable cxoBuffer_init(&ipAddressBuffer); if (ipAddress) { if (cxoBuffer_fromObject(&ipAddressBuffer, ipAddress, conn->encodingInfo.encoding) < 0) return NULL; params.ipAddress = ipAddressBuffer.ptr; params.ipAddressLength = ipAddressBuffer.size; } // populate name in parameters, if applicable cxoBuffer_init(&nameBuffer); if (name) { if (cxoBuffer_fromObject(&nameBuffer, name, conn->encodingInfo.encoding) < 0) { cxoBuffer_clear(&ipAddressBuffer); return NULL; } params.name = nameBuffer.ptr; params.nameLength = nameBuffer.size; } // create Python subscription object subscr = (cxoSubscr*) cxoPyTypeSubscr.tp_alloc(&cxoPyTypeSubscr, 0); if (!subscr) { cxoBuffer_clear(&ipAddressBuffer); cxoBuffer_clear(&nameBuffer); return NULL; } Py_INCREF(conn); subscr->connection = conn; Py_XINCREF(callback); subscr->callback = callback; subscr->namespace = params.subscrNamespace; subscr->protocol = params.protocol; Py_XINCREF(ipAddress); subscr->ipAddress = ipAddress; Py_XINCREF(name); subscr->name = name; subscr->port = params.portNumber; subscr->timeout = params.timeout; subscr->operations = params.operations; subscr->qos = params.qos; subscr->groupingClass = params.groupingClass; subscr->groupingValue = params.groupingValue; subscr->groupingType = params.groupingType; // populate callback in parameters, if applicable if (callback) { params.callback = (dpiSubscrCallback) cxoSubscr_callback; params.callbackContext = subscr; } // create ODPI-C subscription if (dpiConn_subscribe(conn->handle, ¶ms, &subscr->handle) < 0) { cxoError_raiseAndReturnNull(); cxoBuffer_clear(&ipAddressBuffer); cxoBuffer_clear(&nameBuffer); Py_DECREF(subscr); return NULL; } subscr->id = params.outRegId; cxoBuffer_clear(&ipAddressBuffer); cxoBuffer_clear(&nameBuffer); return (PyObject*) subscr; } //----------------------------------------------------------------------------- // cxoConnection_unsubscribe() // Destroy a subscription to events that take place in the database. //----------------------------------------------------------------------------- static PyObject *cxoConnection_unsubscribe(cxoConnection *conn, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "subscription", NULL }; PyObject *subscrObj; cxoSubscr *subscr; int status; // validate parameters if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!", keywordList, &cxoPyTypeSubscr, &subscrObj)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; // destroy ODPI-C subscription subscr = (cxoSubscr*) subscrObj; Py_BEGIN_ALLOW_THREADS status = dpiConn_unsubscribe(conn->handle, subscr->handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); subscr->handle = NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoConnection_getSodaDatabase() // Create and return a new SODA database object associated with the // connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getSodaDatabase(cxoConnection *conn, PyObject *args) { if (cxoConnection_isConnected(conn) < 0) return NULL; return (PyObject*) cxoSodaDatabase_new(conn); } //----------------------------------------------------------------------------- // cxoConnection_getOciAttr() // Return the value of the OCI attribute. This is intended to be used for // testing attributes which are not currently exposed directly and should only // be used for that purpose. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getOciAttr(cxoConnection *conn, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "handle_type", "attr_num", "attr_type", NULL }; unsigned handleType, attrNum, attrType; uint32_t valueLength; dpiDataBuffer value; // validate parameters if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "III", keywordList, &handleType, &attrNum, &attrType)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; // get value and convert it to the appropriate Python value if (dpiConn_getOciAttr(conn->handle, handleType, attrNum, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); return cxoUtils_convertOciAttrToPythonValue(attrType, &value, valueLength, conn->encodingInfo.encoding); } //----------------------------------------------------------------------------- // cxoConnection_getCurrentSchema() // Return the current schema associated with the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getCurrentSchema(cxoConnection* conn, void* unused) { return cxoConnection_getAttrText(conn, dpiConn_getCurrentSchema); } //----------------------------------------------------------------------------- // cxoConnection_getEdition() // Return the edition associated with the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getEdition(cxoConnection* conn, void* unused) { return cxoConnection_getAttrText(conn, dpiConn_getEdition); } //----------------------------------------------------------------------------- // cxoConnection_getExternalName() // Return the external name associated with the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getExternalName(cxoConnection* conn, void* unused) { return cxoConnection_getAttrText(conn, dpiConn_getExternalName); } //----------------------------------------------------------------------------- // cxoConnection_getInternalName() // Return the internal name associated with the connection. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getInternalName(cxoConnection* conn, void* unused) { return cxoConnection_getAttrText(conn, dpiConn_getInternalName); } //----------------------------------------------------------------------------- // cxoConnection_getException() // Return the requested exception. //----------------------------------------------------------------------------- static PyObject *cxoConnection_getException(cxoConnection *conn, void *arg) { PyObject *exc = * (PyObject**) arg; Py_INCREF(exc); return exc; } //----------------------------------------------------------------------------- // cxoConnection_setAction() // Set the action associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setAction(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setAction); } //----------------------------------------------------------------------------- // cxoConnection_setClientIdentifier() // Set the client identifier associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setClientIdentifier(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setClientIdentifier); } //----------------------------------------------------------------------------- // cxoConnection_setClientInfo() // Set the client info associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setClientInfo(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setClientInfo); } //----------------------------------------------------------------------------- // cxoConnection_setCurrentSchema() // Set the current schema associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setCurrentSchema(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setCurrentSchema); } //----------------------------------------------------------------------------- // cxoConnection_setDbOp() // Set the database operation associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setDbOp(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setDbOp); } //----------------------------------------------------------------------------- // cxoConnection_setExternalName() // Set the external name associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setExternalName(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setExternalName); } //----------------------------------------------------------------------------- // cxoConnection_setInternalName() // Set the internal name associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setInternalName(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setInternalName); } //----------------------------------------------------------------------------- // cxoConnection_setModule() // Set the module associated with the connection. //----------------------------------------------------------------------------- static int cxoConnection_setModule(cxoConnection* conn, PyObject *value, void* unused) { return cxoConnection_setAttrText(conn, value, dpiConn_setModule); } //----------------------------------------------------------------------------- // cxoConnection_setOciAttr() // Set the value of the OCI attribute to the specified value. This is // intended to be used for testing attributes which are not currently exposed // directly and should only be used for that purpose. //----------------------------------------------------------------------------- static PyObject *cxoConnection_setOciAttr(cxoConnection *conn, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "handle_type", "attr_num", "attr_type", "value", NULL }; unsigned handleType, attrNum, attrType; uint32_t ociValueLength; dpiDataBuffer ociBuffer; cxoBuffer buffer; PyObject *value; void *ociValue; // validate parameters if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "IIIO", keywordList, &handleType, &attrNum, &attrType, &value)) return NULL; if (cxoConnection_isConnected(conn) < 0) return NULL; // convert value to OCI requirement and then set it cxoBuffer_init(&buffer); if (cxoUtils_convertPythonValueToOciAttr(value, attrType, &buffer, &ociBuffer, &ociValue, &ociValueLength, conn->encodingInfo.encoding) < 0) return NULL; if (dpiConn_setOciAttr(conn->handle, handleType, attrNum, ociValue, ociValueLength) < 0) return cxoError_raiseAndReturnNull(); cxoBuffer_clear(&buffer); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // declaration of methods for the Python type //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "cursor", (PyCFunction) cxoConnection_newCursor, METH_VARARGS | METH_KEYWORDS }, { "commit", (PyCFunction) cxoConnection_commit, METH_NOARGS }, { "rollback", (PyCFunction) cxoConnection_rollback, METH_NOARGS }, { "begin", (PyCFunction) cxoConnection_begin, METH_VARARGS }, { "prepare", (PyCFunction) cxoConnection_prepare, METH_NOARGS }, { "close", (PyCFunction) cxoConnection_close, METH_NOARGS }, { "cancel", (PyCFunction) cxoConnection_cancel, METH_NOARGS }, { "__enter__", (PyCFunction) cxoConnection_contextManagerEnter, METH_NOARGS }, { "__exit__", (PyCFunction) cxoConnection_contextManagerExit, METH_VARARGS }, { "ping", (PyCFunction) cxoConnection_ping, METH_NOARGS }, { "shutdown", (PyCFunction) cxoConnection_shutdown, METH_VARARGS | METH_KEYWORDS}, { "startup", (PyCFunction) cxoConnection_startup, METH_VARARGS | METH_KEYWORDS}, { "subscribe", (PyCFunction) cxoConnection_subscribe, METH_VARARGS | METH_KEYWORDS}, { "unsubscribe", (PyCFunction) cxoConnection_unsubscribe, METH_VARARGS | METH_KEYWORDS}, { "changepassword", (PyCFunction) cxoConnection_changePassword, METH_VARARGS }, { "gettype", (PyCFunction) cxoConnection_getType, METH_O }, { "deqoptions", (PyCFunction) cxoConnection_newDequeueOptions, METH_NOARGS }, { "enqoptions", (PyCFunction) cxoConnection_newEnqueueOptions, METH_NOARGS }, { "msgproperties", (PyCFunction) cxoConnection_newMessageProperties, METH_VARARGS | METH_KEYWORDS }, { "deq", (PyCFunction) cxoConnection_dequeue, METH_VARARGS | METH_KEYWORDS }, { "enq", (PyCFunction) cxoConnection_enqueue, METH_VARARGS | METH_KEYWORDS }, { "queue", (PyCFunction) cxoConnection_queue, METH_VARARGS | METH_KEYWORDS }, { "createlob", (PyCFunction) cxoConnection_createLob, METH_O }, { "getSodaDatabase", (PyCFunction) cxoConnection_getSodaDatabase, METH_NOARGS }, { "_get_oci_attr", (PyCFunction) cxoConnection_getOciAttr, METH_VARARGS | METH_KEYWORDS }, { "_set_oci_attr", (PyCFunction) cxoConnection_setOciAttr, METH_VARARGS | METH_KEYWORDS }, { NULL } }; //----------------------------------------------------------------------------- // declaration of members for the Python type //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "username", T_OBJECT, offsetof(cxoConnection, username), READONLY }, { "dsn", T_OBJECT, offsetof(cxoConnection, dsn), READONLY }, { "tnsentry", T_OBJECT, offsetof(cxoConnection, dsn), READONLY }, { "tag", T_OBJECT, offsetof(cxoConnection, tag), 0 }, { "autocommit", T_INT, offsetof(cxoConnection, autocommit), 0 }, { "inputtypehandler", T_OBJECT, offsetof(cxoConnection, inputTypeHandler), 0 }, { "outputtypehandler", T_OBJECT, offsetof(cxoConnection, outputTypeHandler), 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members for the Python type //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "version", (getter) cxoConnection_getVersion, 0, 0, 0 }, { "encoding", (getter) cxoConnection_getEncoding, 0, 0, 0 }, { "nencoding", (getter) cxoConnection_getNationalEncoding, 0, 0, 0 }, { "call_timeout", (getter) cxoConnection_getCallTimeout, (setter) cxoConnection_setCallTimeout, 0, 0 }, { "maxBytesPerCharacter", (getter) cxoConnection_getMaxBytesPerCharacter, 0, 0, 0 }, { "stmtcachesize", (getter) cxoConnection_getStmtCacheSize, (setter) cxoConnection_setStmtCacheSize, 0, 0 }, { "module", 0, (setter) cxoConnection_setModule, 0, 0 }, { "action", 0, (setter) cxoConnection_setAction, 0, 0 }, { "clientinfo", 0, (setter) cxoConnection_setClientInfo, 0, 0 }, { "client_identifier", 0, (setter) cxoConnection_setClientIdentifier, 0, 0 }, { "current_schema", (getter) cxoConnection_getCurrentSchema, (setter) cxoConnection_setCurrentSchema, 0, 0 }, { "external_name", (getter) cxoConnection_getExternalName, (setter) cxoConnection_setExternalName, 0, 0 }, { "internal_name", (getter) cxoConnection_getInternalName, (setter) cxoConnection_setInternalName, 0, 0 }, { "dbop", 0, (setter) cxoConnection_setDbOp, 0, 0 }, { "edition", (getter) cxoConnection_getEdition, 0, 0, 0 }, { "ltxid", (getter) cxoConnection_getLTXID, 0, 0, 0 }, { "handle", (getter) cxoConnection_getHandle, 0, 0, 0 }, { "Error", (getter) cxoConnection_getException, NULL, NULL, &cxoErrorException }, { "Warning", (getter) cxoConnection_getException, NULL, NULL, &cxoWarningException }, { "InterfaceError", (getter) cxoConnection_getException, NULL, NULL, &cxoInterfaceErrorException }, { "DatabaseError", (getter) cxoConnection_getException, NULL, NULL, &cxoDatabaseErrorException }, { "InternalError", (getter) cxoConnection_getException, NULL, NULL, &cxoInternalErrorException }, { "OperationalError", (getter) cxoConnection_getException, NULL, NULL, &cxoOperationalErrorException }, { "ProgrammingError", (getter) cxoConnection_getException, NULL, NULL, &cxoProgrammingErrorException }, { "IntegrityError", (getter) cxoConnection_getException, NULL, NULL, &cxoIntegrityErrorException }, { "DataError", (getter) cxoConnection_getException, NULL, NULL, &cxoDataErrorException }, { "NotSupportedError", (getter) cxoConnection_getException, NULL, NULL, &cxoNotSupportedErrorException }, { "callTimeout", (getter) cxoConnection_getCallTimeout, (setter) cxoConnection_setCallTimeout, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of the Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeConnection = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.Connection", .tp_basicsize = sizeof(cxoConnection), .tp_dealloc = (destructor) cxoConnection_free, .tp_repr = (reprfunc) cxoConnection_repr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_methods = cxoMethods, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers, .tp_init = (initproc) cxoConnection_init, .tp_new = (newfunc) cxoConnection_new }; python-cx_Oracle-8.3.0/src/cxoCursor.c000066400000000000000000002362351414105416400176750ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoCursor.c // Definition of the Python type Cursor. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoCursor_new() // Create a new cursor object. //----------------------------------------------------------------------------- static PyObject *cxoCursor_new(PyTypeObject *type, PyObject *args, PyObject *keywordArgs) { return type->tp_alloc(type, 0); } //----------------------------------------------------------------------------- // cxoCursor_init() // Create a new cursor object. //----------------------------------------------------------------------------- static int cxoCursor_init(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "connection", "scrollable", NULL }; cxoConnection *connection; int isScrollable; // parse arguments isScrollable = 0; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|p", keywordList, &cxoPyTypeConnection, &connection, &isScrollable)) return -1; cursor->isScrollable = (char) isScrollable; // initialize members Py_INCREF(connection); cursor->connection = connection; cursor->arraySize = 100; cursor->fetchArraySize = 100; cursor->prefetchRows = DPI_DEFAULT_PREFETCH_ROWS; cursor->bindArraySize = 1; cursor->isOpen = 1; return 0; } //----------------------------------------------------------------------------- // cxoCursor_repr() // Return a string representation of the cursor. //----------------------------------------------------------------------------- static PyObject *cxoCursor_repr(cxoCursor *cursor) { PyObject *connectionRepr, *module, *name, *result; connectionRepr = PyObject_Repr((PyObject*) cursor->connection); if (!connectionRepr) return NULL; if (cxoUtils_getModuleAndName(Py_TYPE(cursor), &module, &name) < 0) { Py_DECREF(connectionRepr); return NULL; } result = cxoUtils_formatString("<%s.%s on %s>", PyTuple_Pack(3, module, name, connectionRepr)); Py_DECREF(module); Py_DECREF(name); Py_DECREF(connectionRepr); return result; } //----------------------------------------------------------------------------- // cxoCursor_free() // Deallocate the cursor. //----------------------------------------------------------------------------- static void cxoCursor_free(cxoCursor *cursor) { Py_CLEAR(cursor->statement); Py_CLEAR(cursor->statementTag); Py_CLEAR(cursor->bindVariables); Py_CLEAR(cursor->fetchVariables); if (cursor->handle) { dpiStmt_release(cursor->handle); cursor->handle = NULL; } Py_CLEAR(cursor->connection); Py_CLEAR(cursor->rowFactory); Py_CLEAR(cursor->inputTypeHandler); Py_CLEAR(cursor->outputTypeHandler); Py_TYPE(cursor)->tp_free((PyObject*) cursor); } //----------------------------------------------------------------------------- // cxoCursor_isOpen() // Determines if the cursor object is open. Since the same cursor can be // used to execute multiple statements, simply checking for the DPI statement // handle is insufficient. //----------------------------------------------------------------------------- static int cxoCursor_isOpen(cxoCursor *cursor) { if (!cursor->isOpen) { cxoError_raiseFromString(cxoInterfaceErrorException, "not open"); return -1; } return cxoConnection_isConnected(cursor->connection); } //----------------------------------------------------------------------------- // cxoCursor_fetchRow() // Fetch a single row from the cursor. Internally the number of rows left in // the buffer is managed in order to minimize calls to Py_BEGIN_ALLOW_THREADS // and Py_END_ALLOW_THREADS which have a significant overhead. //----------------------------------------------------------------------------- static int cxoCursor_fetchRow(cxoCursor *cursor, int *found, uint32_t *bufferRowIndex) { int status; // if the number of rows in the fetch buffer is zero and there are more // rows to fetch, call DPI with threading enabled in order to perform any // fetch requiring a network round trip if (cursor->numRowsInFetchBuffer == 0 && cursor->moreRowsToFetch) { Py_BEGIN_ALLOW_THREADS status = dpiStmt_fetchRows(cursor->handle, cursor->fetchArraySize, &cursor->fetchBufferRowIndex, &cursor->numRowsInFetchBuffer, &cursor->moreRowsToFetch); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnInt(); } // keep track of where we are in the fetch buffer if (cursor->numRowsInFetchBuffer == 0) *found = 0; else { *found = 1; *bufferRowIndex = cursor->fetchBufferRowIndex++; cursor->numRowsInFetchBuffer--; } return 0; } //----------------------------------------------------------------------------- // cxoCursor_performDefine() // Perform the defines for the cursor. At this point it is assumed that the // statement being executed is in fact a query. //----------------------------------------------------------------------------- static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns) { PyObject *outputTypeHandler, *result; cxoTransformNum transformNum; cxoObjectType *objectType; dpiQueryInfo queryInfo; uint32_t pos, size; cxoDbType *dbType; char message[120]; cxoVar *var; // initialize fetching variables; these are used to reduce the number of // times that Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS is called as // there is a significant amount of overhead in making these calls cursor->numRowsInFetchBuffer = 0; cursor->moreRowsToFetch = 1; // if fetch variables already exist, nothing more to do (we are executing // the same statement and therefore all defines have already been // performed) if (cursor->fetchVariables) return 0; // create a list corresponding to the number of items cursor->fetchVariables = PyList_New(numQueryColumns); if (!cursor->fetchVariables) return -1; // create a variable for each of the query columns cursor->fetchArraySize = cursor->arraySize; for (pos = 1; pos <= numQueryColumns; pos++) { // get query information for the column position if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0) return cxoError_raiseAndReturnInt(); if (queryInfo.typeInfo.sizeInChars) size = queryInfo.typeInfo.sizeInChars; else size = queryInfo.typeInfo.clientSizeInBytes; // determine object type, if applicable objectType = NULL; if (queryInfo.typeInfo.objectType) { objectType = cxoObjectType_new(cursor->connection, queryInfo.typeInfo.objectType); if (!objectType) return -1; } // determine the default types to use transformNum = cxoTransform_getNumFromDataTypeInfo(&queryInfo.typeInfo); if (transformNum == CXO_TRANSFORM_UNSUPPORTED) { snprintf(message, sizeof(message), "Oracle type %d not supported.", queryInfo.typeInfo.oracleTypeNum); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return -1; } dbType = cxoDbType_fromTransformNum(transformNum); if (!dbType) return -1; // see if an output type handler should be used var = NULL; outputTypeHandler = NULL; if (cursor->outputTypeHandler && cursor->outputTypeHandler != Py_None) outputTypeHandler = cursor->outputTypeHandler; else if (cursor->connection->outputTypeHandler && cursor->connection->outputTypeHandler != Py_None) outputTypeHandler = cursor->connection->outputTypeHandler; // if using an output type handler, None implies default behavior if (outputTypeHandler) { result = PyObject_CallFunction(outputTypeHandler, "Os#Oiii", cursor, queryInfo.name, (Py_ssize_t) queryInfo.nameLength, dbType, size, queryInfo.typeInfo.precision, queryInfo.typeInfo.scale); if (!result) { Py_XDECREF(objectType); return -1; } else if (result == Py_None) Py_DECREF(result); else if (!cxoVar_check(result)) { Py_DECREF(result); Py_XDECREF(objectType); PyErr_SetString(PyExc_TypeError, "expecting variable from output type handler"); return -1; } else { var = (cxoVar*) result; if (var->allocatedElements < cursor->fetchArraySize) { Py_DECREF(result); Py_XDECREF(objectType); PyErr_SetString(PyExc_TypeError, "expecting variable with array size large " "enough for fetch"); return -1; } } } // if no variable created yet, use the database metadata if (!var) { var = cxoVar_new(cursor, cursor->fetchArraySize, transformNum, size, 0, objectType); if (!var) { Py_XDECREF(objectType); return -1; } } // add the variable to the fetch variables and perform define Py_XDECREF(objectType); PyList_SET_ITEM(cursor->fetchVariables, pos - 1, (PyObject *) var); if (dpiStmt_define(cursor->handle, pos, var->handle) < 0) return cxoError_raiseAndReturnInt(); } return 0; } //----------------------------------------------------------------------------- // cxoCursor_verifyFetch() // Verify that fetching may happen from this cursor. //----------------------------------------------------------------------------- static int cxoCursor_verifyFetch(cxoCursor *cursor) { uint32_t numQueryColumns; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return -1; // fixup REF cursor, if applicable if (cursor->fixupRefCursor) { cursor->fetchArraySize = cursor->arraySize; if (dpiStmt_setFetchArraySize(cursor->handle, cursor->fetchArraySize) < 0) return cxoError_raiseAndReturnInt(); if (dpiStmt_getNumQueryColumns(cursor->handle, &numQueryColumns) < 0) return cxoError_raiseAndReturnInt(); if (cxoCursor_performDefine(cursor, numQueryColumns) < 0) return cxoError_raiseAndReturnInt(); cursor->fixupRefCursor = 0; } // make sure the cursor is for a query if (!cursor->fetchVariables) { cxoError_raiseFromString(cxoInterfaceErrorException, "not a query"); return -1; } return 0; } //----------------------------------------------------------------------------- // cxoCursor_itemDescription() // Return a tuple describing the item at the given position. //----------------------------------------------------------------------------- static PyObject *cxoCursor_itemDescription(cxoCursor *cursor, uint32_t pos) { int displaySize, index; dpiQueryInfo queryInfo; PyObject *tuple, *temp; cxoDbType *dbType; // get information about the column position if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0) return NULL; dbType = cxoDbType_fromDataTypeInfo(&queryInfo.typeInfo); if (!dbType) return NULL; // set display size based on data type switch (queryInfo.typeInfo.oracleTypeNum) { case DPI_ORACLE_TYPE_VARCHAR: case DPI_ORACLE_TYPE_NVARCHAR: case DPI_ORACLE_TYPE_CHAR: case DPI_ORACLE_TYPE_NCHAR: case DPI_ORACLE_TYPE_ROWID: displaySize = (int) queryInfo.typeInfo.sizeInChars; break; case DPI_ORACLE_TYPE_RAW: displaySize = (int) queryInfo.typeInfo.clientSizeInBytes; break; case DPI_ORACLE_TYPE_NATIVE_FLOAT: case DPI_ORACLE_TYPE_NATIVE_DOUBLE: case DPI_ORACLE_TYPE_NATIVE_INT: case DPI_ORACLE_TYPE_NUMBER: if (queryInfo.typeInfo.precision) { displaySize = queryInfo.typeInfo.precision + 1; if (queryInfo.typeInfo.scale > 0) displaySize += queryInfo.typeInfo.scale + 1; } else displaySize = 127; break; case DPI_ORACLE_TYPE_DATE: case DPI_ORACLE_TYPE_TIMESTAMP: displaySize = 23; break; default: displaySize = 0; } // create the tuple and populate it tuple = PyTuple_New(7); if (!tuple) return NULL; // set each of the items in the tuple PyTuple_SET_ITEM(tuple, 0, PyUnicode_Decode(queryInfo.name, queryInfo.nameLength, cursor->connection->encodingInfo.encoding, NULL)); Py_INCREF(dbType); PyTuple_SET_ITEM(tuple, 1, (PyObject*) dbType); if (displaySize) PyTuple_SET_ITEM(tuple, 2, PyLong_FromLong(displaySize)); else { Py_INCREF(Py_None); PyTuple_SET_ITEM(tuple, 2, Py_None); } if (queryInfo.typeInfo.clientSizeInBytes) PyTuple_SET_ITEM(tuple, 3, PyLong_FromLong(queryInfo.typeInfo.clientSizeInBytes)); else { Py_INCREF(Py_None); PyTuple_SET_ITEM(tuple, 3, Py_None); } if (queryInfo.typeInfo.precision || queryInfo.typeInfo.scale || queryInfo.typeInfo.fsPrecision) { PyTuple_SET_ITEM(tuple, 4, PyLong_FromLong(queryInfo.typeInfo.precision)); PyTuple_SET_ITEM(tuple, 5, PyLong_FromLong(queryInfo.typeInfo.scale + queryInfo.typeInfo.fsPrecision)); } else { Py_INCREF(Py_None); PyTuple_SET_ITEM(tuple, 4, Py_None); Py_INCREF(Py_None); PyTuple_SET_ITEM(tuple, 5, Py_None); } PyTuple_SET_ITEM(tuple, 6, PyLong_FromLong(queryInfo.nullOk != 0)); // make sure the tuple is ok for (index = 0; index < 7; index++) { temp = PyTuple_GET_ITEM(tuple, index); if (!temp) { Py_DECREF(tuple); return NULL; } else if (temp == Py_None) Py_INCREF(temp); } return tuple; } //----------------------------------------------------------------------------- // cxoCursor_getDescription() // Return a list of 7-tuples consisting of the description of the define // variables. //----------------------------------------------------------------------------- static PyObject *cxoCursor_getDescription(cxoCursor *cursor, void *unused) { uint32_t numQueryColumns, i; PyObject *results, *tuple; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // determine the number of query columns; if not a query return None if (!cursor->handle) Py_RETURN_NONE; if (dpiStmt_getNumQueryColumns(cursor->handle, &numQueryColumns) < 0) return cxoError_raiseAndReturnNull(); if (numQueryColumns == 0) Py_RETURN_NONE; // create a list of the required length results = PyList_New(numQueryColumns); if (!results) return NULL; // create tuples corresponding to the select-items for (i = 0; i < numQueryColumns; i++) { tuple = cxoCursor_itemDescription(cursor, i + 1); if (!tuple) { Py_DECREF(results); return NULL; } PyList_SET_ITEM(results, i, tuple); } return results; } //----------------------------------------------------------------------------- // cxoCursor_getLastRowid() // Return the rowid of the last modified row if applicable. If no row was // modified the value None is returned. //----------------------------------------------------------------------------- static PyObject *cxoCursor_getLastRowid(cxoCursor *cursor, void *unused) { uint32_t rowidStrLength; const char *rowidStr; dpiRowid *rowid; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // get the value, if applicable if (cursor->handle) { if (dpiStmt_getLastRowid(cursor->handle, &rowid) < 0) return cxoError_raiseAndReturnNull(); if (rowid) { if (dpiRowid_getStringValue(rowid, &rowidStr, &rowidStrLength) < 0) return cxoError_raiseAndReturnNull(); return PyUnicode_Decode(rowidStr, rowidStrLength, cursor->connection->encodingInfo.encoding, NULL); } } Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_getOciAttr() // Return the value of the OCI attribute. This is intended to be used for // testing attributes which are not currently exposed directly and should only // be used for that purpose. //----------------------------------------------------------------------------- static PyObject *cxoCursor_getOciAttr(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "attr_num", "attr_type", NULL }; unsigned attrNum, attrType; uint32_t valueLength; dpiDataBuffer value; // validate parameters if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "II", keywordList, &attrNum, &attrType)) return NULL; if (cxoCursor_isOpen(cursor) < 0) return NULL; // get value and convert it to the appropriate Python value if (dpiStmt_getOciAttr(cursor->handle, attrNum, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); return cxoUtils_convertOciAttrToPythonValue(attrType, &value, valueLength, cursor->connection->encodingInfo.encoding); } //----------------------------------------------------------------------------- // cxoCursor_getPrefetchRows() // Return an integer providing the number of rows that are prefetched by the // Oracle Client library. //----------------------------------------------------------------------------- static PyObject *cxoCursor_getPrefetchRows(cxoCursor *cursor, void *unused) { if (cxoCursor_isOpen(cursor) < 0) return NULL; return PyLong_FromUnsignedLong(cursor->prefetchRows); } //----------------------------------------------------------------------------- // cxoCursor_close() // Close the cursor. Any action taken on this cursor from this point forward // results in an exception being raised. //----------------------------------------------------------------------------- static PyObject *cxoCursor_close(cxoCursor *cursor, PyObject *args) { if (cxoCursor_isOpen(cursor) < 0) return NULL; Py_CLEAR(cursor->bindVariables); Py_CLEAR(cursor->fetchVariables); if (cursor->handle) { if (dpiStmt_close(cursor->handle, NULL, 0) < 0) return cxoError_raiseAndReturnNull(); dpiStmt_release(cursor->handle); cursor->handle = NULL; } cursor->isOpen = 0; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_setBindVariableHelper() // Helper for setting a bind variable. //----------------------------------------------------------------------------- static int cxoCursor_setBindVariableHelper(cxoCursor *cursor, unsigned numElements, unsigned arrayPos, PyObject *value, cxoVar *origVar, cxoVar **newVar, int deferTypeAssignment) { cxoVar *varToSet; int isValueVar; // initialization *newVar = NULL; isValueVar = cxoVar_check(value); // handle case where variable is already bound, either from a prior // execution or a call to setinputsizes() if (origVar) { // if the value is a variable object, rebind it if necessary if (isValueVar) { if ( (PyObject*) origVar != value) { Py_INCREF(value); *newVar = (cxoVar*) value; } // otherwise, attempt to set the value, but if this fails, simply // ignore the original bind variable and create a new one; this is // intended for cases where the type changes between executions of a // statement or where setinputsizes() has been called with the wrong // type (as mandated by the DB API) } else { varToSet = origVar; // first check to see if the variable transform is for None (which // can happen if all of the values in a previous invocation of // executemany() were None) and there is now a value; in this case, // discard the original variable and have a new one created if (origVar->transformNum == CXO_TRANSFORM_NONE && value != Py_None) { origVar = NULL; varToSet = NULL; // otherwise, if the number of elements has changed, create a new // variable this is only necessary for executemany() since // execute() always passes a value of 1 for the number of elements } else if (numElements > origVar->allocatedElements) { *newVar = cxoVar_new(cursor, numElements, origVar->transformNum, origVar->size, origVar->isArray, origVar->objectType); if (!*newVar) return -1; varToSet = *newVar; } // attempt to set the value if (varToSet && cxoVar_setValue(varToSet, arrayPos, value) < 0) { // executemany() should simply fail after the first element if (arrayPos > 0) return -1; // clear the exception and try to create a new variable PyErr_Clear(); Py_CLEAR(*newVar); origVar = NULL; } } } // if no original variable used, create a new one if (!origVar) { // if the value is a variable object, bind it directly if (isValueVar) { Py_INCREF(value); *newVar = (cxoVar*) value; // otherwise, create a new variable, unless the value is None and // we wish to defer type assignment } else if (value != Py_None || !deferTypeAssignment) { *newVar = cxoVar_newByValue(cursor, value, numElements); if (!*newVar) return -1; if (cxoVar_setValue(*newVar, arrayPos, value) < 0) { Py_CLEAR(*newVar); return -1; } } } return 0; } //----------------------------------------------------------------------------- // cxoCursor_setBindVariables() // Create or set bind variables. //----------------------------------------------------------------------------- int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters, unsigned numElements, unsigned arrayPos, int deferTypeAssignment) { uint32_t i, origBoundByPos, origNumParams, boundByPos, numParams; PyObject *key, *value, *origVar; cxoVar *newVar; Py_ssize_t pos, temp; // make sure positional and named binds are not being intermixed origNumParams = numParams = 0; boundByPos = PySequence_Check(parameters); if (boundByPos) { temp = PySequence_Size(parameters); if (temp < 0) return -1; numParams = (uint32_t) temp; } if (cursor->bindVariables) { origBoundByPos = PyList_Check(cursor->bindVariables); if (boundByPos != origBoundByPos) { cxoError_raiseFromString(cxoProgrammingErrorException, "positional and named binds cannot be intermixed"); return -1; } if (origBoundByPos) origNumParams = (uint32_t) PyList_GET_SIZE(cursor->bindVariables); // otherwise, create the list or dictionary if needed } else { if (boundByPos) cursor->bindVariables = PyList_New(numParams); else cursor->bindVariables = PyDict_New(); if (!cursor->bindVariables) return -1; } // handle positional binds if (boundByPos) { for (i = 0; i < numParams; i++) { value = PySequence_GetItem(parameters, i); if (!value) return -1; Py_DECREF(value); if (i < origNumParams) { origVar = PyList_GET_ITEM(cursor->bindVariables, i); if (origVar == Py_None) origVar = NULL; } else origVar = NULL; if (cxoCursor_setBindVariableHelper(cursor, numElements, arrayPos, value, (cxoVar*) origVar, &newVar, deferTypeAssignment) < 0) return -1; if (newVar) { if (i < (uint32_t) PyList_GET_SIZE(cursor->bindVariables)) { if (PyList_SetItem(cursor->bindVariables, i, (PyObject*) newVar) < 0) { Py_DECREF(newVar); return -1; } } else { if (PyList_Append(cursor->bindVariables, (PyObject*) newVar) < 0) { Py_DECREF(newVar); return -1; } Py_DECREF(newVar); } } } // handle named binds } else { pos = 0; while (PyDict_Next(parameters, &pos, &key, &value)) { origVar = PyDict_GetItem(cursor->bindVariables, key); if (cxoCursor_setBindVariableHelper(cursor, numElements, arrayPos, value, (cxoVar*) origVar, &newVar, deferTypeAssignment) < 0) return -1; if (newVar) { if (PyDict_SetItem(cursor->bindVariables, key, (PyObject*) newVar) < 0) { Py_DECREF(newVar); return -1; } Py_DECREF(newVar); } } } return 0; } //----------------------------------------------------------------------------- // cxoCursor_performBind() // Perform the binds on the cursor. //----------------------------------------------------------------------------- int cxoCursor_performBind(cxoCursor *cursor) { PyObject *key, *var; Py_ssize_t pos; int i; // ensure that input sizes are reset // this is done before binding is attempted so that if binding fails and // a new statement is prepared, the bind variables will be reset and // spurious errors will not occur cursor->setInputSizes = 0; // set values and perform binds for all bind variables if (cursor->bindVariables) { if (PyDict_Check(cursor->bindVariables)) { pos = 0; while (PyDict_Next(cursor->bindVariables, &pos, &key, &var)) { if (cxoVar_bind((cxoVar*) var, cursor, key, 0) < 0) return -1; } } else { for (i = 0; i < PyList_GET_SIZE(cursor->bindVariables); i++) { var = PyList_GET_ITEM(cursor->bindVariables, i); if (var != Py_None) { if (cxoVar_bind((cxoVar*) var, cursor, NULL, i + 1) < 0) return -1; } } } } return 0; } //----------------------------------------------------------------------------- // cxoCursor_createRow() // Create an object for the row. The object created is a tuple unless a row // factory function has been defined in which case it is the result of the // row factory function called with the argument tuple that would otherwise be // returned. //----------------------------------------------------------------------------- static PyObject *cxoCursor_createRow(cxoCursor *cursor, uint32_t pos) { PyObject *tuple, *item, *result; Py_ssize_t numItems, i; cxoVar *var; // bump row count as a new row has been found cursor->rowCount++; // create a new tuple numItems = PyList_GET_SIZE(cursor->fetchVariables); tuple = PyTuple_New(numItems); if (!tuple) return NULL; // acquire the value for each item for (i = 0; i < numItems; i++) { var = (cxoVar*) PyList_GET_ITEM(cursor->fetchVariables, i); item = cxoVar_getSingleValue(var, var->data, pos); if (!item) { Py_DECREF(tuple); return NULL; } PyTuple_SET_ITEM(tuple, i, item); } // if a row factory is defined, call it if (cursor->rowFactory && cursor->rowFactory != Py_None) { result = PyObject_CallObject(cursor->rowFactory, tuple); Py_DECREF(tuple); return result; } return tuple; } //----------------------------------------------------------------------------- // cxoCursor_internalPrepare() // Internal method for preparing a statement for execution. //----------------------------------------------------------------------------- static int cxoCursor_internalPrepare(cxoCursor *cursor, PyObject *statement, PyObject *statementTag) { cxoBuffer statementBuffer, tagBuffer; int status; // make sure we don't get a situation where nothing is to be executed if (statement == Py_None && !cursor->statement) { cxoError_raiseFromString(cxoProgrammingErrorException, "no statement specified and no prior statement prepared"); return -1; } // nothing to do if the statement is identical to the one already stored // but go ahead and prepare anyway for create, alter and drop statments if (statement == Py_None || statement == cursor->statement) { if (cursor->handle && !cursor->stmtInfo.isDDL) return 0; statement = cursor->statement; } // keep track of the statement Py_XDECREF(cursor->statement); Py_INCREF(statement); cursor->statement = statement; // keep track of the tag Py_XDECREF(cursor->statementTag); Py_XINCREF(statementTag); cursor->statementTag = statementTag; // clear fetch and bind variables if applicable Py_CLEAR(cursor->fetchVariables); if (!cursor->setInputSizes) Py_CLEAR(cursor->bindVariables); // prepare statement if (cxoBuffer_fromObject(&statementBuffer, statement, cursor->connection->encodingInfo.encoding) < 0) return -1; if (cxoBuffer_fromObject(&tagBuffer, statementTag, cursor->connection->encodingInfo.encoding) < 0) { cxoBuffer_clear(&statementBuffer); return -1; } Py_BEGIN_ALLOW_THREADS if (cursor->handle) dpiStmt_release(cursor->handle); status = dpiConn_prepareStmt(cursor->connection->handle, cursor->isScrollable, (const char*) statementBuffer.ptr, statementBuffer.size, (const char*) tagBuffer.ptr, tagBuffer.size, &cursor->handle); Py_END_ALLOW_THREADS cxoBuffer_clear(&statementBuffer); cxoBuffer_clear(&tagBuffer); if (status < 0) return cxoError_raiseAndReturnInt(); // get statement information if (dpiStmt_getInfo(cursor->handle, &cursor->stmtInfo) < 0) return cxoError_raiseAndReturnInt(); // set the fetch array size, if applicable if (cursor->stmtInfo.statementType == DPI_STMT_TYPE_SELECT) { if (dpiStmt_setFetchArraySize(cursor->handle, cursor->arraySize) < 0) return cxoError_raiseAndReturnInt(); } // set number of rows to prefetch on execute, if applicable if (cursor->prefetchRows != DPI_DEFAULT_PREFETCH_ROWS) { if (dpiStmt_setPrefetchRows(cursor->handle, cursor->prefetchRows) < 0) return cxoError_raiseAndReturnInt(); } // clear row factory, if applicable Py_CLEAR(cursor->rowFactory); return 0; } //----------------------------------------------------------------------------- // cxoCursor_parse() // Parse the statement without executing it. This also retrieves information // about the select list for select statements. //----------------------------------------------------------------------------- static PyObject *cxoCursor_parse(cxoCursor *cursor, PyObject *statement) { uint32_t mode, numQueryColumns; dpiStmtInfo stmtInfo; int status; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // prepare the statement and get statement information if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0) return NULL; if (dpiStmt_getInfo(cursor->handle, &stmtInfo) < 0) return cxoError_raiseAndReturnNull(); // parse the statement if (stmtInfo.isQuery) mode = DPI_MODE_EXEC_DESCRIBE_ONLY; else mode = DPI_MODE_EXEC_PARSE_ONLY; Py_BEGIN_ALLOW_THREADS status = dpiStmt_execute(cursor->handle, mode, &numQueryColumns); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_prepare() // Prepare the statement for execution. //----------------------------------------------------------------------------- static PyObject *cxoCursor_prepare(cxoCursor *cursor, PyObject *args) { PyObject *statement, *statementTag; // statement text and optional tag is expected statementTag = NULL; if (!PyArg_ParseTuple(args, "O|O", &statement, &statementTag)) return NULL; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // prepare the statement if (cxoCursor_internalPrepare(cursor, statement, statementTag) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_callCalculateSize() // Calculate the size of the statement that is to be executed. //----------------------------------------------------------------------------- static int cxoCursor_callCalculateSize(PyObject *name, cxoVar *returnValue, PyObject *listOfArguments, PyObject *keywordArguments, int *size) { Py_ssize_t numPositionalArgs, numKeywordArgs; // set base size without any arguments *size = 17; // add any additional space required to handle the return value if (returnValue) *size += 6; // assume up to 9 characters for each positional argument // this allows up to four digits for the placeholder if the bind variale // is a boolean value (prior to Oracle 12.1) numPositionalArgs = 0; if (listOfArguments) { numPositionalArgs = PySequence_Size(listOfArguments); if (numPositionalArgs < 0) return -1; *size += (int) (numPositionalArgs * 9); } // assume up to 15 characters for each keyword argument // this allows up to four digits for the placeholder if the bind variable // is a boolean value (prior to Oracle 12.1) numKeywordArgs = 0; if (keywordArguments) { numKeywordArgs = PyDict_Size(keywordArguments); if (numKeywordArgs < 0) return -1; *size += (int) (numKeywordArgs * 15); } // the above assume a maximum of 10,000 arguments; check and raise an // error if the number of arguments exceeds this value; more than this // number would probably be unusable in any case! if (numPositionalArgs + numKeywordArgs > 10000) { cxoError_raiseFromString(cxoInterfaceErrorException, "too many arguments"); return -1; } return 0; } //----------------------------------------------------------------------------- // cxoCursor_callBuildStatement() // Determine the statement and the bind variables to bind to the statement // that is created for calling a stored procedure or function. //----------------------------------------------------------------------------- static int cxoCursor_callBuildStatement(PyObject *name, cxoVar *returnValue, PyObject *listOfArguments, PyObject *keywordArguments, char *statement, PyObject **statementObj, PyObject **bindVariables) { PyObject *key, *value, *formatArgs, *positionalArgs; uint32_t i, argNum, numPositionalArgs; Py_ssize_t pos; char *ptr; // initialize the bind variables to the list of positional arguments if (listOfArguments) *bindVariables = PySequence_List(listOfArguments); else *bindVariables = PyList_New(0); if (!*bindVariables) return -1; // insert the return variable, if applicable if (returnValue) { if (PyList_Insert(*bindVariables, 0, (PyObject*) returnValue) < 0) return -1; } // initialize format arguments formatArgs = PyList_New(0); if (!formatArgs) return -1; if (PyList_Append(formatArgs, name) < 0) { Py_DECREF(formatArgs); return -1; } // begin building the statement argNum = 1; strcpy(statement, "begin "); if (returnValue) { strcat(statement, ":1 := "); argNum++; } strcat(statement, "%s"); ptr = statement + strlen(statement); *ptr++ = '('; // include any positional arguments first if (listOfArguments) { positionalArgs = PySequence_Fast(listOfArguments, "expecting sequence of arguments"); if (!positionalArgs) { Py_DECREF(formatArgs); return -1; } numPositionalArgs = (uint32_t) PySequence_Size(listOfArguments); for (i = 0; i < numPositionalArgs; i++) { if (i > 0) *ptr++ = ','; ptr += sprintf(ptr, ":%d", argNum++); if (cxoClientVersionInfo.versionNum < 12 && PyBool_Check(PySequence_Fast_GET_ITEM(positionalArgs, i))) ptr += sprintf(ptr, " = 1"); } Py_DECREF(positionalArgs); } // next append any keyword arguments if (keywordArguments) { pos = 0; while (PyDict_Next(keywordArguments, &pos, &key, &value)) { if (PyList_Append(*bindVariables, value) < 0) { Py_DECREF(formatArgs); return -1; } if (PyList_Append(formatArgs, key) < 0) { Py_DECREF(formatArgs); return -1; } if ((argNum > 1 && !returnValue) || (argNum > 2 && returnValue)) *ptr++ = ','; ptr += sprintf(ptr, "%%s => :%d", argNum++); if (cxoClientVersionInfo.versionNum < 12 && PyBool_Check(value)) ptr += sprintf(ptr, " = 1"); } } // create statement object strcpy(ptr, "); end;"); *statementObj = cxoUtils_formatString(statement, PyList_AsTuple(formatArgs)); Py_DECREF(formatArgs); if (!*statementObj) return -1; return 0; } //----------------------------------------------------------------------------- // cxoCursor_call() // Call a stored procedure or function. //----------------------------------------------------------------------------- static int cxoCursor_call(cxoCursor *cursor, cxoVar *returnValue, PyObject *name, PyObject *listOfArguments, PyObject *keywordArguments) { PyObject *bindVariables, *statementObj, *results; int statementSize; char *statement; // verify that the arguments are passed correctly if (listOfArguments) { if (!PySequence_Check(listOfArguments)) { PyErr_SetString(PyExc_TypeError, "arguments must be a sequence"); return -1; } } if (keywordArguments) { if (!PyDict_Check(keywordArguments)) { PyErr_SetString(PyExc_TypeError, "keyword arguments must be a dictionary"); return -1; } } // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return -1; // determine the statement size if (cxoCursor_callCalculateSize(name, returnValue, listOfArguments, keywordArguments, &statementSize) < 0) return -1; // allocate a string for the statement statement = (char*) PyMem_Malloc(statementSize); if (!statement) { PyErr_NoMemory(); return -1; } // determine the statement to execute and the argument to pass bindVariables = statementObj = NULL; if (cxoCursor_callBuildStatement(name, returnValue, listOfArguments, keywordArguments, statement, &statementObj, &bindVariables) < 0) { PyMem_Free(statement); Py_XDECREF(statementObj); Py_XDECREF(bindVariables); return -1; } PyMem_Free(statement); // execute the statement on the cursor results = PyObject_CallMethod( (PyObject*) cursor, "execute", "OO", statementObj, bindVariables); Py_DECREF(statementObj); Py_DECREF(bindVariables); if (!results) return -1; Py_DECREF(results); return 0; } //----------------------------------------------------------------------------- // cxoCursor_callFunc() // Call a stored function and return the return value of the function. //----------------------------------------------------------------------------- static PyObject *cxoCursor_callFunc(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "name", "returnType", "parameters", "keyword_parameters", "keywordParameters", NULL }; PyObject *listOfArguments, *keywordArguments, *keywordArgumentsDeprecated; PyObject *returnType, *results, *name; cxoVar *var; // parse arguments listOfArguments = keywordArguments = keywordArgumentsDeprecated = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|OOO", keywordList, &name, &returnType, &listOfArguments, &keywordArguments, &keywordArgumentsDeprecated)) return NULL; if (keywordArgumentsDeprecated) { if (keywordArguments) { cxoError_raiseFromString(cxoProgrammingErrorException, "keyword_parameters and keywordParameters cannot both be " "specified"); return NULL; } keywordArguments = keywordArgumentsDeprecated; } // create the return variable var = cxoVar_newByType(cursor, returnType, 1); if (!var) return NULL; // call the function if (cxoCursor_call(cursor, var, name, listOfArguments, keywordArguments) < 0) return NULL; // determine the results results = cxoVar_getValue(var, 0); Py_DECREF(var); return results; } //----------------------------------------------------------------------------- // cxoCursor_callProc() // Call a stored procedure and return the (possibly modified) arguments. //----------------------------------------------------------------------------- static PyObject *cxoCursor_callProc(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "name", "parameters", "keyword_parameters", "keywordParameters", NULL }; PyObject *listOfArguments, *keywordArguments, *keywordArgumentsDeprecated; PyObject *results, *var, *temp, *name; Py_ssize_t numArgs, i; // parse arguments listOfArguments = keywordArguments = keywordArgumentsDeprecated = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OOO", keywordList, &name, &listOfArguments, &keywordArguments, &keywordArgumentsDeprecated)) return NULL; if (keywordArgumentsDeprecated) { if (keywordArguments) { cxoError_raiseFromString(cxoProgrammingErrorException, "keyword_parameters and keywordParameters cannot both be " "specified"); return NULL; } keywordArguments = keywordArgumentsDeprecated; } // call the stored procedure if (cxoCursor_call(cursor, NULL, name, listOfArguments, keywordArguments) < 0) return NULL; // create the return value (only positional arguments are returned) numArgs = (listOfArguments) ? PySequence_Size(listOfArguments) : 0; results = PyList_New(numArgs); if (!results) return NULL; for (i = 0; i < numArgs; i++) { var = PyList_GET_ITEM(cursor->bindVariables, i); temp = cxoVar_getValue((cxoVar*) var, 0); if (!temp) { Py_DECREF(results); return NULL; } PyList_SET_ITEM(results, i, temp); } return results; } //----------------------------------------------------------------------------- // cxoCursor_execute() // Execute the statement. //----------------------------------------------------------------------------- static PyObject *cxoCursor_execute(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { PyObject *statement, *executeArgs; uint32_t numQueryColumns, mode; int status; executeArgs = NULL; if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs)) return NULL; if (executeArgs && keywordArgs) { if (PyDict_Size(keywordArgs) == 0) keywordArgs = NULL; else return cxoError_raiseFromString(cxoInterfaceErrorException, "expecting argument or keyword arguments, not both"); } if (keywordArgs) executeArgs = keywordArgs; if (executeArgs) { if (!PyDict_Check(executeArgs) && !PySequence_Check(executeArgs)) { PyErr_SetString(PyExc_TypeError, "expecting a dictionary, sequence or keyword args"); return NULL; } } // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // prepare the statement, if applicable if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0) return NULL; // perform binds if (executeArgs && cxoCursor_setBindVariables(cursor, executeArgs, 1, 0, 0) < 0) return NULL; if (cxoCursor_performBind(cursor) < 0) return NULL; // execute the statement Py_BEGIN_ALLOW_THREADS mode = (cursor->connection->autocommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS : DPI_MODE_EXEC_DEFAULT; status = dpiStmt_execute(cursor->handle, mode, &numQueryColumns); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); // get the count of the rows affected if (dpiStmt_getRowCount(cursor->handle, &cursor->rowCount) < 0) return cxoError_raiseAndReturnNull(); // for queries, return the cursor for convenience if (numQueryColumns > 0) { if (cxoCursor_performDefine(cursor, numQueryColumns) < 0) { Py_CLEAR(cursor->fetchVariables); return NULL; } Py_INCREF(cursor); return (PyObject*) cursor; } // for statements other than queries, simply return None Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_executeMany() // Execute the statement many times. The number of times is equivalent to the // number of elements in the list of parameters, or the provided integer if no // parameters are required. //----------------------------------------------------------------------------- static PyObject *cxoCursor_executeMany(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "statement", "parameters", "batcherrors", "arraydmlrowcounts", NULL }; int arrayDMLRowCountsEnabled = 0, batchErrorsEnabled = 0; PyObject *arguments, *parameters, *statement; uint32_t mode, i, numRows; int status; // validate parameters if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|ii", keywordList, &statement, ¶meters, &batchErrorsEnabled, &arrayDMLRowCountsEnabled)) return NULL; if (!PyList_Check(parameters) && !PyLong_Check(parameters)) { PyErr_SetString(PyExc_TypeError, "parameters should be a list of sequences/dictionaries " "or an integer specifying the number of times to execute " "the statement"); return NULL; } // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // determine execution mode mode = (cursor->connection->autocommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS : DPI_MODE_EXEC_DEFAULT; if (batchErrorsEnabled) mode |= DPI_MODE_EXEC_BATCH_ERRORS; if (arrayDMLRowCountsEnabled) mode |= DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS; // prepare the statement if (cxoCursor_internalPrepare(cursor, statement, NULL) < 0) return NULL; // perform binds, as required if (PyLong_Check(parameters)) numRows = (uint32_t) PyLong_AsLong(parameters); else { numRows = (uint32_t) PyList_GET_SIZE(parameters); for (i = 0; i < numRows; i++) { arguments = PyList_GET_ITEM(parameters, i); if (!PyDict_Check(arguments) && !PySequence_Check(arguments)) return cxoError_raiseFromString(cxoInterfaceErrorException, "expecting a list of dictionaries or sequences"); if (cxoCursor_setBindVariables(cursor, arguments, numRows, i, (i < numRows - 1)) < 0) return NULL; } } if (cxoCursor_performBind(cursor) < 0) return NULL; // execute the statement, but only if the number of rows is greater than // zero since Oracle raises an error otherwise if (numRows > 0) { Py_BEGIN_ALLOW_THREADS status = dpiStmt_executeMany(cursor->handle, mode, numRows); Py_END_ALLOW_THREADS if (status < 0) { cxoError_raiseAndReturnNull(); dpiStmt_getRowCount(cursor->handle, &cursor->rowCount); return NULL; } if (dpiStmt_getRowCount(cursor->handle, &cursor->rowCount) < 0) return cxoError_raiseAndReturnNull(); } Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_executeManyPrepared() // Execute the prepared statement the number of times requested. At this // point, the statement must have been already prepared and the bind variables // must have their values set. //----------------------------------------------------------------------------- static PyObject *cxoCursor_executeManyPrepared(cxoCursor *cursor, PyObject *args) { int numIters, status; // expect number of times to execute the statement if (!PyArg_ParseTuple(args, "i", &numIters)) return NULL; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // perform binds if (cxoCursor_performBind(cursor) < 0) return NULL; // execute the statement Py_BEGIN_ALLOW_THREADS status = dpiStmt_executeMany(cursor->handle, DPI_MODE_EXEC_DEFAULT, numIters); Py_END_ALLOW_THREADS if (status < 0 || dpiStmt_getRowCount(cursor->handle, &cursor->rowCount) < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_multiFetch() // Return a list consisting of the remaining rows up to the given row limit // (if specified). //----------------------------------------------------------------------------- static PyObject *cxoCursor_multiFetch(cxoCursor *cursor, int rowLimit) { uint32_t bufferRowIndex = 0; PyObject *results, *row; int found, rowNum; // verify fetch can be performed if (cxoCursor_verifyFetch(cursor) < 0) return NULL; // create an empty list results = PyList_New(0); if (!results) return NULL; // fetch as many rows as possible for (rowNum = 0; rowLimit == 0 || rowNum < rowLimit; rowNum++) { if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0) { Py_DECREF(results); return NULL; } if (!found) break; row = cxoCursor_createRow(cursor, bufferRowIndex); if (!row) { Py_DECREF(results); return NULL; } if (PyList_Append(results, row) < 0) { Py_DECREF(row); Py_DECREF(results); return NULL; } Py_DECREF(row); } return results; } //----------------------------------------------------------------------------- // cxoCursor_fetchOne() // Fetch a single row from the cursor. //----------------------------------------------------------------------------- static PyObject *cxoCursor_fetchOne(cxoCursor *cursor, PyObject *args) { uint32_t bufferRowIndex = 0; int found = 0; if (cxoCursor_verifyFetch(cursor) < 0) return NULL; if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0) return NULL; if (found) return cxoCursor_createRow(cursor, bufferRowIndex); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_fetchMany() // Fetch multiple rows from the cursor based on the arraysize. //----------------------------------------------------------------------------- static PyObject *cxoCursor_fetchMany(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "numRows", NULL }; int rowLimit; // parse arguments -- optional rowlimit expected rowLimit = cursor->arraySize; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList, &rowLimit)) return NULL; return cxoCursor_multiFetch(cursor, rowLimit); } //----------------------------------------------------------------------------- // cxoCursor_fetchAll() // Fetch all remaining rows from the cursor. //----------------------------------------------------------------------------- static PyObject *cxoCursor_fetchAll(cxoCursor *cursor, PyObject *args) { return cxoCursor_multiFetch(cursor, 0); } //----------------------------------------------------------------------------- // cxoCursor_fetchRaw() // Perform raw fetch on the cursor; return the actual number of rows fetched. //----------------------------------------------------------------------------- static PyObject *cxoCursor_fetchRaw(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "numRows", NULL }; uint32_t numRowsToFetch, numRowsFetched, bufferRowIndex; int moreRows; // expect an optional number of rows to retrieve numRowsToFetch = cursor->fetchArraySize; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList, &numRowsToFetch)) return NULL; if (numRowsToFetch > cursor->fetchArraySize) return cxoError_raiseFromString(cxoInterfaceErrorException, "rows to fetch exceeds array size"); // perform the fetch if (dpiStmt_fetchRows(cursor->handle, numRowsToFetch, &bufferRowIndex, &numRowsFetched, &moreRows) < 0) return cxoError_raiseAndReturnNull(); cursor->rowCount += numRowsFetched; cursor->numRowsInFetchBuffer = 0; return PyLong_FromLong(numRowsFetched); } //----------------------------------------------------------------------------- // cxoCursor_scroll() // Scroll the cursor using the value and mode specified. //----------------------------------------------------------------------------- static PyObject *cxoCursor_scroll(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "value", "mode", NULL }; dpiFetchMode mode; int32_t offset; char *strMode; int status; // parse arguments offset = 0; strMode = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|is", keywordList, &offset, &strMode)) return NULL; // validate mode if (!strMode) mode = DPI_MODE_FETCH_RELATIVE; else if (strcmp(strMode, "relative") == 0) mode = DPI_MODE_FETCH_RELATIVE; else if (strcmp(strMode, "absolute") == 0) mode = DPI_MODE_FETCH_ABSOLUTE; else if (strcmp(strMode, "first") == 0) mode = DPI_MODE_FETCH_FIRST; else if (strcmp(strMode, "last") == 0) mode = DPI_MODE_FETCH_LAST; else return cxoError_raiseFromString(cxoInterfaceErrorException, "mode must be one of relative, absolute, first or last"); // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // perform scroll and get new row count and number of rows in buffer Py_BEGIN_ALLOW_THREADS status = dpiStmt_scroll(cursor->handle, mode, offset, 0 - cursor->numRowsInFetchBuffer); if (status == 0) status = dpiStmt_fetchRows(cursor->handle, cursor->fetchArraySize, &cursor->fetchBufferRowIndex, &cursor->numRowsInFetchBuffer, &cursor->moreRowsToFetch); if (status == 0) status = dpiStmt_getRowCount(cursor->handle, &cursor->rowCount); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); cursor->rowCount -= cursor->numRowsInFetchBuffer; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_setInputSizes() // Set the sizes of the bind variables. //----------------------------------------------------------------------------- static PyObject *cxoCursor_setInputSizes(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { Py_ssize_t numPositionalArgs, numKeywordArgs = 0, i; PyObject *key, *value; cxoVar *var; // only expect keyword arguments or positional arguments, not both numPositionalArgs = PyTuple_Size(args); if (keywordArgs) numKeywordArgs = PyDict_Size(keywordArgs); if (numKeywordArgs > 0 && numPositionalArgs > 0) return cxoError_raiseFromString(cxoInterfaceErrorException, "expecting arguments or keyword arguments, not both"); // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // eliminate existing bind variables Py_CLEAR(cursor->bindVariables); // if no values passed, do nothing further, but return an empty list or // dictionary as appropriate if (numKeywordArgs == 0 && numPositionalArgs == 0) { if (keywordArgs) return PyDict_New(); return PyList_New(0); } // retain bind variables cursor->setInputSizes = 1; if (numKeywordArgs > 0) cursor->bindVariables = PyDict_New(); else cursor->bindVariables = PyList_New(numPositionalArgs); if (!cursor->bindVariables) return NULL; // process each input if (numKeywordArgs > 0) { i = 0; while (PyDict_Next(keywordArgs, &i, &key, &value)) { var = cxoVar_newByType(cursor, value, cursor->bindArraySize); if (!var) return NULL; if (PyDict_SetItem(cursor->bindVariables, key, (PyObject*) var) < 0) { Py_DECREF(var); return NULL; } Py_DECREF(var); } } else { for (i = 0; i < numPositionalArgs; i++) { value = PyTuple_GET_ITEM(args, i); if (value == Py_None) { Py_INCREF(Py_None); PyList_SET_ITEM(cursor->bindVariables, i, Py_None); } else { var = cxoVar_newByType(cursor, value, cursor->bindArraySize); if (!var) return NULL; PyList_SET_ITEM(cursor->bindVariables, i, (PyObject*) var); } } } Py_INCREF(cursor->bindVariables); return cursor->bindVariables; } //----------------------------------------------------------------------------- // cxoCursor_setOciAttr() // Set the value of the OCI attribute to the specified value. This is // intended to be used for testing attributes which are not currently exposed // directly and should only be used for that purpose. //----------------------------------------------------------------------------- static PyObject *cxoCursor_setOciAttr(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "attr_num", "attr_type", "value", NULL }; unsigned attrNum, attrType; uint32_t ociValueLength; dpiDataBuffer ociBuffer; cxoBuffer buffer; PyObject *value; void *ociValue; // validate parameters if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "IIO", keywordList, &attrNum, &attrType, &value)) return NULL; if (cxoCursor_isOpen(cursor) < 0) return NULL; // convert value to OCI requirement and then set it cxoBuffer_init(&buffer); if (cxoUtils_convertPythonValueToOciAttr(value, attrType, &buffer, &ociBuffer, &ociValue, &ociValueLength, cursor->connection->encodingInfo.encoding) < 0) return NULL; if (dpiStmt_setOciAttr(cursor->handle, attrNum, ociValue, ociValueLength) < 0) return cxoError_raiseAndReturnNull(); cxoBuffer_clear(&buffer); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_setOutputSize() // Does nothing as ODPI-C handles long columns dynamically without the need // to specify a maximum length. //----------------------------------------------------------------------------- static PyObject *cxoCursor_setOutputSize(cxoCursor *cursor, PyObject *args) { int outputSize, outputSizeColumn; if (!PyArg_ParseTuple(args, "i|i", &outputSize, &outputSizeColumn)) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoCursor_var() // Create a bind variable and return it. //----------------------------------------------------------------------------- static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "typ", "size", "arraysize", "inconverter", "outconverter", "typename", "encoding_errors", "bypass_decode", "encodingErrors", NULL }; Py_ssize_t encodingErrorsLength, encodingErrorsDeprecatedLength; const char *encodingErrors, *encodingErrorsDeprecated; PyObject *inConverter, *outConverter, *typeNameObj; int size, arraySize, bypassDecode; cxoTransformNum transformNum; cxoObjectType *objType; PyObject *type; cxoVar *var; // parse arguments size = bypassDecode = 0; arraySize = cursor->bindArraySize; encodingErrors = encodingErrorsDeprecated = NULL; inConverter = outConverter = typeNameObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|iiOOOz#pz#", keywordList, &type, &size, &arraySize, &inConverter, &outConverter, &typeNameObj, &encodingErrors, &encodingErrorsLength, &bypassDecode, &encodingErrorsDeprecated, &encodingErrorsDeprecatedLength)) return NULL; if (encodingErrorsDeprecated) { if (encodingErrors) { cxoError_raiseFromString(cxoProgrammingErrorException, "encoding_errors and encodingErrors cannot both be " "specified"); return NULL; } encodingErrors = encodingErrorsDeprecated; encodingErrorsLength = encodingErrorsDeprecatedLength; } // determine the type of variable if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0) return NULL; Py_XINCREF(objType); if (typeNameObj && typeNameObj != Py_None && !objType) { objType = cxoObjectType_newByName(cursor->connection, typeNameObj); if (!objType) return NULL; } // create the variable var = cxoVar_new(cursor, arraySize, transformNum, size, 0, objType); Py_XDECREF(objType); if (!var) return NULL; Py_XINCREF(inConverter); var->inConverter = inConverter; Py_XINCREF(outConverter); var->outConverter = outConverter; // assign encoding errors, if applicable if (encodingErrors) { var->encodingErrors = PyMem_Malloc(encodingErrorsLength + 1); if (!var->encodingErrors) { Py_DECREF(var); return NULL; } strcpy((char*) var->encodingErrors, encodingErrors); } // if the decode step is to be bypassed, use the binary transform instead if (bypassDecode) var->transformNum = CXO_TRANSFORM_BINARY; return (PyObject*) var; } //----------------------------------------------------------------------------- // cxoCursor_arrayVar() // Create an array bind variable and return it. //----------------------------------------------------------------------------- static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args) { cxoTransformNum transformNum; uint32_t size, numElements; PyObject *type, *value; cxoObjectType *objType; cxoVar *var; // parse arguments size = 0; if (!PyArg_ParseTuple(args, "OO|i", &type, &value, &size)) return NULL; // determine the transform to use for the variable if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0) return NULL; // determine the number of elements to create if (PyList_Check(value)) numElements = (uint32_t) PyList_GET_SIZE(value); else if (PyLong_Check(value)) { numElements = (uint32_t) PyLong_AsLong(value); if (PyErr_Occurred()) return NULL; } else { PyErr_SetString(PyExc_TypeError, "expecting integer or list of values"); return NULL; } // create the variable var = cxoVar_new(cursor, numElements, transformNum, size, 1, objType); if (!var) return NULL; // set the value, if applicable if (PyList_Check(value)) { if (cxoVar_setValue(var, 0, value) < 0) return NULL; } return (PyObject*) var; } //----------------------------------------------------------------------------- // cxoCursor_bindNames() // Return a list of bind variable names. //----------------------------------------------------------------------------- static PyObject *cxoCursor_bindNames(cxoCursor *cursor, PyObject *args) { uint32_t numBinds, *nameLengths, i; PyObject *namesList, *temp; const char **names; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // ensure that a statement has already been prepared if (!cursor->statement) return cxoError_raiseFromString(cxoProgrammingErrorException, "statement must be prepared first"); // determine the number of binds if (dpiStmt_getBindCount(cursor->handle, &numBinds) < 0) return cxoError_raiseAndReturnNull(); // if the number of binds is zero, nothing to do if (numBinds == 0) return PyList_New(0); // allocate memory for the bind names and their lengths names = (const char**) PyMem_Malloc(numBinds * sizeof(char*)); if (!names) return PyErr_NoMemory(); nameLengths = (uint32_t*) PyMem_Malloc(numBinds * sizeof(uint32_t)); if (!nameLengths) { PyMem_Free((void*) names); return PyErr_NoMemory(); } // get the bind names if (dpiStmt_getBindNames(cursor->handle, &numBinds, names, nameLengths) < 0) { PyMem_Free((void*) names); PyMem_Free(nameLengths); return cxoError_raiseAndReturnNull(); } // populate list with the results namesList = PyList_New(numBinds); if (namesList) { for (i = 0; i < numBinds; i++) { temp = PyUnicode_Decode(names[i], nameLengths[i], cursor->connection->encodingInfo.encoding, NULL); if (!temp) { Py_CLEAR(namesList); break; } PyList_SET_ITEM(namesList, i, temp); } } PyMem_Free((void*) names); PyMem_Free(nameLengths); return namesList; } //----------------------------------------------------------------------------- // cxoCursor_getIter() // Return a reference to the cursor which supports the iterator protocol. //----------------------------------------------------------------------------- static PyObject *cxoCursor_getIter(cxoCursor *cursor) { if (cxoCursor_verifyFetch(cursor) < 0) return NULL; Py_INCREF(cursor); return (PyObject*) cursor; } //----------------------------------------------------------------------------- // cxoCursor_getNext() // Return a reference to the cursor which supports the iterator protocol. //----------------------------------------------------------------------------- static PyObject *cxoCursor_getNext(cxoCursor *cursor) { uint32_t bufferRowIndex = 0; int found = 0; if (cxoCursor_verifyFetch(cursor) < 0) return NULL; if (cxoCursor_fetchRow(cursor, &found, &bufferRowIndex) < 0) return NULL; if (found) return cxoCursor_createRow(cursor, bufferRowIndex); // no more rows, return NULL without setting an exception return NULL; } //----------------------------------------------------------------------------- // cxoCursor_getBatchErrors() // Returns a list of batch error objects. //----------------------------------------------------------------------------- static PyObject* cxoCursor_getBatchErrors(cxoCursor *cursor) { uint32_t numErrors, i; dpiErrorInfo *errors; PyObject *result; cxoError *error; // determine the number of errors if (dpiStmt_getBatchErrorCount(cursor->handle, &numErrors) < 0) return cxoError_raiseAndReturnNull(); if (numErrors == 0) return PyList_New(0); // allocate memory for the errors errors = PyMem_Malloc(numErrors * sizeof(dpiErrorInfo)); if (!errors) return PyErr_NoMemory(); // get error information if (dpiStmt_getBatchErrors(cursor->handle, numErrors, errors) < 0) { PyMem_Free(errors); return cxoError_raiseAndReturnNull(); } // create result result = PyList_New(numErrors); if (result) { for (i = 0; i < numErrors; i++) { error = cxoError_newFromInfo(&errors[i]); if (!error) { Py_CLEAR(result); break; } PyList_SET_ITEM(result, i, (PyObject*) error); } } PyMem_Free(errors); return result; } //----------------------------------------------------------------------------- // cxoCursor_getArrayDMLRowCounts // Populates the array dml row count list. //----------------------------------------------------------------------------- static PyObject* cxoCursor_getArrayDMLRowCounts(cxoCursor *cursor) { PyObject *result, *element; uint32_t numRowCounts, i; uint64_t *rowCounts; // get row counts from DPI if (dpiStmt_getRowCounts(cursor->handle, &numRowCounts, &rowCounts) < 0) return cxoError_raiseAndReturnNull(); // return array result = PyList_New(numRowCounts); if (!result) return NULL; for (i = 0; i < numRowCounts; i++) { element = PyLong_FromUnsignedLong((unsigned long) rowCounts[i]); if (!element) { Py_DECREF(result); return NULL; } PyList_SET_ITEM(result, i, element); } return result; } //----------------------------------------------------------------------------- // cxoCursor_getImplicitResults // Return a list of cursors available implicitly after execution of a PL/SQL // block or stored procedure. If none are available, an empty list is returned. //----------------------------------------------------------------------------- static PyObject *cxoCursor_getImplicitResults(cxoCursor *cursor) { cxoCursor *childCursor; dpiStmt *childStmt; PyObject *result; // make sure the cursor is open if (cxoCursor_isOpen(cursor) < 0) return NULL; // make sure we have a statement executed (handle defined) if (!cursor->handle) return cxoError_raiseFromString(cxoInterfaceErrorException, "no statement executed"); // create result result = PyList_New(0); if (!result) return NULL; while (1) { if (dpiStmt_getImplicitResult(cursor->handle, &childStmt) < 0) return cxoError_raiseAndReturnNull(); if (!childStmt) break; childCursor = (cxoCursor*) PyObject_CallMethod( (PyObject*) cursor->connection, "cursor", NULL); if (!childCursor) { dpiStmt_release(childStmt); Py_DECREF(result); return NULL; } childCursor->handle = childStmt; childCursor->fixupRefCursor = 1; if (PyList_Append(result, (PyObject*) childCursor) < 0) { Py_DECREF(result); Py_DECREF(childCursor); return NULL; } Py_DECREF(childCursor); } return result; } //----------------------------------------------------------------------------- // cxoCursor_contextManagerEnter() // Called when the cursor is used as a context manager and simply returns it // to the caller. //----------------------------------------------------------------------------- static PyObject *cxoCursor_contextManagerEnter(cxoCursor *cursor, PyObject* args) { Py_INCREF(cursor); return (PyObject*) cursor; } //----------------------------------------------------------------------------- // cxoCursor_contextManagerExit() // Called when the cursor is used as a context manager and simply closes the // cursor. //----------------------------------------------------------------------------- static PyObject *cxoCursor_contextManagerExit(cxoCursor *cursor, PyObject* args) { PyObject *excType, *excValue, *excTraceback, *result; if (!PyArg_ParseTuple(args, "OOO", &excType, &excValue, &excTraceback)) return NULL; result = cxoCursor_close(cursor, NULL); if (!result) return NULL; Py_DECREF(result); Py_INCREF(Py_False); return Py_False; } //----------------------------------------------------------------------------- // cxoCursor_setPrefetchRows() // Set the number of rows that are prefetched by the Oracle Client library. //----------------------------------------------------------------------------- static int cxoCursor_setPrefetchRows(cxoCursor* cursor, PyObject *value, void* arg) { unsigned long numRows; if (cxoCursor_isOpen(cursor) < 0) return -1; numRows = PyLong_AsUnsignedLong(value); if (PyErr_Occurred()) return -1; cursor->prefetchRows = (uint32_t) numRows; if (cursor->handle && dpiStmt_setPrefetchRows(cursor->handle, cursor->prefetchRows) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // declaration of methods for Python type //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "execute", (PyCFunction) cxoCursor_execute, METH_VARARGS | METH_KEYWORDS }, { "fetchall", (PyCFunction) cxoCursor_fetchAll, METH_NOARGS }, { "fetchone", (PyCFunction) cxoCursor_fetchOne, METH_NOARGS }, { "fetchmany", (PyCFunction) cxoCursor_fetchMany, METH_VARARGS | METH_KEYWORDS }, { "fetchraw", (PyCFunction) cxoCursor_fetchRaw, METH_VARARGS | METH_KEYWORDS }, { "prepare", (PyCFunction) cxoCursor_prepare, METH_VARARGS }, { "parse", (PyCFunction) cxoCursor_parse, METH_O }, { "setinputsizes", (PyCFunction) cxoCursor_setInputSizes, METH_VARARGS | METH_KEYWORDS }, { "executemany", (PyCFunction) cxoCursor_executeMany, METH_VARARGS | METH_KEYWORDS }, { "callproc", (PyCFunction) cxoCursor_callProc, METH_VARARGS | METH_KEYWORDS }, { "callfunc", (PyCFunction) cxoCursor_callFunc, METH_VARARGS | METH_KEYWORDS }, { "executemanyprepared", (PyCFunction) cxoCursor_executeManyPrepared, METH_VARARGS }, { "setoutputsize", (PyCFunction) cxoCursor_setOutputSize, METH_VARARGS }, { "scroll", (PyCFunction) cxoCursor_scroll, METH_VARARGS | METH_KEYWORDS }, { "var", (PyCFunction) cxoCursor_var, METH_VARARGS | METH_KEYWORDS }, { "arrayvar", (PyCFunction) cxoCursor_arrayVar, METH_VARARGS }, { "bindnames", (PyCFunction) cxoCursor_bindNames, METH_NOARGS }, { "close", (PyCFunction) cxoCursor_close, METH_NOARGS }, { "getbatcherrors", (PyCFunction) cxoCursor_getBatchErrors, METH_NOARGS }, { "getarraydmlrowcounts", (PyCFunction) cxoCursor_getArrayDMLRowCounts, METH_NOARGS }, { "getimplicitresults", (PyCFunction) cxoCursor_getImplicitResults, METH_NOARGS }, { "__enter__", (PyCFunction) cxoCursor_contextManagerEnter, METH_NOARGS }, { "__exit__", (PyCFunction) cxoCursor_contextManagerExit, METH_VARARGS }, { "_get_oci_attr", (PyCFunction) cxoCursor_getOciAttr, METH_VARARGS | METH_KEYWORDS }, { "_set_oci_attr", (PyCFunction) cxoCursor_setOciAttr, METH_VARARGS | METH_KEYWORDS }, { NULL, NULL } }; //----------------------------------------------------------------------------- // declaration of members for Python type //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "arraysize", T_UINT, offsetof(cxoCursor, arraySize), 0 }, { "bindarraysize", T_UINT, offsetof(cxoCursor, bindArraySize), 0 }, { "rowcount", T_ULONGLONG, offsetof(cxoCursor, rowCount), READONLY }, { "statement", T_OBJECT, offsetof(cxoCursor, statement), READONLY }, { "connection", T_OBJECT_EX, offsetof(cxoCursor, connection), READONLY }, { "rowfactory", T_OBJECT, offsetof(cxoCursor, rowFactory), 0 }, { "bindvars", T_OBJECT, offsetof(cxoCursor, bindVariables), READONLY }, { "fetchvars", T_OBJECT, offsetof(cxoCursor, fetchVariables), READONLY }, { "inputtypehandler", T_OBJECT, offsetof(cxoCursor, inputTypeHandler), 0 }, { "outputtypehandler", T_OBJECT, offsetof(cxoCursor, outputTypeHandler), 0 }, { "scrollable", T_BOOL, offsetof(cxoCursor, isScrollable), 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members for Python type //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "description", (getter) cxoCursor_getDescription, 0, 0, 0 }, { "lastrowid", (getter) cxoCursor_getLastRowid, 0, 0, 0 }, { "prefetchrows", (getter) cxoCursor_getPrefetchRows, (setter) cxoCursor_setPrefetchRows, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeCursor = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.Cursor", .tp_basicsize = sizeof(cxoCursor), .tp_dealloc = (destructor) cxoCursor_free, .tp_repr = (reprfunc) cxoCursor_repr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_iter = (getiterfunc) cxoCursor_getIter, .tp_iternext = (iternextfunc) cxoCursor_getNext, .tp_methods = cxoMethods, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers, .tp_init = (initproc) cxoCursor_init, .tp_new = cxoCursor_new }; python-cx_Oracle-8.3.0/src/cxoDbType.c000066400000000000000000000234451414105416400176040ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoDbType.c // Defines the objects used for identifying all types used by the database. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoDbType_free() // Free the database type object. //----------------------------------------------------------------------------- static void cxoDbType_free(cxoDbType *dbType) { Py_TYPE(dbType)->tp_free((PyObject*) dbType); } //----------------------------------------------------------------------------- // cxoDbType_repr() // Return a string representation of a queue. //----------------------------------------------------------------------------- static PyObject *cxoDbType_repr(cxoDbType *dbType) { PyObject *module, *name, *dbTypeName, *result; dbTypeName = PyUnicode_DecodeASCII(dbType->name, strlen(dbType->name), NULL); if (!dbTypeName) return NULL; if (cxoUtils_getModuleAndName(Py_TYPE(dbType), &module, &name) < 0) { Py_DECREF(dbTypeName); return NULL; } result = cxoUtils_formatString("<%s.%s %s>", PyTuple_Pack(3, module, name, dbTypeName)); Py_DECREF(module); Py_DECREF(name); Py_DECREF(dbTypeName); return result; } //----------------------------------------------------------------------------- // cxoDbType_richCompare() // Peforms a comparison between the database type and another Python object. // Equality (and inequality) are used to match database API types with their // associated database types. //----------------------------------------------------------------------------- static PyObject *cxoDbType_richCompare(cxoDbType* dbType, PyObject* obj, int op) { cxoApiType *apiType; int status, equal; // only equality and inequality can be checked if (op != Py_EQ && op != Py_NE) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } // check for exact object equal = 0; if (obj == (PyObject*) dbType) { equal = 1; // check for API type } else { status = PyObject_IsInstance(obj, (PyObject*) &cxoPyTypeApiType); if (status < 0) return NULL; if (status == 1) { apiType = (cxoApiType*) obj; status = PySequence_Contains(apiType->dbTypes, (PyObject*) dbType); if (status < 0) return NULL; equal = (status == 1) ? 1 : 0; } } // determine return value if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } //----------------------------------------------------------------------------- // cxoDbType_hash() // Return a hash value for the instance. //----------------------------------------------------------------------------- static Py_hash_t cxoDbType_hash(cxoDbType *dbType) { return (Py_hash_t) dbType->num; } //----------------------------------------------------------------------------- // cxoDbType_fromDataTypeInfo() // Return the database type given the data type info available from ODPI-C. //----------------------------------------------------------------------------- cxoDbType *cxoDbType_fromDataTypeInfo(dpiDataTypeInfo *info) { char message[120]; switch (info->oracleTypeNum) { case DPI_ORACLE_TYPE_VARCHAR: return cxoDbTypeVarchar; case DPI_ORACLE_TYPE_NVARCHAR: return cxoDbTypeNvarchar; case DPI_ORACLE_TYPE_CHAR: return cxoDbTypeChar; case DPI_ORACLE_TYPE_NCHAR: return cxoDbTypeNchar; case DPI_ORACLE_TYPE_ROWID: return cxoDbTypeRowid; case DPI_ORACLE_TYPE_RAW: return cxoDbTypeRaw; case DPI_ORACLE_TYPE_NATIVE_DOUBLE: return cxoDbTypeBinaryDouble; case DPI_ORACLE_TYPE_NATIVE_FLOAT: return cxoDbTypeBinaryFloat; case DPI_ORACLE_TYPE_NATIVE_INT: return cxoDbTypeBinaryInteger; case DPI_ORACLE_TYPE_NUMBER: return cxoDbTypeNumber; case DPI_ORACLE_TYPE_DATE: return cxoDbTypeDate; case DPI_ORACLE_TYPE_TIMESTAMP: return cxoDbTypeTimestamp; case DPI_ORACLE_TYPE_TIMESTAMP_TZ: return cxoDbTypeTimestampTZ; case DPI_ORACLE_TYPE_TIMESTAMP_LTZ: return cxoDbTypeTimestampLTZ; case DPI_ORACLE_TYPE_INTERVAL_DS: return cxoDbTypeIntervalDS; case DPI_ORACLE_TYPE_INTERVAL_YM: return cxoDbTypeIntervalYM; case DPI_ORACLE_TYPE_CLOB: return cxoDbTypeClob; case DPI_ORACLE_TYPE_NCLOB: return cxoDbTypeNclob; case DPI_ORACLE_TYPE_BLOB: return cxoDbTypeBlob; case DPI_ORACLE_TYPE_BFILE: return cxoDbTypeBfile; case DPI_ORACLE_TYPE_STMT: return cxoDbTypeCursor; case DPI_ORACLE_TYPE_OBJECT: return cxoDbTypeObject; case DPI_ORACLE_TYPE_LONG_VARCHAR: return cxoDbTypeLong; case DPI_ORACLE_TYPE_LONG_RAW: return cxoDbTypeLongRaw; case DPI_ORACLE_TYPE_BOOLEAN: return cxoDbTypeBoolean; default: break; } snprintf(message, sizeof(message), "Oracle type %d not supported.", info->oracleTypeNum); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return NULL; } //----------------------------------------------------------------------------- // cxoDbType_fromTransformNum() // Return the database type given the transformation number. //----------------------------------------------------------------------------- cxoDbType *cxoDbType_fromTransformNum(cxoTransformNum transformNum) { char message[120]; switch (transformNum) { case CXO_TRANSFORM_BINARY: return cxoDbTypeRaw; case CXO_TRANSFORM_BFILE: return cxoDbTypeBfile; case CXO_TRANSFORM_BLOB: return cxoDbTypeBlob; case CXO_TRANSFORM_BOOLEAN: return cxoDbTypeBoolean; case CXO_TRANSFORM_CLOB: return cxoDbTypeClob; case CXO_TRANSFORM_CURSOR: return cxoDbTypeCursor; case CXO_TRANSFORM_DATE: case CXO_TRANSFORM_DATETIME: return cxoDbTypeDate; case CXO_TRANSFORM_DECIMAL: case CXO_TRANSFORM_FLOAT: case CXO_TRANSFORM_INT: return cxoDbTypeNumber; case CXO_TRANSFORM_FIXED_CHAR: return cxoDbTypeChar; case CXO_TRANSFORM_FIXED_NCHAR: return cxoDbTypeNchar; case CXO_TRANSFORM_LONG_BINARY: return cxoDbTypeLongRaw; case CXO_TRANSFORM_LONG_STRING: return cxoDbTypeLong; case CXO_TRANSFORM_NATIVE_DOUBLE: return cxoDbTypeBinaryDouble; case CXO_TRANSFORM_NATIVE_FLOAT: return cxoDbTypeBinaryFloat; case CXO_TRANSFORM_NATIVE_INT: return cxoDbTypeBinaryInteger; case CXO_TRANSFORM_NCLOB: return cxoDbTypeNclob; case CXO_TRANSFORM_NSTRING: return cxoDbTypeNvarchar; case CXO_TRANSFORM_OBJECT: return cxoDbTypeObject; case CXO_TRANSFORM_ROWID: return cxoDbTypeRowid; case CXO_TRANSFORM_NONE: case CXO_TRANSFORM_STRING: return cxoDbTypeVarchar; case CXO_TRANSFORM_TIMEDELTA: return cxoDbTypeIntervalDS; case CXO_TRANSFORM_TIMESTAMP: return cxoDbTypeTimestamp; case CXO_TRANSFORM_TIMESTAMP_LTZ: return cxoDbTypeTimestampLTZ; case CXO_TRANSFORM_TIMESTAMP_TZ: return cxoDbTypeTimestampTZ; case CXO_TRANSFORM_JSON: return cxoDbTypeJson; default: break; } snprintf(message, sizeof(message), "transform %d not supported.", transformNum); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return NULL; } //----------------------------------------------------------------------------- // cxoDBType_reduce() // Method provided for pickling/unpickling of DB types. //----------------------------------------------------------------------------- static PyObject *cxoDBType_reduce(cxoDbType *dbType) { return PyUnicode_DecodeASCII(dbType->name, strlen(dbType->name), NULL); } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "__reduce__", (PyCFunction) cxoDBType_reduce, METH_NOARGS }, { NULL, NULL} }; //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "name", T_STRING, offsetof(cxoDbType, name), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // Python type declaration //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeDbType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.DbType", .tp_basicsize = sizeof(cxoDbType), .tp_dealloc = (destructor) cxoDbType_free, .tp_repr = (reprfunc) cxoDbType_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMembers, .tp_methods = cxoMethods, .tp_richcompare = (richcmpfunc) cxoDbType_richCompare, .tp_hash = (hashfunc) cxoDbType_hash }; python-cx_Oracle-8.3.0/src/cxoDeqOptions.c000066400000000000000000000346321414105416400205020ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoDeqOptions.c // Implements the dequeue options objects used in Advanced Queuing. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoDeqOptions_new() // Create a new dequeue options object. //----------------------------------------------------------------------------- cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection, dpiDeqOptions *handle) { cxoDeqOptions *options; int status; options = (cxoDeqOptions*) cxoPyTypeDeqOptions.tp_alloc(&cxoPyTypeDeqOptions, 0); if (!options) return NULL; if (handle) { status = dpiDeqOptions_addRef(handle); } else { status = dpiConn_newDeqOptions(connection->handle, &handle); } if (status < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(options); return NULL; } options->handle = handle; options->encoding = connection->encodingInfo.encoding; return options; } //----------------------------------------------------------------------------- // cxoDeqOptions_free() // Free the memory associated with the dequeue options object. //----------------------------------------------------------------------------- static void cxoDeqOptions_free(cxoDeqOptions *options) { if (options->handle) { dpiDeqOptions_release(options->handle); options->handle = NULL; } Py_TYPE(options)->tp_free((PyObject*) options); } //----------------------------------------------------------------------------- // cxoDeqOptions_getAttrText() // Get the value of the attribute as text. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getAttrText(cxoDeqOptions *options, int (*func)(dpiDeqOptions*, const char**, uint32_t*)) { uint32_t valueLength; const char *value; if ((*func)(options->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (!value) Py_RETURN_NONE; return PyUnicode_Decode(value, valueLength, options->encoding, NULL); } //----------------------------------------------------------------------------- // cxoDeqOptions_setAttrText() // Set the value of the attribute as text. //----------------------------------------------------------------------------- static int cxoDeqOptions_setAttrText(cxoDeqOptions *options, PyObject *value, int (*func)(dpiDeqOptions*, const char*, uint32_t)) { cxoBuffer buffer; int status; if (cxoBuffer_fromObject(&buffer, value, options->encoding)) return -1; status = (*func)(options->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoDeqOptions_getCondition() // Get the value of the condition option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getCondition(cxoDeqOptions *options, void *unused) { return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getCondition); } //----------------------------------------------------------------------------- // cxoDeqOptions_getConsumerName() // Get the value of the consumer name option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getConsumerName(cxoDeqOptions *options, void *unused) { return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getConsumerName); } //----------------------------------------------------------------------------- // cxoDeqOptions_getCorrelation() // Get the value of the correlation option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getCorrelation(cxoDeqOptions *options, void *unused) { return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getCorrelation); } //----------------------------------------------------------------------------- // cxoDeqOptions_getMode() // Get the value of the mode option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getMode(cxoDeqOptions *options, void *unused) { dpiDeqMode value; if (dpiDeqOptions_getMode(options->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoDeqOptions_getMsgId() // Get the value of the message id option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getMsgId(cxoDeqOptions *options, void *unused) { uint32_t valueLength; const char *value; if (dpiDeqOptions_getMsgId(options->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (!value) Py_RETURN_NONE; return PyBytes_FromStringAndSize(value, valueLength); } //----------------------------------------------------------------------------- // cxoDeqOptions_getNavigation() // Get the value of the navigation option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getNavigation(cxoDeqOptions *options, void *unused) { dpiDeqNavigation value; if (dpiDeqOptions_getNavigation(options->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoDeqOptions_getTransformation() // Get the value of the transformation option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getTransformation(cxoDeqOptions *options, void *unused) { return cxoDeqOptions_getAttrText(options, dpiDeqOptions_getTransformation); } //----------------------------------------------------------------------------- // cxoDeqOptions_getVisibility() // Get the value of the visibility option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getVisibility(cxoDeqOptions *options, void *unused) { dpiVisibility value; if (dpiDeqOptions_getVisibility(options->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoDeqOptions_getWait() // Get the value of the wait option. //----------------------------------------------------------------------------- static PyObject *cxoDeqOptions_getWait(cxoDeqOptions *options, void *unused) { uint32_t value; if (dpiDeqOptions_getWait(options->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoDeqOptions_setCondition() // Set the value of the condition option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setCondition(cxoDeqOptions *options, PyObject *valueObj, void *unused) { return cxoDeqOptions_setAttrText(options, valueObj, dpiDeqOptions_setCondition); } //----------------------------------------------------------------------------- // cxoDeqOptions_setConsumerName() // Set the value of the consumer name option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setConsumerName(cxoDeqOptions *options, PyObject *valueObj, void *unused) { return cxoDeqOptions_setAttrText(options, valueObj, dpiDeqOptions_setConsumerName); } //----------------------------------------------------------------------------- // cxoDeqOptions_setCorrelation() // Set the value of the correlation option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setCorrelation(cxoDeqOptions *options, PyObject *valueObj, void *unused) { return cxoDeqOptions_setAttrText(options, valueObj, dpiDeqOptions_setCorrelation); } //----------------------------------------------------------------------------- // cxoDeqOptions_setDeliveryMode() // Set the value of the delivery mode option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setDeliveryMode(cxoDeqOptions *options, PyObject *valueObj, void *unused) { dpiMessageDeliveryMode value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if (dpiDeqOptions_setDeliveryMode(options->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoDeqOptions_setMode() // Set the value of the mode option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setMode(cxoDeqOptions *options, PyObject *valueObj, void *unused) { dpiDeqMode value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if (dpiDeqOptions_setMode(options->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoDeqOptions_setMsgId() // Set the value of the message id option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setMsgId(cxoDeqOptions *options, PyObject *valueObj, void *unused) { Py_ssize_t valueLength; char *value; if (PyBytes_AsStringAndSize(valueObj, &value, &valueLength) < 0) return -1; if (dpiDeqOptions_setMsgId(options->handle, value, (uint32_t) valueLength) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoDeqOptions_setNavigation() // Set the value of the navigation option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setNavigation(cxoDeqOptions *options, PyObject *valueObj, void *unused) { dpiDeqNavigation value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if (dpiDeqOptions_setNavigation(options->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoDeqOptions_setTransformation() // Set the value of the correlation option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setTransformation(cxoDeqOptions *options, PyObject *valueObj, void *unused) { return cxoDeqOptions_setAttrText(options, valueObj, dpiDeqOptions_setTransformation); } //----------------------------------------------------------------------------- // cxoDeqOptions_setVisibility() // Set the value of the visibility option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setVisibility(cxoDeqOptions *options, PyObject *valueObj, void *unused) { dpiVisibility value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if (dpiDeqOptions_setVisibility(options->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoDeqOptions_setWait() // Set the value of the wait option. //----------------------------------------------------------------------------- static int cxoDeqOptions_setWait(cxoDeqOptions *options, PyObject *valueObj, void *unused) { uint32_t value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if (dpiDeqOptions_setWait(options->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // declaration of calculated members for Python type //----------------------------------------------------------------------------- static PyGetSetDef cxoDeqOptionsCalcMembers[] = { { "condition", (getter) cxoDeqOptions_getCondition, (setter) cxoDeqOptions_setCondition, 0, 0 }, { "consumername", (getter) cxoDeqOptions_getConsumerName, (setter) cxoDeqOptions_setConsumerName, 0, 0 }, { "correlation", (getter) cxoDeqOptions_getCorrelation, (setter) cxoDeqOptions_setCorrelation, 0, 0 }, { "deliverymode", 0, (setter) cxoDeqOptions_setDeliveryMode, 0, 0 }, { "mode", (getter) cxoDeqOptions_getMode, (setter) cxoDeqOptions_setMode, 0, 0 }, { "msgid", (getter) cxoDeqOptions_getMsgId, (setter) cxoDeqOptions_setMsgId, 0, 0 }, { "navigation", (getter) cxoDeqOptions_getNavigation, (setter) cxoDeqOptions_setNavigation, 0, 0 }, { "transformation", (getter) cxoDeqOptions_getTransformation, (setter) cxoDeqOptions_setTransformation, 0, 0 }, { "visibility", (getter) cxoDeqOptions_getVisibility, (setter) cxoDeqOptions_setVisibility, 0, 0 }, { "wait", (getter) cxoDeqOptions_getWait, (setter) cxoDeqOptions_setWait, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeDeqOptions = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.DeqOptions", .tp_basicsize = sizeof(cxoDeqOptions), .tp_dealloc = (destructor) cxoDeqOptions_free, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_getset = cxoDeqOptionsCalcMembers }; python-cx_Oracle-8.3.0/src/cxoEnqOptions.c000066400000000000000000000137431414105416400205140ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoEnqOptions.c // Implements the enqueue options objects used in Advanced Queuing. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoEnqOptions_new() // Create a new enqueue options object. //----------------------------------------------------------------------------- cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection, dpiEnqOptions *handle) { cxoEnqOptions *options; int status; options = (cxoEnqOptions*) cxoPyTypeEnqOptions.tp_alloc(&cxoPyTypeEnqOptions, 0); if (!options) return NULL; if (handle) { status = dpiEnqOptions_addRef(handle); } else { status = dpiConn_newEnqOptions(connection->handle, &handle); } if (status < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(options); return NULL; } options->handle = handle; options->encoding = connection->encodingInfo.encoding; return options; } //----------------------------------------------------------------------------- // cxoEnqOptions_free() // Free the memory associated with the enqueue options object. //----------------------------------------------------------------------------- static void cxoEnqOptions_free(cxoEnqOptions *self) { if (self->handle) { dpiEnqOptions_release(self->handle); self->handle = NULL; } Py_TYPE(self)->tp_free((PyObject*) self); } //----------------------------------------------------------------------------- // cxoEnqOptions_getTransformation() // Get the value of the transformation option. //----------------------------------------------------------------------------- static PyObject *cxoEnqOptions_getTransformation(cxoEnqOptions *self, void *unused) { uint32_t valueLength; const char *value; if (dpiEnqOptions_getTransformation(self->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (!value) Py_RETURN_NONE; return PyUnicode_Decode(value, valueLength, self->encoding, NULL); } //----------------------------------------------------------------------------- // cxoEnqOptions_getVisibility() // Get the value of the visibility option. //----------------------------------------------------------------------------- static PyObject *cxoEnqOptions_getVisibility(cxoEnqOptions *self, void *unused) { dpiVisibility value; if (dpiEnqOptions_getVisibility(self->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoEnqOptions_setDeliveryMode() // Set the value of the delivery mode option. //----------------------------------------------------------------------------- static int cxoEnqOptions_setDeliveryMode(cxoEnqOptions *self, PyObject *valueObj, void *unused) { dpiMessageDeliveryMode value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if (dpiEnqOptions_setDeliveryMode(self->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoEnqOptions_setTransformation() // Set the value of the transformation option. //----------------------------------------------------------------------------- static int cxoEnqOptions_setTransformation(cxoEnqOptions *self, PyObject *valueObj, void *unused) { cxoBuffer buffer; int status; if (cxoBuffer_fromObject(&buffer, valueObj, self->encoding) < 0) return -1; status = dpiEnqOptions_setTransformation(self->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoEnqOptions_setVisibility() // Set the value of the visibility option. //----------------------------------------------------------------------------- static int cxoEnqOptions_setVisibility(cxoEnqOptions *self, PyObject *valueObj, void *unused) { dpiVisibility value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if (dpiEnqOptions_setVisibility(self->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // declaration of calculated members for Python type //----------------------------------------------------------------------------- static PyGetSetDef cxoEnqOptionsCalcMembers[] = { { "deliverymode", 0, (setter) cxoEnqOptions_setDeliveryMode, 0, 0 }, { "transformation", (getter) cxoEnqOptions_getTransformation, (setter) cxoEnqOptions_setTransformation, 0, 0 }, { "visibility", (getter) cxoEnqOptions_getVisibility, (setter) cxoEnqOptions_setVisibility, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeEnqOptions = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.EnqOptions", .tp_basicsize = sizeof(cxoEnqOptions), .tp_dealloc = (destructor) cxoEnqOptions_free, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_getset = cxoEnqOptionsCalcMembers }; python-cx_Oracle-8.3.0/src/cxoError.c000066400000000000000000000242011414105416400174750ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoError.c // Error handling. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoError_free() // Deallocate the error. //----------------------------------------------------------------------------- static void cxoError_free(cxoError *error) { Py_CLEAR(error->message); Py_CLEAR(error->context); PyObject_Del(error); } //----------------------------------------------------------------------------- // cxoError_new() // Create a new error object. This is intended to only be used by the // unpickling routine, and not by direct creation! //----------------------------------------------------------------------------- static PyObject *cxoError_new(PyTypeObject *type, PyObject *args, PyObject *keywordArgs) { PyObject *message, *context; int isRecoverable, code; cxoError *error; unsigned offset; isRecoverable = 0; if (!PyArg_ParseTuple(args, "OiIO|i", &message, &code, &offset, &context, &isRecoverable)) return NULL; error = (cxoError*) type->tp_alloc(type, 0); if (!error) return NULL; error->code = code; error->offset = offset; error->isRecoverable = (char) isRecoverable; Py_INCREF(message); error->message = message; Py_INCREF(context); error->context = context; return (PyObject*) error; } //----------------------------------------------------------------------------- // cxoError_newFromInfo() // Internal method for creating an error object from the DPI error // information. //----------------------------------------------------------------------------- cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo) { cxoError *error; // create error object and initialize it error = (cxoError*) cxoPyTypeError.tp_alloc(&cxoPyTypeError, 0); if (!error) return NULL; error->code = errorInfo->code; error->offset = errorInfo->offset; error->isRecoverable = (char) errorInfo->isRecoverable; // create message error->message = PyUnicode_Decode(errorInfo->message, errorInfo->messageLength, errorInfo->encoding, NULL); if (!error->message) { Py_DECREF(error); return NULL; } // create context composed of function name and action error->context = PyUnicode_FromFormat("%s: %s", errorInfo->fnName, errorInfo->action); if (!error->context) { Py_DECREF(error); return NULL; } return error; } //----------------------------------------------------------------------------- // cxoError_newFromString() // Internal method for creating an error object from the DPI error // information. //----------------------------------------------------------------------------- static cxoError *cxoError_newFromString(const char *message) { cxoError *error; error = (cxoError*) cxoPyTypeError.tp_alloc(&cxoPyTypeError, 0); if (!error) return NULL; Py_INCREF(Py_None); error->context = Py_None; error->message = PyUnicode_DecodeASCII(message, strlen(message), NULL); if (!error->message) { Py_DECREF(error); return NULL; } return error; } //----------------------------------------------------------------------------- // cxoError_raiseAndReturnInt() // Internal method for raising an exception from an error generated from DPI. // Return -1 as a convenience to the caller. //----------------------------------------------------------------------------- int cxoError_raiseAndReturnInt(void) { dpiErrorInfo errorInfo; dpiContext_getError(cxoDpiContext, &errorInfo); return cxoError_raiseFromInfo(&errorInfo); } //----------------------------------------------------------------------------- // cxoError_raiseAndReturnNull() // Internal method for raising an exception from an error generated from DPI. // Return NULL as a convenience to the caller. //----------------------------------------------------------------------------- PyObject *cxoError_raiseAndReturnNull(void) { cxoError_raiseAndReturnInt(); return NULL; } //----------------------------------------------------------------------------- // cxoError_raiseFromInfo() // Internal method for raising an exception given an error information // structure from DPI. Return -1 as a convenience to the caller. //----------------------------------------------------------------------------- int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo) { PyObject *exceptionType; cxoError *error; error = cxoError_newFromInfo(errorInfo); if (!error) return -1; switch (errorInfo->code) { case 1: // unique constraint violated case 1400: // cannot insert NULL case 2290: // check constraint violated case 2291: // integrity constraint violated - parent key not found case 2292: // integrity constraint violated - child record found case 40479: // internal JSON serializer error exceptionType = cxoIntegrityErrorException; break; case 22: // invalid session ID; access denied case 378: // buffer pools cannot be created as specified case 600: // internal error code case 602: // internal programming exception case 603: // ORACLE server session terminated by fatal error case 604: // error occurred at recursive SQL level case 609: // could not attach to incoming connection case 1012: // not logged on case 1013: // user requested cancel of current operation case 1033: // ORACLE initialization or shutdown in progress case 1034: // ORACLE not available case 1041: // internal error. hostdef extension doesn't exist case 1043: // user side memory corruption case 1089: // immediate shutdown or close in progress case 1090: // shutdown in progress - connection is not permitted case 1092: // ORACLE instance terminated. Disconnection forced case 3113: // end-of-file on communication channel case 3114: // not connected to ORACLE case 3122: // attempt to close ORACLE-side window on user side case 3135: // connection lost contact case 12153: // TNS:not connected case 12203: // TNS:unable to connect to destination case 12500: // TNS:listener failed to start a dedicated server process case 12571: // TNS:packet writer failure case 27146: // post/wait initialization failed case 28511: // lost RPC connection to heterogeneous remote agent exceptionType = cxoOperationalErrorException; break; default: exceptionType = cxoDatabaseErrorException; break; } PyErr_SetObject(exceptionType, (PyObject*) error); Py_DECREF(error); return -1; } //----------------------------------------------------------------------------- // cxoError_raiseFromString() // Internal method for raising an exception given an error information // structure from DPI. Return -1 as a convenience to the caller. //----------------------------------------------------------------------------- PyObject *cxoError_raiseFromString(PyObject *exceptionType, const char *message) { cxoError *error; error = cxoError_newFromString(message); if (!error) return NULL; PyErr_SetObject(exceptionType, (PyObject*) error); Py_DECREF(error); return NULL; } //----------------------------------------------------------------------------- // cxoError_reduce() // Method provided for pickling/unpickling of Error objects. //----------------------------------------------------------------------------- static PyObject *cxoError_reduce(cxoError *error) { return Py_BuildValue("(O(OiIO))", Py_TYPE(error), error->message, error->code, error->offset, error->context); } //----------------------------------------------------------------------------- // cxoError_str() // Return a string representation of the error variable. //----------------------------------------------------------------------------- static PyObject *cxoError_str(cxoError *error) { Py_INCREF(error->message); return error->message; } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoErrorMethods[] = { { "__reduce__", (PyCFunction) cxoError_reduce, METH_NOARGS }, { NULL, NULL } }; //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoErrorMembers[] = { { "code", T_LONG, offsetof(cxoError, code), READONLY }, { "offset", T_UINT, offsetof(cxoError, offset), READONLY }, { "message", T_OBJECT, offsetof(cxoError, message), READONLY }, { "context", T_OBJECT, offsetof(cxoError, context), READONLY }, { "isrecoverable", T_BOOL, offsetof(cxoError, isRecoverable), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeError = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle._Error", .tp_basicsize = sizeof(cxoError), .tp_dealloc = (destructor) cxoError_free, .tp_str = (reprfunc) cxoError_str, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoErrorMethods, .tp_members = cxoErrorMembers, .tp_new = cxoError_new }; python-cx_Oracle-8.3.0/src/cxoFuture.c000066400000000000000000000041011414105416400176530ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoFuture.c // Defines the object used for managing behavior changes. This object permits // setting any attribute to any value but only tracks certain values. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoFuture_free() // Free the future object and reset global. //----------------------------------------------------------------------------- static void cxoFuture_free(cxoFuture *obj) { Py_TYPE(obj)->tp_free((PyObject*) obj); cxoFutureObj = NULL; } //----------------------------------------------------------------------------- // cxoFuture_getAttr() // Retrieve an attribute on an object. //----------------------------------------------------------------------------- static PyObject *cxoFuture_getAttr(cxoFuture *obj, PyObject *nameObject) { Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoFuture_setAttr() // Set an attribute on an object. //----------------------------------------------------------------------------- static int cxoFuture_setAttr(cxoFuture *obj, PyObject *nameObject, PyObject *value) { return 0; } //----------------------------------------------------------------------------- // Python type declaration //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeFuture = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.__future__", .tp_basicsize = sizeof(cxoFuture), .tp_dealloc = (destructor) cxoFuture_free, .tp_getattro = (getattrofunc) cxoFuture_getAttr, .tp_setattro = (setattrofunc) cxoFuture_setAttr, .tp_flags = Py_TPFLAGS_DEFAULT }; python-cx_Oracle-8.3.0/src/cxoJsonBuffer.c000066400000000000000000000224401414105416400204520ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoJsonBuffer.c // Defines buffer structure and routines for populating JSON values. These // are used to translate Python objects (scalars, dictionaries and lists) into // JSON values stored in the database. //----------------------------------------------------------------------------- #include "cxoModule.h" #define CXO_JSON_ENCODING "UTF-8" //----------------------------------------------------------------------------- // cxoJsonBuffer_getBuffer() // Acquire a new buffer from the array of buffers. If one is not available, // more space is allocated in chunks. //----------------------------------------------------------------------------- static int cxoJsonBuffer_getBuffer(cxoJsonBuffer *buf, cxoBuffer **buffer) { cxoBuffer *tempBuffers; if (buf->numBuffers == buf->allocatedBuffers) { buf->allocatedBuffers += 16; tempBuffers = PyMem_Realloc(buf->buffers, buf->allocatedBuffers * sizeof(cxoBuffer)); if (!tempBuffers) { PyErr_NoMemory(); return -1; } buf->buffers = tempBuffers; } *buffer = &buf->buffers[buf->numBuffers++]; return 0; } //----------------------------------------------------------------------------- // cxoJsonBuffer_populateNode() // Populate a particular node with the value of the Python object. //----------------------------------------------------------------------------- static int cxoJsonBuffer_populateNode(cxoJsonBuffer *buf, dpiJsonNode *node, PyObject *value) { cxoTransformNum transformNum; PyObject *childValue, *key; cxoBuffer *tempBuffer; Py_ssize_t pos, size; dpiJsonArray *array; dpiJsonObject *obj; char message[250]; uint32_t i; // handle NULL values if (value == Py_None) { node->oracleTypeNum = DPI_ORACLE_TYPE_NONE; node->nativeTypeNum = DPI_NATIVE_TYPE_NULL; return 0; } // handle arrays if (PyList_Check(value)) { // initialize array node->oracleTypeNum = DPI_ORACLE_TYPE_JSON_ARRAY; node->nativeTypeNum = DPI_NATIVE_TYPE_JSON_ARRAY; array = &node->value->asJsonArray; array->numElements = (uint32_t) PyList_GET_SIZE(value); array->elements = PyMem_Calloc(array->numElements, sizeof(dpiJsonNode)); array->elementValues = PyMem_Calloc(array->numElements, sizeof(dpiDataBuffer)); if (!array->elements || !array->elementValues) { PyErr_NoMemory(); return -1; } // process each element of the array for (i = 0; i < array->numElements; i++) { childValue = PyList_GET_ITEM(value, i); array->elements[i].value = &array->elementValues[i]; if (cxoJsonBuffer_populateNode(buf, &array->elements[i], childValue) < 0) return -1; } return 0; } // handle dictionaries if (PyDict_Check(value)) { // initialize object node->oracleTypeNum = DPI_ORACLE_TYPE_JSON_OBJECT; node->nativeTypeNum = DPI_NATIVE_TYPE_JSON_OBJECT; obj = &node->value->asJsonObject; size = PyDict_Size(value); if (size < 0) return -1; obj->numFields = (uint32_t) size; obj->fieldNames = PyMem_Calloc(obj->numFields, sizeof(char*)); obj->fieldNameLengths = PyMem_Calloc(obj->numFields, sizeof(uint32_t)); obj->fields = PyMem_Calloc(obj->numFields, sizeof(dpiJsonNode)); obj->fieldValues = PyMem_Calloc(obj->numFields, sizeof(dpiDataBuffer)); if (!obj->fieldNames || !obj->fieldNameLengths || !obj->fields || !obj->fieldValues) { PyErr_NoMemory(); return -1; } // process each entry in the dictionary i = 0; pos = 0; while (PyDict_Next(value, &pos, &key, &childValue)) { if (cxoJsonBuffer_getBuffer(buf, &tempBuffer) < 0) return -1; if (cxoBuffer_fromObject(tempBuffer, key, CXO_JSON_ENCODING) < 0) return -1; obj->fields[i].value = &obj->fieldValues[i]; obj->fieldNames[i] = (char*) tempBuffer->ptr; obj->fieldNameLengths[i] = tempBuffer->size; if (cxoJsonBuffer_populateNode(buf, &obj->fields[i], childValue) < 0) return -1; i++; } return 0; } // handle scalar values tempBuffer = NULL; transformNum = cxoTransform_getNumFromPythonValue(value, 1); switch (transformNum) { // strings and bytes must have a buffer made available for them to // store a reference to the object and the actual pointer and length; // numbers are converted to a string in order to prevent precision loss case CXO_TRANSFORM_STRING: case CXO_TRANSFORM_BINARY: case CXO_TRANSFORM_INT: case CXO_TRANSFORM_FLOAT: case CXO_TRANSFORM_DECIMAL: if (cxoJsonBuffer_getBuffer(buf, &tempBuffer) < 0) return -1; break; // swap CXO_TRANSFORM_DATETIME to CXO_TRANSFORM_TIMESTAMP to preserve // fractional seconds case CXO_TRANSFORM_DATETIME: transformNum = CXO_TRANSFORM_TIMESTAMP; break; // all other types do not need any special processing case CXO_TRANSFORM_BOOLEAN: case CXO_TRANSFORM_DATE: case CXO_TRANSFORM_TIMEDELTA: break; // any other type is not currently supported default: snprintf(message, sizeof(message), "Python type %s not supported.", Py_TYPE(value)->tp_name); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return -1; } // transform the Python value into the Oracle value cxoTransform_getTypeInfo(transformNum, &node->oracleTypeNum, &node->nativeTypeNum); if (cxoTransform_fromPython(transformNum, &node->nativeTypeNum, value, node->value, tempBuffer, CXO_JSON_ENCODING, CXO_JSON_ENCODING, NULL, 0) < 0) return -1; return 0; } //----------------------------------------------------------------------------- // cxoJsonBuffer_freeNode() // Frees any arrays allocated earlier for the specified node. //----------------------------------------------------------------------------- static void cxoJsonBuffer_freeNode(dpiJsonNode *node) { dpiJsonArray *array; dpiJsonObject *obj; uint32_t i; switch (node->nativeTypeNum) { case DPI_NATIVE_TYPE_JSON_ARRAY: array = &node->value->asJsonArray; if (array->elements) { for (i = 0; i < array->numElements; i++) { if (array->elements[i].value) cxoJsonBuffer_freeNode(&array->elements[i]); } PyMem_Free(array->elements); array->elements = NULL; } if (array->elementValues) { PyMem_Free(array->elementValues); array->elementValues = NULL; } break; case DPI_NATIVE_TYPE_JSON_OBJECT: obj = &node->value->asJsonObject; if (obj->fields) { for (i = 0; i < obj->numFields; i++) { if (obj->fields[i].value) cxoJsonBuffer_freeNode(&obj->fields[i]); } PyMem_Free(obj->fields); obj->fields = NULL; } if (obj->fieldNames) { PyMem_Free(obj->fieldNames); obj->fieldNames = NULL; } if (obj->fieldNameLengths) { PyMem_Free(obj->fieldNameLengths); obj->fieldNameLengths = NULL; } if (obj->fieldValues) { PyMem_Free(obj->fieldValues); obj->fieldValues = NULL; } break; } } //----------------------------------------------------------------------------- // cxoJsonBuffer_free() // Frees any memory allocated for the JSON buffer. //----------------------------------------------------------------------------- void cxoJsonBuffer_free(cxoJsonBuffer *buf) { uint32_t i; if (buf->buffers) { for (i = 0; i < buf->numBuffers; i++) cxoBuffer_clear(&buf->buffers[i]); PyMem_Free(buf->buffers); buf->buffers = NULL; } cxoJsonBuffer_freeNode(&buf->topNode); } //----------------------------------------------------------------------------- // cxoJsonBuffer_fromObject() // Populate the JSON buffer from a Python object. //----------------------------------------------------------------------------- int cxoJsonBuffer_fromObject(cxoJsonBuffer *buf, PyObject *obj) { // initialize JSON buffer structure buf->topNode.value = &buf->topNodeBuffer; buf->allocatedBuffers = 0; buf->numBuffers = 0; buf->buffers = NULL; // populate the top level node return cxoJsonBuffer_populateNode(buf, &buf->topNode, obj); } python-cx_Oracle-8.3.0/src/cxoLob.c000066400000000000000000000355531414105416400171340ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoLob.c // Defines the routines for handling LOB values. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoLob_new() // Create a new LOB. //----------------------------------------------------------------------------- PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType, dpiLob *handle) { cxoLob *lob; lob = (cxoLob*) cxoPyTypeLob.tp_alloc(&cxoPyTypeLob, 0); if (!lob) return NULL; lob->handle = handle; Py_INCREF(connection); lob->connection = connection; Py_INCREF(dbType); lob->dbType = dbType; return (PyObject*) lob; } //----------------------------------------------------------------------------- // cxoLob_free() // Free a LOB. //----------------------------------------------------------------------------- static void cxoLob_free(cxoLob *lob) { if (lob->handle) { dpiLob_release(lob->handle); lob->handle = NULL; } Py_CLEAR(lob->dbType); Py_CLEAR(lob->connection); Py_TYPE(lob)->tp_free((PyObject*) lob); } //----------------------------------------------------------------------------- // cxoLob_internalRead() // Return a portion (or all) of the data in the LOB. //----------------------------------------------------------------------------- static PyObject *cxoLob_internalRead(cxoLob *lob, uint64_t offset, uint64_t amount) { uint64_t bufferSize; PyObject *result; char *buffer; int status; // modify the arguments if (amount == (uint64_t)(-1)) { if (dpiLob_getSize(lob->handle, &amount) < 0) return cxoError_raiseAndReturnNull(); if (amount >= offset) amount = amount - offset + 1; else amount = 1; } // create a buffer of the correct size if (dpiLob_getBufferSize(lob->handle, amount, &bufferSize) < 0) return cxoError_raiseAndReturnNull(); buffer = (char*) PyMem_Malloc((Py_ssize_t) bufferSize); if (!buffer) return PyErr_NoMemory(); // read the LOB Py_BEGIN_ALLOW_THREADS status = dpiLob_readBytes(lob->handle, offset, amount, buffer, &bufferSize); Py_END_ALLOW_THREADS if (status < 0) { PyMem_Free(buffer); return cxoError_raiseAndReturnNull(); } // return the result if (lob->dbType == cxoDbTypeNclob) { result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize, lob->connection->encodingInfo.nencoding, NULL); } else if (lob->dbType == cxoDbTypeClob) { result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize, lob->connection->encodingInfo.encoding, NULL); } else { result = PyBytes_FromStringAndSize(buffer, (Py_ssize_t) bufferSize); } PyMem_Free(buffer); return result; } //----------------------------------------------------------------------------- // cxoLob_internalWrite() // Write the data in the Python object to the LOB. //----------------------------------------------------------------------------- static int cxoLob_internalWrite(cxoLob *lob, PyObject *dataObj, uint64_t offset) { const char *encoding; cxoBuffer buffer; int status; if (lob->dbType == cxoDbTypeNclob) encoding = lob->connection->encodingInfo.nencoding; else encoding = lob->connection->encodingInfo.encoding; if (cxoBuffer_fromObject(&buffer, dataObj, encoding) < 0) return -1; Py_BEGIN_ALLOW_THREADS status = dpiLob_writeBytes(lob->handle, offset, (char*) buffer.ptr, buffer.size); Py_END_ALLOW_THREADS cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoLob_size() // Return the size of the data in the LOB. //----------------------------------------------------------------------------- static PyObject *cxoLob_size(cxoLob *lob, PyObject *args) { uint64_t length; if (dpiLob_getSize(lob->handle, &length) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromUnsignedLongLong(length); } //----------------------------------------------------------------------------- // cxoLob_open() // Open the LOB to speed further accesses. //----------------------------------------------------------------------------- static PyObject *cxoLob_open(cxoLob *lob, PyObject *args) { int status; Py_BEGIN_ALLOW_THREADS status = dpiLob_openResource(lob->handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoLob_close() // Close the LOB. //----------------------------------------------------------------------------- static PyObject *cxoLob_close(cxoLob *lob, PyObject *args) { int status; Py_BEGIN_ALLOW_THREADS status = dpiLob_closeResource(lob->handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoLob_read() // Return a portion (or all) of the data in the LOB. //----------------------------------------------------------------------------- static PyObject *cxoLob_read(cxoLob *lob, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "offset", "amount", NULL }; unsigned PY_LONG_LONG offset, amount; // offset and amount are expected, both optional offset = 1; amount = (unsigned PY_LONG_LONG)(-1); if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|KK", keywordList, &offset, &amount)) return NULL; return cxoLob_internalRead(lob, (uint64_t) offset, (uint64_t) amount); } //----------------------------------------------------------------------------- // cxoLob_str() // Return all of the data in the LOB. //----------------------------------------------------------------------------- static PyObject *cxoLob_str(cxoLob *lob) { return cxoLob_internalRead(lob, 1, (uint64_t)(-1)); } //----------------------------------------------------------------------------- // cxoLob_write() // Write a value to the LOB. //----------------------------------------------------------------------------- static PyObject *cxoLob_write(cxoLob *lob, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "data", "offset", NULL }; unsigned PY_LONG_LONG offset; PyObject *dataObj; offset = 1; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|K", keywordList, &dataObj, &offset)) return NULL; if (cxoLob_internalWrite(lob, dataObj, (uint64_t) offset) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoLob_trim() // Trim the LOB to the specified length. //----------------------------------------------------------------------------- static PyObject *cxoLob_trim(cxoLob *lob, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "new_size", "newSize", NULL }; unsigned PY_LONG_LONG newSize, newSizeDeprecated; int status; newSize = newSizeDeprecated = 0; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|KK", keywordList, &newSize, &newSizeDeprecated)) return NULL; if (newSizeDeprecated > 0) { if (newSize > 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "new_size and newSize cannot both be specified"); return NULL; } newSize = newSizeDeprecated; } Py_BEGIN_ALLOW_THREADS status = dpiLob_trim(lob->handle, (uint64_t) newSize); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoLob_reduce() // Method provided for pickling/unpickling of LOBs. //----------------------------------------------------------------------------- static PyObject *cxoLob_reduce(cxoLob *lob) { PyObject *result, *value; value = cxoLob_str(lob); if (!value) return NULL; result = Py_BuildValue("(O(O))", Py_TYPE(value), value); Py_DECREF(value); return result; } //----------------------------------------------------------------------------- // cxoLob_getChunkSize() // Return the chunk size that should be used when reading/writing the LOB in // chunks. //----------------------------------------------------------------------------- static PyObject *cxoLob_getChunkSize(cxoLob *lob, PyObject *args) { uint32_t size; if (dpiLob_getChunkSize(lob->handle, &size) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(size); } //----------------------------------------------------------------------------- // cxoLob_isOpen() // Return a boolean indicating if the lob is open or not. //----------------------------------------------------------------------------- static PyObject *cxoLob_isOpen(cxoLob *lob, PyObject *args) { int isOpen, status; Py_BEGIN_ALLOW_THREADS status = dpiLob_getIsResourceOpen(lob->handle, &isOpen); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); return PyBool_FromLong(isOpen); } //----------------------------------------------------------------------------- // cxoLob_getFileName() // Return the directory alias and file name for the BFILE lob. //----------------------------------------------------------------------------- static PyObject *cxoLob_getFileName(cxoLob *lob, PyObject *args) { uint32_t directoryAliasLength, fileNameLength; const char *directoryAlias, *fileName; PyObject *result, *temp; int status; // get the information from the LOB Py_BEGIN_ALLOW_THREADS status = dpiLob_getDirectoryAndFileName(lob->handle, &directoryAlias, &directoryAliasLength, &fileName, &fileNameLength); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); // create the two-tuple for returning result = PyTuple_New(2); if (!result) return NULL; temp = PyUnicode_Decode(directoryAlias, directoryAliasLength, lob->connection->encodingInfo.encoding, NULL); if (!temp) { Py_DECREF(result); return NULL; } PyTuple_SET_ITEM(result, 0, temp); temp = PyUnicode_Decode(fileName, fileNameLength, lob->connection->encodingInfo.encoding, NULL); if (!temp) { Py_DECREF(result); return NULL; } PyTuple_SET_ITEM(result, 1, temp); return result; } //----------------------------------------------------------------------------- // cxoLob_setFileName() // Set the directory alias and file name for the BFILE lob. //----------------------------------------------------------------------------- static PyObject *cxoLob_setFileName(cxoLob *lob, PyObject *args) { cxoBuffer directoryAliasBuffer, fileNameBuffer; PyObject *directoryAliasObj, *fileNameObj; int status; // get the directory alias and file name if (!PyArg_ParseTuple(args, "OO", &directoryAliasObj, &fileNameObj)) return NULL; if (cxoBuffer_fromObject(&directoryAliasBuffer, directoryAliasObj, lob->connection->encodingInfo.encoding) < 0) return NULL; if (cxoBuffer_fromObject(&fileNameBuffer, fileNameObj, lob->connection->encodingInfo.encoding) < 0) { cxoBuffer_clear(&directoryAliasBuffer); return NULL; } // perform the work Py_BEGIN_ALLOW_THREADS status = dpiLob_setDirectoryAndFileName(lob->handle, (char*) directoryAliasBuffer.ptr, directoryAliasBuffer.size, (char*) fileNameBuffer.ptr, fileNameBuffer.size); Py_END_ALLOW_THREADS cxoBuffer_clear(&directoryAliasBuffer); cxoBuffer_clear(&fileNameBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoLob_fileExists() // Return a boolean indicating if the BFIILE lob exists. //----------------------------------------------------------------------------- static PyObject *cxoLob_fileExists(cxoLob *lob, PyObject *args) { int status, exists; Py_BEGIN_ALLOW_THREADS status = dpiLob_getFileExists(lob->handle, &exists); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); if (exists) Py_RETURN_TRUE; Py_RETURN_FALSE; } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoLobMethods[] = { { "size", (PyCFunction) cxoLob_size, METH_NOARGS }, { "open", (PyCFunction) cxoLob_open, METH_NOARGS }, { "close", (PyCFunction) cxoLob_close, METH_NOARGS }, { "read", (PyCFunction) cxoLob_read, METH_VARARGS | METH_KEYWORDS }, { "write", (PyCFunction) cxoLob_write, METH_VARARGS | METH_KEYWORDS }, { "trim", (PyCFunction) cxoLob_trim, METH_VARARGS | METH_KEYWORDS }, { "getchunksize", (PyCFunction) cxoLob_getChunkSize, METH_NOARGS }, { "isopen", (PyCFunction) cxoLob_isOpen, METH_NOARGS }, { "getfilename", (PyCFunction) cxoLob_getFileName, METH_NOARGS }, { "setfilename", (PyCFunction) cxoLob_setFileName, METH_VARARGS }, { "fileexists", (PyCFunction) cxoLob_fileExists, METH_NOARGS }, { "__reduce__", (PyCFunction) cxoLob_reduce, METH_NOARGS }, { NULL, NULL } }; //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "type", T_OBJECT, offsetof(cxoLob, dbType), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // Python type declaration //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeLob = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.LOB", .tp_basicsize = sizeof(cxoLob), .tp_dealloc = (destructor) cxoLob_free, .tp_str = (reprfunc) cxoLob_str, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoLobMethods, .tp_members = cxoMembers }; python-cx_Oracle-8.3.0/src/cxoModule.c000066400000000000000000000745271414105416400176510ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. // // Licensed under BSD license (see LICENSE.txt). //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoModule.c // Implementation of cx_Oracle module. //----------------------------------------------------------------------------- #include "cxoModule.h" // define macro for adding integer constants #define CXO_ADD_INT_CONSTANT(name, value) \ if (PyModule_AddIntConstant(module, name, value) < 0) \ return NULL; // define macro for adding Python Database API types #define CXO_ADD_API_TYPE(name, transformNum, typeObj) \ if (cxoModule_addApiType(module, name, transformNum, typeObj) < 0) \ return NULL; // define macro for adding database types #define CXO_ADD_DB_TYPE(num, name, transformNum, typeObj) \ if (cxoModule_addDbType(module, num, name, transformNum, typeObj) < 0) \ return NULL; // define macro for associating database types with Database API types #define CXO_ASSOCIATE_DB_TYPE(apiType, dbType) \ if (PyList_Append(apiType->dbTypes, (PyObject*) dbType) < 0) \ return NULL; // define macro for adding type objects #define CXO_ADD_TYPE_OBJECT(name, type) \ Py_INCREF(type); \ if (PyModule_AddObject(module, name, (PyObject*) type) < 0) \ return NULL; // define macro for and making types ready #define CXO_MAKE_TYPE_READY(type) \ if (PyType_Ready(type) < 0) \ return NULL; //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- PyObject *cxoWarningException = NULL; PyObject *cxoErrorException = NULL; PyObject *cxoInterfaceErrorException = NULL; PyObject *cxoDatabaseErrorException = NULL; PyObject *cxoDataErrorException = NULL; PyObject *cxoOperationalErrorException = NULL; PyObject *cxoIntegrityErrorException = NULL; PyObject *cxoInternalErrorException = NULL; PyObject *cxoProgrammingErrorException = NULL; PyObject *cxoNotSupportedErrorException = NULL; PyObject *cxoJsonDumpFunction = NULL; PyObject *cxoJsonLoadFunction = NULL; cxoDbType *cxoDbTypeBfile = NULL; cxoDbType *cxoDbTypeBinaryDouble = NULL; cxoDbType *cxoDbTypeBinaryFloat = NULL; cxoDbType *cxoDbTypeBinaryInteger = NULL; cxoDbType *cxoDbTypeBlob = NULL; cxoDbType *cxoDbTypeBoolean = NULL; cxoDbType *cxoDbTypeChar = NULL; cxoDbType *cxoDbTypeClob = NULL; cxoDbType *cxoDbTypeCursor = NULL; cxoDbType *cxoDbTypeDate = NULL; cxoDbType *cxoDbTypeIntervalDS = NULL; cxoDbType *cxoDbTypeIntervalYM = NULL; cxoDbType *cxoDbTypeJson = NULL; cxoDbType *cxoDbTypeLong = NULL; cxoDbType *cxoDbTypeLongRaw = NULL; cxoDbType *cxoDbTypeNchar = NULL; cxoDbType *cxoDbTypeNclob = NULL; cxoDbType *cxoDbTypeNumber = NULL; cxoDbType *cxoDbTypeNvarchar = NULL; cxoDbType *cxoDbTypeObject = NULL; cxoDbType *cxoDbTypeRaw = NULL; cxoDbType *cxoDbTypeRowid = NULL; cxoDbType *cxoDbTypeTimestamp = NULL; cxoDbType *cxoDbTypeTimestampLTZ = NULL; cxoDbType *cxoDbTypeTimestampTZ = NULL; cxoDbType *cxoDbTypeVarchar = NULL; cxoApiType *cxoApiTypeBinary = NULL; cxoApiType *cxoApiTypeDatetime = NULL; cxoApiType *cxoApiTypeNumber = NULL; cxoApiType *cxoApiTypeRowid = NULL; cxoApiType *cxoApiTypeString = NULL; cxoFuture *cxoFutureObj = NULL; dpiContext *cxoDpiContext = NULL; dpiVersionInfo cxoClientVersionInfo; //----------------------------------------------------------------------------- // cxoModule_addApiType() // Create a Python Database API type and add it to the module. //----------------------------------------------------------------------------- static int cxoModule_addApiType(PyObject *module, const char *name, cxoTransformNum defaultTransformNum, cxoApiType **apiType) { cxoApiType *tempApiType; tempApiType = (cxoApiType*) cxoPyTypeApiType.tp_alloc(&cxoPyTypeApiType, 0); if (!tempApiType) return -1; tempApiType->name = name; tempApiType->defaultTransformNum = defaultTransformNum; tempApiType->dbTypes = PyList_New(0); if (!tempApiType->dbTypes) { Py_DECREF(tempApiType); return -1; } if (PyModule_AddObject(module, name, (PyObject*) tempApiType) < 0) { Py_DECREF(tempApiType); return -1; } *apiType = tempApiType; return 0; } //----------------------------------------------------------------------------- // cxoModule_addDbType() // Create a database type and add it to the module. //----------------------------------------------------------------------------- static int cxoModule_addDbType(PyObject *module, uint32_t num, const char *name, cxoTransformNum defaultTransformNum, cxoDbType **dbType) { cxoDbType *tempDbType; tempDbType = (cxoDbType*) cxoPyTypeDbType.tp_alloc(&cxoPyTypeDbType, 0); if (!tempDbType) return -1; tempDbType->num = num; tempDbType->name = name; tempDbType->defaultTransformNum = defaultTransformNum; if (PyModule_AddObject(module, name, (PyObject*) tempDbType) < 0) { Py_DECREF(tempDbType); return -1; } *dbType = tempDbType; return 0; } //----------------------------------------------------------------------------- // cxoModule_setException() // Create an exception and set it in the provided dictionary. //----------------------------------------------------------------------------- static int cxoModule_setException(PyObject *module, PyObject **exception, char *name, PyObject *baseException) { char buffer[100]; sprintf(buffer, "cx_Oracle.%s", name); *exception = PyErr_NewException(buffer, baseException, NULL); if (!*exception) return -1; return PyModule_AddObject(module, name, *exception); } //----------------------------------------------------------------------------- // cxoModule_makeDSN() // Make a data source name given the host port and SID. //----------------------------------------------------------------------------- static PyObject* cxoModule_makeDSN(PyObject* self, PyObject* args, PyObject* keywordArgs) { static const unsigned int numConnectDataArgs = 5; static char *keywordList[] = { "host", "port", "sid", "service_name", "region", "sharding_key", "super_sharding_key", NULL }; PyObject *result, *connectData, *hostObj, *portObj; char connectDataFormat[72], *sourcePtr, *targetPtr; PyObject *connectDataArgs[5], *formatArgsArray; unsigned int i; // parse arguments for (i = 0; i < numConnectDataArgs; i++) connectDataArgs[i] = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|OOOOO", keywordList, &hostObj, &portObj, &connectDataArgs[0], &connectDataArgs[1], &connectDataArgs[2], &connectDataArgs[3], &connectDataArgs[4])) return NULL; // create list for connect data format arguments formatArgsArray = PyList_New(0); if (!formatArgsArray) return NULL; // process each of the connect data arguments // build up a format string and a list of format arguments targetPtr = connectDataFormat; *targetPtr = '\0'; for (i = 0; i < numConnectDataArgs; i++) { if (connectDataArgs[i]) { if (PyList_Append(formatArgsArray, connectDataArgs[i]) < 0) { Py_DECREF(formatArgsArray); return NULL; } sourcePtr = keywordList[i + 2]; *targetPtr++ = '('; while (*sourcePtr) *targetPtr++ = toupper(*sourcePtr++); *targetPtr++ = '='; *targetPtr++ = '%'; *targetPtr++ = 's'; *targetPtr++ = ')'; *targetPtr = '\0'; } } // determine connect data connectData = cxoUtils_formatString(connectDataFormat, PyList_AsTuple(formatArgsArray)); Py_DECREF(formatArgsArray); if (!connectData) return NULL; // perform overall format result = cxoUtils_formatString("(DESCRIPTION=(ADDRESS=" "(PROTOCOL=TCP)(HOST=%s)(PORT=%s))(CONNECT_DATA=%s))", PyTuple_Pack(3, hostObj, portObj, connectData)); Py_DECREF(connectData); return result; } //----------------------------------------------------------------------------- // cxoModule_clientVersion() // Return the version of the Oracle client being used as a 5-tuple. //----------------------------------------------------------------------------- static PyObject* cxoModule_clientVersion(PyObject* self, PyObject* args) { if (cxoUtils_initializeDPI(NULL) < 0) return NULL; return Py_BuildValue("(iiiii)", cxoClientVersionInfo.versionNum, cxoClientVersionInfo.releaseNum, cxoClientVersionInfo.updateNum, cxoClientVersionInfo.portReleaseNum, cxoClientVersionInfo.portUpdateNum); } //----------------------------------------------------------------------------- // cxoModule_initClientLib() // Initialize the client library now, rather than when the first call to // get the Oracle Client library version, create a standalone connection or // session pool is performed. //----------------------------------------------------------------------------- static PyObject* cxoModule_initClientLib(PyObject* self, PyObject* args, PyObject* keywordArgs) { static char *keywordList[] = { "lib_dir", "config_dir", "error_url", "driver_name", NULL }; Py_ssize_t libDirSize, configDirSize, errorUrlSize, driverNameSize; dpiContextCreateParams params; memset(¶ms, 0, sizeof(dpiContextCreateParams)); libDirSize = configDirSize = errorUrlSize = driverNameSize = 0; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|z#z#z#z#", keywordList, ¶ms.oracleClientLibDir, &libDirSize, ¶ms.oracleClientConfigDir, &configDirSize, ¶ms.loadErrorUrl, &errorUrlSize, ¶ms.defaultDriverName, &driverNameSize)) return NULL; if (libDirSize == 0) params.oracleClientLibDir = NULL; if (configDirSize == 0) params.oracleClientConfigDir = NULL; if (errorUrlSize == 0) params.loadErrorUrl = NULL; if (driverNameSize == 0) params.defaultDriverName = NULL; if (cxoUtils_initializeDPI(¶ms) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoModule_time() // Returns a time value suitable for binding. //----------------------------------------------------------------------------- static PyObject* cxoModule_time(PyObject* self, PyObject* args) { return cxoError_raiseFromString(cxoNotSupportedErrorException, "Oracle does not support time only variables"); } //----------------------------------------------------------------------------- // cxoModule_timeFromTicks() // Returns a time value suitable for binding. //----------------------------------------------------------------------------- static PyObject* cxoModule_timeFromTicks(PyObject* self, PyObject* args) { return cxoError_raiseFromString(cxoNotSupportedErrorException, "Oracle does not support time only variables"); } //----------------------------------------------------------------------------- // cxoModule_dateFromTicks() // Returns a date value suitable for binding. //----------------------------------------------------------------------------- static PyObject* cxoModule_dateFromTicks(PyObject* self, PyObject* args) { return cxoTransform_dateFromTicks(args); } //----------------------------------------------------------------------------- // cxoModule_timestampFromTicks() // Returns a date value suitable for binding. //----------------------------------------------------------------------------- static PyObject* cxoModule_timestampFromTicks(PyObject* self, PyObject* args) { return cxoTransform_timestampFromTicks(args); } //----------------------------------------------------------------------------- // Declaration of methods supported by this module //----------------------------------------------------------------------------- static PyMethodDef cxoModuleMethods[] = { { "makedsn", (PyCFunction) cxoModule_makeDSN, METH_VARARGS | METH_KEYWORDS }, { "Time", (PyCFunction) cxoModule_time, METH_VARARGS }, { "DateFromTicks", (PyCFunction) cxoModule_dateFromTicks, METH_VARARGS }, { "TimeFromTicks", (PyCFunction) cxoModule_timeFromTicks, METH_VARARGS }, { "TimestampFromTicks", (PyCFunction) cxoModule_timestampFromTicks, METH_VARARGS }, { "clientversion", (PyCFunction) cxoModule_clientVersion, METH_NOARGS }, { "init_oracle_client", (PyCFunction) cxoModule_initClientLib, METH_VARARGS | METH_KEYWORDS }, { NULL } }; //----------------------------------------------------------------------------- // Declaration of module definition //----------------------------------------------------------------------------- static struct PyModuleDef cxoModuleDef = { PyModuleDef_HEAD_INIT, "cx_Oracle", NULL, -1, cxoModuleMethods, // methods NULL, // m_reload NULL, // traverse NULL, // clear NULL // free }; //----------------------------------------------------------------------------- // cxoModule_initialize() // Initialization routine for the module. //----------------------------------------------------------------------------- static PyObject *cxoModule_initialize(void) { PyObject *module; // initialize transforms if (cxoTransform_init() < 0) return NULL; // prepare the types for use by the module CXO_MAKE_TYPE_READY(&cxoPyTypeApiType); CXO_MAKE_TYPE_READY(&cxoPyTypeConnection); CXO_MAKE_TYPE_READY(&cxoPyTypeCursor); CXO_MAKE_TYPE_READY(&cxoPyTypeDbType); CXO_MAKE_TYPE_READY(&cxoPyTypeDeqOptions); CXO_MAKE_TYPE_READY(&cxoPyTypeEnqOptions); CXO_MAKE_TYPE_READY(&cxoPyTypeError); CXO_MAKE_TYPE_READY(&cxoPyTypeFuture); CXO_MAKE_TYPE_READY(&cxoPyTypeLob); CXO_MAKE_TYPE_READY(&cxoPyTypeMsgProps); CXO_MAKE_TYPE_READY(&cxoPyTypeMessage); CXO_MAKE_TYPE_READY(&cxoPyTypeMessageQuery); CXO_MAKE_TYPE_READY(&cxoPyTypeMessageRow); CXO_MAKE_TYPE_READY(&cxoPyTypeMessageTable); CXO_MAKE_TYPE_READY(&cxoPyTypeObjectAttr); CXO_MAKE_TYPE_READY(&cxoPyTypeObject); CXO_MAKE_TYPE_READY(&cxoPyTypeObjectType); CXO_MAKE_TYPE_READY(&cxoPyTypeQueue); CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaCollection); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDatabase); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDoc); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDocCursor); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaOperation); CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr); CXO_MAKE_TYPE_READY(&cxoPyTypeVar); // initialize module and retrieve the dictionary module = PyModule_Create(&cxoModuleDef); if (!module) return NULL; // create exception object and add it to the dictionary if (cxoModule_setException(module, &cxoWarningException, "Warning", NULL) < 0) return NULL; if (cxoModule_setException(module, &cxoErrorException, "Error", NULL) < 0) return NULL; if (cxoModule_setException(module, &cxoInterfaceErrorException, "InterfaceError", cxoErrorException) < 0) return NULL; if (cxoModule_setException(module, &cxoDatabaseErrorException, "DatabaseError", cxoErrorException) < 0) return NULL; if (cxoModule_setException(module, &cxoDataErrorException, "DataError", cxoDatabaseErrorException) < 0) return NULL; if (cxoModule_setException(module, &cxoOperationalErrorException, "OperationalError", cxoDatabaseErrorException) < 0) return NULL; if (cxoModule_setException(module, &cxoIntegrityErrorException, "IntegrityError", cxoDatabaseErrorException) < 0) return NULL; if (cxoModule_setException(module, &cxoInternalErrorException, "InternalError", cxoDatabaseErrorException) < 0) return NULL; if (cxoModule_setException(module, &cxoProgrammingErrorException, "ProgrammingError", cxoDatabaseErrorException) < 0) return NULL; if (cxoModule_setException(module, &cxoNotSupportedErrorException, "NotSupportedError", cxoDatabaseErrorException) < 0) return NULL; // set up the types that are available CXO_ADD_TYPE_OBJECT("ApiType", &cxoPyTypeApiType) CXO_ADD_TYPE_OBJECT("Binary", &PyBytes_Type) CXO_ADD_TYPE_OBJECT("Connection", &cxoPyTypeConnection) CXO_ADD_TYPE_OBJECT("Cursor", &cxoPyTypeCursor) CXO_ADD_TYPE_OBJECT("Date", cxoPyTypeDate) CXO_ADD_TYPE_OBJECT("DbType", &cxoPyTypeDbType) CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions) CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions) CXO_ADD_TYPE_OBJECT("_Error", &cxoPyTypeError) CXO_ADD_TYPE_OBJECT("LOB", &cxoPyTypeLob) CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps) CXO_ADD_TYPE_OBJECT("Object", &cxoPyTypeObject) CXO_ADD_TYPE_OBJECT("ObjectType", &cxoPyTypeObjectType) CXO_ADD_TYPE_OBJECT("SessionPool", &cxoPyTypeSessionPool) CXO_ADD_TYPE_OBJECT("SodaCollection", &cxoPyTypeSodaCollection) CXO_ADD_TYPE_OBJECT("SodaDatabase", &cxoPyTypeSodaDatabase) CXO_ADD_TYPE_OBJECT("SodaDoc", &cxoPyTypeSodaDoc) CXO_ADD_TYPE_OBJECT("SodaDocCursor", &cxoPyTypeSodaDocCursor) CXO_ADD_TYPE_OBJECT("SodaOperation", &cxoPyTypeSodaOperation) CXO_ADD_TYPE_OBJECT("Timestamp", cxoPyTypeDateTime) CXO_ADD_TYPE_OBJECT("Var", &cxoPyTypeVar) // the name "connect" is required by the DB API CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection) // create the database types (preferred names) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BFILE, "DB_TYPE_BFILE", CXO_TRANSFORM_BFILE, &cxoDbTypeBfile) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_DOUBLE, "DB_TYPE_BINARY_DOUBLE", CXO_TRANSFORM_NATIVE_DOUBLE, &cxoDbTypeBinaryDouble) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_FLOAT, "DB_TYPE_BINARY_FLOAT", CXO_TRANSFORM_NATIVE_FLOAT, &cxoDbTypeBinaryFloat) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_INT, "DB_TYPE_BINARY_INTEGER", CXO_TRANSFORM_NATIVE_INT, &cxoDbTypeBinaryInteger) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BLOB, "DB_TYPE_BLOB", CXO_TRANSFORM_BLOB, &cxoDbTypeBlob) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BOOLEAN, "DB_TYPE_BOOLEAN", CXO_TRANSFORM_BOOLEAN, &cxoDbTypeBoolean) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CHAR, "DB_TYPE_CHAR", CXO_TRANSFORM_FIXED_CHAR, &cxoDbTypeChar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CLOB, "DB_TYPE_CLOB", CXO_TRANSFORM_CLOB, &cxoDbTypeClob) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_STMT, "DB_TYPE_CURSOR", CXO_TRANSFORM_CURSOR, &cxoDbTypeCursor) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_DATE, "DB_TYPE_DATE", CXO_TRANSFORM_DATETIME, &cxoDbTypeDate) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_INTERVAL_DS, "DB_TYPE_INTERVAL_DS", CXO_TRANSFORM_TIMEDELTA, &cxoDbTypeIntervalDS) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_INTERVAL_YM, "DB_TYPE_INTERVAL_YM", CXO_TRANSFORM_UNSUPPORTED, &cxoDbTypeIntervalYM) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_JSON, "DB_TYPE_JSON", CXO_TRANSFORM_JSON, &cxoDbTypeJson) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_LONG_VARCHAR, "DB_TYPE_LONG", CXO_TRANSFORM_LONG_STRING, &cxoDbTypeLong) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_LONG_RAW, "DB_TYPE_LONG_RAW", CXO_TRANSFORM_LONG_BINARY, &cxoDbTypeLongRaw) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NCHAR, "DB_TYPE_NCHAR", CXO_TRANSFORM_FIXED_NCHAR, &cxoDbTypeNchar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NCLOB, "DB_TYPE_NCLOB", CXO_TRANSFORM_NCLOB, &cxoDbTypeNclob) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NUMBER, "DB_TYPE_NUMBER", CXO_TRANSFORM_FLOAT, &cxoDbTypeNumber) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NVARCHAR, "DB_TYPE_NVARCHAR", CXO_TRANSFORM_NSTRING, &cxoDbTypeNvarchar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_OBJECT, "DB_TYPE_OBJECT", CXO_TRANSFORM_OBJECT, &cxoDbTypeObject) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_RAW, "DB_TYPE_RAW", CXO_TRANSFORM_BINARY, &cxoDbTypeRaw) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_ROWID, "DB_TYPE_ROWID", CXO_TRANSFORM_ROWID, &cxoDbTypeRowid) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP, "DB_TYPE_TIMESTAMP", CXO_TRANSFORM_TIMESTAMP, &cxoDbTypeTimestamp) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP_LTZ, "DB_TYPE_TIMESTAMP_LTZ", CXO_TRANSFORM_TIMESTAMP_LTZ, &cxoDbTypeTimestampLTZ) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP_TZ, "DB_TYPE_TIMESTAMP_TZ", CXO_TRANSFORM_TIMESTAMP_TZ, &cxoDbTypeTimestampTZ) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_VARCHAR, "DB_TYPE_VARCHAR", CXO_TRANSFORM_STRING, &cxoDbTypeVarchar) // create the synonyms for database types (deprecated names) CXO_ADD_TYPE_OBJECT("BFILE", cxoDbTypeBfile) CXO_ADD_TYPE_OBJECT("BLOB", cxoDbTypeBlob) CXO_ADD_TYPE_OBJECT("CLOB", cxoDbTypeClob) CXO_ADD_TYPE_OBJECT("CURSOR", cxoDbTypeCursor) CXO_ADD_TYPE_OBJECT("OBJECT", cxoDbTypeObject) CXO_ADD_TYPE_OBJECT("FIXED_CHAR", cxoDbTypeChar) CXO_ADD_TYPE_OBJECT("FIXED_NCHAR", cxoDbTypeNchar) CXO_ADD_TYPE_OBJECT("NCHAR", cxoDbTypeNvarchar) CXO_ADD_TYPE_OBJECT("INTERVAL", cxoDbTypeIntervalDS) CXO_ADD_TYPE_OBJECT("LONG_BINARY", cxoDbTypeLongRaw) CXO_ADD_TYPE_OBJECT("LONG_STRING", cxoDbTypeLong) CXO_ADD_TYPE_OBJECT("NCLOB", cxoDbTypeNclob) CXO_ADD_TYPE_OBJECT("TIMESTAMP", cxoDbTypeTimestamp) CXO_ADD_TYPE_OBJECT("NATIVE_INT", cxoDbTypeBinaryInteger) CXO_ADD_TYPE_OBJECT("NATIVE_FLOAT", cxoDbTypeBinaryDouble) CXO_ADD_TYPE_OBJECT("BOOLEAN", cxoDbTypeBoolean) // create the Python Database API types CXO_ADD_API_TYPE("BINARY", CXO_TRANSFORM_BINARY, &cxoApiTypeBinary) CXO_ADD_API_TYPE("DATETIME", CXO_TRANSFORM_DATETIME, &cxoApiTypeDatetime) CXO_ADD_API_TYPE("NUMBER", CXO_TRANSFORM_FLOAT, &cxoApiTypeNumber) CXO_ADD_API_TYPE("ROWID", CXO_TRANSFORM_ROWID, &cxoApiTypeRowid) CXO_ADD_API_TYPE("STRING", CXO_TRANSFORM_STRING, &cxoApiTypeString) // associate the Python Database API types with the database types CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeLongRaw) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeRaw) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeDate) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestamp) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestampLTZ) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestampTZ) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryDouble) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryFloat) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryInteger) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeNumber) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeRowid, cxoDbTypeRowid) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeChar) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeLong) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeNchar) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeNvarchar) CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeVarchar) // create constants required by Python DB API 2.0 if (PyModule_AddStringConstant(module, "apilevel", "2.0") < 0) return NULL; if (PyModule_AddIntConstant(module, "threadsafety", 2) < 0) return NULL; if (PyModule_AddStringConstant(module, "paramstyle", "named") < 0) return NULL; // add version and build time for easier support if (PyModule_AddStringConstant(module, "version", CXO_BUILD_VERSION_STRING) < 0) return NULL; if (PyModule_AddStringConstant(module, "__version__", CXO_BUILD_VERSION_STRING) < 0) return NULL; if (PyModule_AddStringConstant(module, "buildtime", __DATE__ " " __TIME__) < 0) return NULL; // create and initialize future object cxoFutureObj = (cxoFuture*) cxoPyTypeFuture.tp_alloc(&cxoPyTypeFuture, 0); if (!cxoFutureObj) return NULL; if (PyModule_AddObject(module, "__future__", (PyObject*) cxoFutureObj) < 0) return NULL; // add constants for authorization modes CXO_ADD_INT_CONSTANT("DEFAULT_AUTH", DPI_MODE_AUTH_DEFAULT) CXO_ADD_INT_CONSTANT("SYSASM", DPI_MODE_AUTH_SYSASM) CXO_ADD_INT_CONSTANT("SYSBKP", DPI_MODE_AUTH_SYSBKP) CXO_ADD_INT_CONSTANT("SYSDBA", DPI_MODE_AUTH_SYSDBA) CXO_ADD_INT_CONSTANT("SYSDGD", DPI_MODE_AUTH_SYSDGD) CXO_ADD_INT_CONSTANT("SYSKMT", DPI_MODE_AUTH_SYSKMT) CXO_ADD_INT_CONSTANT("SYSOPER", DPI_MODE_AUTH_SYSOPER) CXO_ADD_INT_CONSTANT("SYSRAC", DPI_MODE_AUTH_SYSRAC) CXO_ADD_INT_CONSTANT("PRELIM_AUTH", DPI_MODE_AUTH_PRELIM) // add constants for session pool get modes CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_WAIT", DPI_MODE_POOL_GET_WAIT) CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_NOWAIT", DPI_MODE_POOL_GET_NOWAIT) CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_FORCEGET", DPI_MODE_POOL_GET_FORCEGET) CXO_ADD_INT_CONSTANT("SPOOL_ATTRVAL_TIMEDWAIT", DPI_MODE_POOL_GET_TIMEDWAIT) // add constants for database shutdown modes CXO_ADD_INT_CONSTANT("DBSHUTDOWN_ABORT", DPI_MODE_SHUTDOWN_ABORT) CXO_ADD_INT_CONSTANT("DBSHUTDOWN_FINAL", DPI_MODE_SHUTDOWN_FINAL) CXO_ADD_INT_CONSTANT("DBSHUTDOWN_IMMEDIATE", DPI_MODE_SHUTDOWN_IMMEDIATE) CXO_ADD_INT_CONSTANT("DBSHUTDOWN_TRANSACTIONAL", DPI_MODE_SHUTDOWN_TRANSACTIONAL) CXO_ADD_INT_CONSTANT("DBSHUTDOWN_TRANSACTIONAL_LOCAL", DPI_MODE_SHUTDOWN_TRANSACTIONAL_LOCAL) // add constants for purity CXO_ADD_INT_CONSTANT("ATTR_PURITY_DEFAULT", DPI_PURITY_DEFAULT) CXO_ADD_INT_CONSTANT("ATTR_PURITY_NEW", DPI_PURITY_NEW) CXO_ADD_INT_CONSTANT("ATTR_PURITY_SELF", DPI_PURITY_SELF) // add constants for subscription protocols CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_OCI", DPI_SUBSCR_PROTO_CALLBACK) CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_MAIL", DPI_SUBSCR_PROTO_MAIL) CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_SERVER", DPI_SUBSCR_PROTO_PLSQL) CXO_ADD_INT_CONSTANT("SUBSCR_PROTO_HTTP", DPI_SUBSCR_PROTO_HTTP) // add constants for subscription quality of service CXO_ADD_INT_CONSTANT("SUBSCR_QOS_RELIABLE", DPI_SUBSCR_QOS_RELIABLE) CXO_ADD_INT_CONSTANT("SUBSCR_QOS_DEREG_NFY", DPI_SUBSCR_QOS_DEREG_NFY) CXO_ADD_INT_CONSTANT("SUBSCR_QOS_ROWIDS", DPI_SUBSCR_QOS_ROWIDS) CXO_ADD_INT_CONSTANT("SUBSCR_QOS_QUERY", DPI_SUBSCR_QOS_QUERY) CXO_ADD_INT_CONSTANT("SUBSCR_QOS_BEST_EFFORT", DPI_SUBSCR_QOS_BEST_EFFORT) // add constants for subscription namespaces CXO_ADD_INT_CONSTANT("SUBSCR_NAMESPACE_AQ", DPI_SUBSCR_NAMESPACE_AQ) CXO_ADD_INT_CONSTANT("SUBSCR_NAMESPACE_DBCHANGE", DPI_SUBSCR_NAMESPACE_DBCHANGE) // add constants for subscription grouping classes CXO_ADD_INT_CONSTANT("SUBSCR_GROUPING_CLASS_TIME", DPI_SUBSCR_GROUPING_CLASS_TIME) // add constants for subscription grouping types CXO_ADD_INT_CONSTANT("SUBSCR_GROUPING_TYPE_SUMMARY", DPI_SUBSCR_GROUPING_TYPE_SUMMARY) CXO_ADD_INT_CONSTANT("SUBSCR_GROUPING_TYPE_LAST", DPI_SUBSCR_GROUPING_TYPE_LAST) // add constants for event types CXO_ADD_INT_CONSTANT("EVENT_NONE", DPI_EVENT_NONE) CXO_ADD_INT_CONSTANT("EVENT_STARTUP", DPI_EVENT_STARTUP) CXO_ADD_INT_CONSTANT("EVENT_SHUTDOWN", DPI_EVENT_SHUTDOWN) CXO_ADD_INT_CONSTANT("EVENT_SHUTDOWN_ANY", DPI_EVENT_SHUTDOWN_ANY) CXO_ADD_INT_CONSTANT("EVENT_DEREG", DPI_EVENT_DEREG) CXO_ADD_INT_CONSTANT("EVENT_OBJCHANGE", DPI_EVENT_OBJCHANGE) CXO_ADD_INT_CONSTANT("EVENT_QUERYCHANGE", DPI_EVENT_QUERYCHANGE) CXO_ADD_INT_CONSTANT("EVENT_AQ", DPI_EVENT_AQ) // add constants for opcodes CXO_ADD_INT_CONSTANT("OPCODE_ALLOPS", DPI_OPCODE_ALL_OPS) CXO_ADD_INT_CONSTANT("OPCODE_ALLROWS", DPI_OPCODE_ALL_ROWS) CXO_ADD_INT_CONSTANT("OPCODE_INSERT", DPI_OPCODE_INSERT) CXO_ADD_INT_CONSTANT("OPCODE_UPDATE", DPI_OPCODE_UPDATE) CXO_ADD_INT_CONSTANT("OPCODE_DELETE", DPI_OPCODE_DELETE) CXO_ADD_INT_CONSTANT("OPCODE_ALTER", DPI_OPCODE_ALTER) CXO_ADD_INT_CONSTANT("OPCODE_DROP", DPI_OPCODE_DROP) // add constants for AQ dequeue modes CXO_ADD_INT_CONSTANT("DEQ_BROWSE", DPI_MODE_DEQ_BROWSE) CXO_ADD_INT_CONSTANT("DEQ_LOCKED", DPI_MODE_DEQ_LOCKED) CXO_ADD_INT_CONSTANT("DEQ_REMOVE", DPI_MODE_DEQ_REMOVE) CXO_ADD_INT_CONSTANT("DEQ_REMOVE_NODATA", DPI_MODE_DEQ_REMOVE_NO_DATA) // add constants for AQ dequeue navigation CXO_ADD_INT_CONSTANT("DEQ_FIRST_MSG", DPI_DEQ_NAV_FIRST_MSG) CXO_ADD_INT_CONSTANT("DEQ_NEXT_TRANSACTION", DPI_DEQ_NAV_NEXT_TRANSACTION) CXO_ADD_INT_CONSTANT("DEQ_NEXT_MSG", DPI_DEQ_NAV_NEXT_MSG) // add constants for AQ dequeue visibility CXO_ADD_INT_CONSTANT("DEQ_IMMEDIATE", DPI_VISIBILITY_IMMEDIATE) CXO_ADD_INT_CONSTANT("DEQ_ON_COMMIT", DPI_VISIBILITY_ON_COMMIT) // add constants for AQ dequeue wait CXO_ADD_INT_CONSTANT("DEQ_NO_WAIT", DPI_DEQ_WAIT_NO_WAIT) CXO_ADD_INT_CONSTANT("DEQ_WAIT_FOREVER", DPI_DEQ_WAIT_FOREVER) // add constants for AQ enqueue visibility CXO_ADD_INT_CONSTANT("ENQ_IMMEDIATE", DPI_VISIBILITY_IMMEDIATE) CXO_ADD_INT_CONSTANT("ENQ_ON_COMMIT", DPI_VISIBILITY_ON_COMMIT) // add constants for AQ table purge mode (message) CXO_ADD_INT_CONSTANT("MSG_PERSISTENT", DPI_MODE_MSG_PERSISTENT) CXO_ADD_INT_CONSTANT("MSG_BUFFERED", DPI_MODE_MSG_BUFFERED) CXO_ADD_INT_CONSTANT("MSG_PERSISTENT_OR_BUFFERED", DPI_MODE_MSG_PERSISTENT_OR_BUFFERED) // add constants for AQ message state CXO_ADD_INT_CONSTANT("MSG_EXPIRED", DPI_MSG_STATE_EXPIRED) CXO_ADD_INT_CONSTANT("MSG_READY", DPI_MSG_STATE_READY) CXO_ADD_INT_CONSTANT("MSG_PROCESSED", DPI_MSG_STATE_PROCESSED) CXO_ADD_INT_CONSTANT("MSG_WAITING", DPI_MSG_STATE_WAITING) // add special constants for AQ delay/expiration CXO_ADD_INT_CONSTANT("MSG_NO_DELAY", 0) CXO_ADD_INT_CONSTANT("MSG_NO_EXPIRATION", -1) return module; } //----------------------------------------------------------------------------- // Start routine for the module. //----------------------------------------------------------------------------- PyMODINIT_FUNC PyInit_cx_Oracle(void) { return cxoModule_initialize(); } python-cx_Oracle-8.3.0/src/cxoModule.h000066400000000000000000000423431414105416400176450ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. // // Licensed under BSD license (see LICENSE.txt). //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoModule.h // Include file for all cx_Oracle source files. //----------------------------------------------------------------------------- #define PY_SSIZE_T_CLEAN 1 #include #include #include #include // define macros to get the build version as a string and the driver name #define xstr(s) str(s) #define str(s) #s #define CXO_BUILD_VERSION_STRING xstr(CXO_BUILD_VERSION) #define CXO_DRIVER_NAME "cx_Oracle : "CXO_BUILD_VERSION_STRING // define macro for clearing buffers #define cxoBuffer_clear(buf) Py_CLEAR((buf)->obj) //----------------------------------------------------------------------------- // Forward Declarations //----------------------------------------------------------------------------- typedef struct cxoApiType cxoApiType; typedef struct cxoBuffer cxoBuffer; typedef struct cxoConnection cxoConnection; typedef struct cxoCursor cxoCursor; typedef struct cxoDbType cxoDbType; typedef struct cxoDeqOptions cxoDeqOptions; typedef struct cxoEnqOptions cxoEnqOptions; typedef struct cxoError cxoError; typedef struct cxoFuture cxoFuture; typedef struct cxoJsonBuffer cxoJsonBuffer; typedef struct cxoLob cxoLob; typedef struct cxoMessage cxoMessage; typedef struct cxoMessageQuery cxoMessageQuery; typedef struct cxoMessageRow cxoMessageRow; typedef struct cxoMessageTable cxoMessageTable; typedef struct cxoMsgProps cxoMsgProps; typedef struct cxoObject cxoObject; typedef struct cxoObjectAttr cxoObjectAttr; typedef struct cxoObjectType cxoObjectType; typedef struct cxoQueue cxoQueue; typedef struct cxoSessionPool cxoSessionPool; typedef struct cxoSodaCollection cxoSodaCollection; typedef struct cxoSodaDatabase cxoSodaDatabase; typedef struct cxoSodaDoc cxoSodaDoc; typedef struct cxoSodaDocCursor cxoSodaDocCursor; typedef struct cxoSodaOperation cxoSodaOperation; typedef struct cxoSubscr cxoSubscr; typedef struct cxoVar cxoVar; //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- // exception objects extern PyObject *cxoWarningException; extern PyObject *cxoErrorException; extern PyObject *cxoInterfaceErrorException; extern PyObject *cxoDatabaseErrorException; extern PyObject *cxoDataErrorException; extern PyObject *cxoOperationalErrorException; extern PyObject *cxoIntegrityErrorException; extern PyObject *cxoInternalErrorException; extern PyObject *cxoProgrammingErrorException; extern PyObject *cxoNotSupportedErrorException; // type objects extern PyTypeObject cxoPyTypeApiType; extern PyTypeObject cxoPyTypeConnection; extern PyTypeObject cxoPyTypeCursor; extern PyTypeObject cxoPyTypeDbType; extern PyTypeObject cxoPyTypeDeqOptions; extern PyTypeObject cxoPyTypeEnqOptions; extern PyTypeObject cxoPyTypeError; extern PyTypeObject cxoPyTypeFuture; extern PyTypeObject cxoPyTypeLob; extern PyTypeObject cxoPyTypeMsgProps; extern PyTypeObject cxoPyTypeMessage; extern PyTypeObject cxoPyTypeMessageQuery; extern PyTypeObject cxoPyTypeMessageRow; extern PyTypeObject cxoPyTypeMessageTable; extern PyTypeObject cxoPyTypeObject; extern PyTypeObject cxoPyTypeObjectAttr; extern PyTypeObject cxoPyTypeObjectType; extern PyTypeObject cxoPyTypeQueue; extern PyTypeObject cxoPyTypeSessionPool; extern PyTypeObject cxoPyTypeSodaCollection; extern PyTypeObject cxoPyTypeSodaDatabase; extern PyTypeObject cxoPyTypeSodaDoc; extern PyTypeObject cxoPyTypeSodaDocCursor; extern PyTypeObject cxoPyTypeSodaOperation; extern PyTypeObject cxoPyTypeSubscr; extern PyTypeObject cxoPyTypeVar; // datetime types extern PyTypeObject *cxoPyTypeDate; extern PyTypeObject *cxoPyTypeDateTime; // database types extern cxoDbType *cxoDbTypeBfile; extern cxoDbType *cxoDbTypeBinaryDouble; extern cxoDbType *cxoDbTypeBinaryFloat; extern cxoDbType *cxoDbTypeBinaryInteger; extern cxoDbType *cxoDbTypeBlob; extern cxoDbType *cxoDbTypeBoolean; extern cxoDbType *cxoDbTypeChar; extern cxoDbType *cxoDbTypeClob; extern cxoDbType *cxoDbTypeCursor; extern cxoDbType *cxoDbTypeDate; extern cxoDbType *cxoDbTypeIntervalDS; extern cxoDbType *cxoDbTypeIntervalYM; extern cxoDbType *cxoDbTypeJson; extern cxoDbType *cxoDbTypeLong; extern cxoDbType *cxoDbTypeLongRaw; extern cxoDbType *cxoDbTypeNchar; extern cxoDbType *cxoDbTypeNclob; extern cxoDbType *cxoDbTypeNumber; extern cxoDbType *cxoDbTypeNvarchar; extern cxoDbType *cxoDbTypeObject; extern cxoDbType *cxoDbTypeRaw; extern cxoDbType *cxoDbTypeRowid; extern cxoDbType *cxoDbTypeTimestamp; extern cxoDbType *cxoDbTypeTimestampLTZ; extern cxoDbType *cxoDbTypeTimestampTZ; extern cxoDbType *cxoDbTypeVarchar; // database API types extern cxoApiType *cxoApiTypeBinary; extern cxoApiType *cxoApiTypeDatetime; extern cxoApiType *cxoApiTypeNumber; extern cxoApiType *cxoApiTypeRowid; extern cxoApiType *cxoApiTypeString; // JSON dump and load functions for use with SODA extern PyObject *cxoJsonDumpFunction; extern PyObject *cxoJsonLoadFunction; // ODPI-C context and version information extern dpiContext *cxoDpiContext; extern dpiVersionInfo cxoClientVersionInfo; // future object extern cxoFuture *cxoFutureObj; //----------------------------------------------------------------------------- // Enumerations //----------------------------------------------------------------------------- typedef enum { CXO_TRANSFORM_NONE = 0, CXO_TRANSFORM_BINARY, CXO_TRANSFORM_BFILE, CXO_TRANSFORM_BLOB, CXO_TRANSFORM_BOOLEAN, CXO_TRANSFORM_CLOB, CXO_TRANSFORM_CURSOR, CXO_TRANSFORM_DATE, CXO_TRANSFORM_DATETIME, CXO_TRANSFORM_DECIMAL, CXO_TRANSFORM_FIXED_CHAR, CXO_TRANSFORM_FIXED_NCHAR, CXO_TRANSFORM_FLOAT, CXO_TRANSFORM_INT, CXO_TRANSFORM_LONG_BINARY, CXO_TRANSFORM_LONG_STRING, CXO_TRANSFORM_NATIVE_DOUBLE, CXO_TRANSFORM_NATIVE_FLOAT, CXO_TRANSFORM_NATIVE_INT, CXO_TRANSFORM_NCLOB, CXO_TRANSFORM_NSTRING, CXO_TRANSFORM_OBJECT, CXO_TRANSFORM_ROWID, CXO_TRANSFORM_STRING, CXO_TRANSFORM_TIMEDELTA, CXO_TRANSFORM_TIMESTAMP, CXO_TRANSFORM_TIMESTAMP_LTZ, CXO_TRANSFORM_TIMESTAMP_TZ, CXO_TRANSFORM_JSON, CXO_TRANSFORM_UNSUPPORTED } cxoTransformNum; typedef enum { CXO_OCI_ATTR_TYPE_STRING = 1, CXO_OCI_ATTR_TYPE_BOOLEAN = 2, CXO_OCI_ATTR_TYPE_UINT8 = 8, CXO_OCI_ATTR_TYPE_UINT16 = 16, CXO_OCI_ATTR_TYPE_UINT32 = 32, CXO_OCI_ATTR_TYPE_UINT64 = 64 } cxoOciAttrType; //----------------------------------------------------------------------------- // Structures //----------------------------------------------------------------------------- struct cxoApiType { PyObject_HEAD const char *name; PyObject *dbTypes; cxoTransformNum defaultTransformNum; }; struct cxoBuffer { const char *ptr; uint32_t numCharacters; uint32_t size; PyObject *obj; }; struct cxoError { PyObject_HEAD long code; unsigned offset; PyObject *message; PyObject *context; char isRecoverable; }; struct cxoConnection { PyObject_HEAD dpiConn *handle; cxoSessionPool *sessionPool; PyObject *inputTypeHandler; PyObject *outputTypeHandler; PyObject *username; PyObject *dsn; PyObject *version; PyObject *tag; dpiEncodingInfo encodingInfo; int autocommit; }; struct cxoCursor { PyObject_HEAD dpiStmt *handle; dpiStmtInfo stmtInfo; cxoConnection *connection; PyObject *statement; PyObject *statementTag; PyObject *bindVariables; PyObject *fetchVariables; PyObject *rowFactory; PyObject *inputTypeHandler; PyObject *outputTypeHandler; uint32_t arraySize; uint32_t bindArraySize; uint32_t fetchArraySize; uint32_t prefetchRows; int setInputSizes; uint64_t rowCount; uint32_t fetchBufferRowIndex; uint32_t numRowsInFetchBuffer; int moreRowsToFetch; char isScrollable; int fixupRefCursor; int isOpen; }; struct cxoDbType { PyObject_HEAD uint32_t num; const char *name; cxoTransformNum defaultTransformNum; }; struct cxoDeqOptions { PyObject_HEAD dpiDeqOptions *handle; const char *encoding; }; struct cxoEnqOptions { PyObject_HEAD dpiEnqOptions *handle; const char *encoding; }; struct cxoFuture { PyObject_HEAD }; struct cxoJsonBuffer { dpiJsonNode topNode; dpiDataBuffer topNodeBuffer; uint32_t allocatedBuffers; uint32_t numBuffers; cxoBuffer *buffers; }; struct cxoLob { PyObject_HEAD cxoConnection *connection; cxoDbType *dbType; dpiLob *handle; }; struct cxoMessage { PyObject_HEAD cxoSubscr *subscription; dpiEventType type; PyObject *dbname; PyObject *txId; PyObject *tables; PyObject *queries; PyObject *queueName; PyObject *consumerName; int registered; }; struct cxoMessageQuery { PyObject_HEAD uint64_t id; dpiOpCode operation; PyObject *tables; }; struct cxoMessageRow { PyObject_HEAD PyObject *rowid; dpiOpCode operation; }; struct cxoMessageTable { PyObject_HEAD PyObject *name; PyObject *rows; dpiOpCode operation; }; struct cxoMsgProps { PyObject_HEAD dpiMsgProps *handle; PyObject *payload; const char *encoding; }; struct cxoObject { PyObject_HEAD cxoObjectType *objectType; dpiObject *handle; }; struct cxoObjectAttr { PyObject_HEAD PyObject *name; dpiObjectAttr *handle; dpiOracleTypeNum oracleTypeNum; cxoTransformNum transformNum; cxoObjectType *objectType; cxoDbType *dbType; }; struct cxoObjectType { PyObject_HEAD dpiObjectType *handle; PyObject *schema; PyObject *name; PyObject *attributes; PyObject *attributesByName; cxoConnection *connection; dpiOracleTypeNum elementOracleTypeNum; cxoTransformNum elementTransformNum; cxoObjectType *elementObjectType; cxoDbType *elementDbType; char isCollection; }; struct cxoQueue { PyObject_HEAD cxoConnection *conn; dpiQueue *handle; PyObject *name; PyObject *deqOptions; PyObject *enqOptions; cxoObjectType *payloadType; }; struct cxoSessionPool { PyObject_HEAD dpiPool *handle; uint32_t minSessions; uint32_t maxSessions; uint32_t sessionIncrement; uint32_t cacheSize; dpiEncodingInfo encodingInfo; int homogeneous; int externalAuth; PyObject *username; PyObject *dsn; PyObject *name; PyObject *sessionCallback; PyTypeObject *connectionType; }; struct cxoSodaCollection { PyObject_HEAD dpiSodaColl *handle; cxoSodaDatabase *db; PyObject *name; }; struct cxoSodaDatabase { PyObject_HEAD dpiSodaDb *handle; cxoConnection *connection; }; struct cxoSodaDoc { PyObject_HEAD cxoSodaDatabase *db; dpiSodaDoc *handle; }; struct cxoSodaDocCursor { PyObject_HEAD cxoSodaDatabase *db; dpiSodaDocCursor *handle; }; struct cxoSodaOperation { PyObject_HEAD cxoSodaCollection *coll; dpiSodaOperOptions options; uint32_t numKeyBuffers; cxoBuffer *keyBuffers; cxoBuffer keyBuffer; cxoBuffer versionBuffer; cxoBuffer filterBuffer; cxoBuffer hintBuffer; }; struct cxoSubscr { PyObject_HEAD dpiSubscr *handle; cxoConnection *connection; PyObject *callback; uint32_t namespace; PyObject *name; uint32_t protocol; PyObject *ipAddress; uint32_t port; uint32_t timeout; uint32_t operations; uint32_t qos; uint8_t groupingClass; uint32_t groupingValue; uint8_t groupingType; uint64_t id; }; struct cxoVar { PyObject_HEAD dpiVar *handle; dpiData *data; cxoConnection *connection; PyObject *inConverter; PyObject *outConverter; cxoObjectType *objectType; const char *encodingErrors; uint32_t allocatedElements; uint32_t size; uint32_t bufferSize; int isArray; int isValueSet; int getReturnedData; cxoTransformNum transformNum; dpiNativeTypeNum nativeTypeNum; cxoDbType *dbType; }; //----------------------------------------------------------------------------- // Functions //----------------------------------------------------------------------------- int cxoBuffer_fromObject(cxoBuffer *buf, PyObject *obj, const char *encoding); int cxoBuffer_init(cxoBuffer *buf); int cxoConnection_getSodaFlags(cxoConnection *conn, uint32_t *flags); int cxoConnection_isConnected(cxoConnection *conn); int cxoCursor_performBind(cxoCursor *cursor); int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters, unsigned numElements, unsigned arrayPos, int deferTypeAssignment); cxoDbType *cxoDbType_fromDataTypeInfo(dpiDataTypeInfo *info); cxoDbType *cxoDbType_fromTransformNum(cxoTransformNum transformNum); cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection, dpiDeqOptions *handle); cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection, dpiEnqOptions *handle); cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo); int cxoError_raiseAndReturnInt(void); PyObject *cxoError_raiseAndReturnNull(void); int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo); PyObject *cxoError_raiseFromString(PyObject *exceptionType, const char *message); void cxoJsonBuffer_free(cxoJsonBuffer *buf); int cxoJsonBuffer_fromObject(cxoJsonBuffer *buf, PyObject *obj); PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType, dpiLob *handle); cxoMsgProps *cxoMsgProps_new(cxoConnection*, dpiMsgProps *handle); int cxoObject_internalExtend(cxoObject *obj, PyObject *sequence); PyObject *cxoObject_new(cxoObjectType *objectType, dpiObject *handle); cxoObjectAttr *cxoObjectAttr_new(cxoConnection *connection, dpiObjectAttr *handle); cxoObjectType *cxoObjectType_new(cxoConnection *connection, dpiObjectType *handle); cxoObjectType *cxoObjectType_newByName(cxoConnection *connection, PyObject *name); cxoQueue *cxoQueue_new(cxoConnection *conn, dpiQueue *handle); cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db, dpiSodaColl *handle); cxoSodaDatabase *cxoSodaDatabase_new(cxoConnection *connection); cxoSodaDoc *cxoSodaDoc_new(cxoSodaDatabase *db, dpiSodaDoc *handle); cxoSodaDocCursor *cxoSodaDocCursor_new(cxoSodaDatabase *db, dpiSodaDocCursor *handle); cxoSodaOperation *cxoSodaOperation_new(cxoSodaCollection *collection); void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message); PyObject *cxoTransform_dateFromTicks(PyObject *args); int cxoTransform_fromPython(cxoTransformNum transformNum, dpiNativeTypeNum *nativeTypeNum, PyObject *pyValue, dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding, const char *nencoding, cxoVar *var, uint32_t arrayPos); uint32_t cxoTransform_getDefaultSize(cxoTransformNum transformNum); cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info); cxoTransformNum cxoTransform_getNumFromPythonValue(PyObject *value, int plsql); int cxoTransform_getNumFromType(PyObject *type, cxoTransformNum *transformNum, cxoObjectType **objType); int cxoTransform_getNumFromValue(PyObject *value, int *isArray, Py_ssize_t *size, Py_ssize_t *numElements, int plsql, cxoTransformNum *transformNum); void cxoTransform_getTypeInfo(cxoTransformNum transformNum, dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum); int cxoTransform_init(void); PyObject *cxoTransform_timestampFromTicks(PyObject *args); PyObject *cxoTransform_toPython(cxoTransformNum transformNum, cxoConnection *connection, cxoObjectType *objType, dpiDataBuffer *dbValue, const char *encodingErrors); PyObject *cxoUtils_convertOciAttrToPythonValue(unsigned attrType, dpiDataBuffer *value, uint32_t valueLength, const char *encoding); int cxoUtils_convertPythonValueToOciAttr(PyObject *value, unsigned attrType, cxoBuffer *buffer, dpiDataBuffer *ociBuffer, void **ociValue, uint32_t *ociValueLength, const char *encoding); PyObject *cxoUtils_formatString(const char *format, PyObject *args); const char *cxoUtils_getAdjustedEncoding(const char *encoding); int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module, PyObject **name); int cxoUtils_initializeDPI(dpiContextCreateParams *params); int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer); int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg, dpiSodaDoc **handle); int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos); int cxoVar_check(PyObject *object); PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos); PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos); cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoTransformNum transformNum, Py_ssize_t size, int isArray, cxoObjectType *objType); cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value, uint32_t numElements); cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value, Py_ssize_t numElements); int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value); python-cx_Oracle-8.3.0/src/cxoMsgProps.c000066400000000000000000000302341414105416400201610ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoMsgProps.c // Implements the message properties object used in Advanced Queuing. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoMsgProps_new() // Create a new message properties object. //----------------------------------------------------------------------------- cxoMsgProps *cxoMsgProps_new(cxoConnection *connection, dpiMsgProps *handle) { cxoMsgProps *props; props = (cxoMsgProps*) cxoPyTypeMsgProps.tp_alloc(&cxoPyTypeMsgProps, 0); if (!props) { if (handle) dpiMsgProps_release(handle); return NULL; } if (!handle && dpiConn_newMsgProps(connection->handle, &handle) < 0) { Py_DECREF(props); cxoError_raiseAndReturnNull(); return NULL; } props->handle = handle; props->encoding = connection->encodingInfo.encoding; return props; } //----------------------------------------------------------------------------- // cxoMsgProps_free() // Free the memory associated with the message properties object. //----------------------------------------------------------------------------- static void cxoMsgProps_free(cxoMsgProps *props) { if (props->handle) { dpiMsgProps_release(props->handle); props->handle = NULL; } Py_CLEAR(props->payload); Py_TYPE(props)->tp_free((PyObject*) props); } //----------------------------------------------------------------------------- // cxoMsgProps_getAttrInt32() // Get the value of the attribute as a 32-bit integer. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getAttrInt32(cxoMsgProps *props, int (*func)(dpiMsgProps *props, int32_t *value)) { int32_t value; if ((*func)(props->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoMsgProps_setAttrInt32() // Set the value of the attribute as a 32-bit integer. //----------------------------------------------------------------------------- static int cxoMsgProps_setAttrInt32(cxoMsgProps *props, PyObject *valueObj, int (*func)(dpiMsgProps *props, int32_t value)) { int32_t value; value = PyLong_AsLong(valueObj); if (PyErr_Occurred()) return -1; if ((*func)(props->handle, value) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoMsgProps_getNumAttempts() // Get the value of the attempts property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getNumAttempts(cxoMsgProps *props, void *unused) { return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getNumAttempts); } //----------------------------------------------------------------------------- // cxoMsgProps_getCorrelation() // Get the value of the correlation property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getCorrelation(cxoMsgProps *props, void *unused) { uint32_t valueLength; const char *value; if (dpiMsgProps_getCorrelation(props->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (!value) Py_RETURN_NONE; return PyUnicode_Decode(value, valueLength, props->encoding, NULL); } //----------------------------------------------------------------------------- // cxoMsgProps_getDelay() // Get the value of the delay property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getDelay(cxoMsgProps *props, void *unused) { return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getDelay); } //----------------------------------------------------------------------------- // cxoMsgProps_getDeliveryMode() // Get the value of the delivery mode property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getDeliveryMode(cxoMsgProps *props, void *unused) { dpiMessageDeliveryMode value; if (dpiMsgProps_getDeliveryMode(props->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoMsgProps_getEnqTime() // Get the value of the enqueue time property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getEnqTime(cxoMsgProps *props, void *unused) { dpiDataBuffer buffer; if (dpiMsgProps_getEnqTime(props->handle, &buffer.asTimestamp) < 0) return cxoError_raiseAndReturnNull(); return cxoTransform_toPython(CXO_TRANSFORM_DATETIME, NULL, NULL, &buffer, NULL); } //----------------------------------------------------------------------------- // cxoMsgProps_getExceptionQ() // Get the value of the exception queue property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getExceptionQ(cxoMsgProps *props, void *unused) { uint32_t valueLength; const char *value; if (dpiMsgProps_getExceptionQ(props->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (!value) Py_RETURN_NONE; return PyUnicode_Decode(value, valueLength, props->encoding, NULL); } //----------------------------------------------------------------------------- // cxoMsgProps_getExpiration() // Get the value of the expiration property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getExpiration(cxoMsgProps *props, void *unused) { return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getExpiration); } //----------------------------------------------------------------------------- // cxoMsgProps_getMsgId() // Get the value of the msgid property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getMsgId(cxoMsgProps *props, void *unused) { uint32_t valueLength; const char *value; if (dpiMsgProps_getMsgId(props->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (!value) Py_RETURN_NONE; return PyBytes_FromStringAndSize(value, valueLength); } //----------------------------------------------------------------------------- // cxoMsgProps_getPriority() // Get the value of the priority property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getPriority(cxoMsgProps *props, void *unused) { return cxoMsgProps_getAttrInt32(props, dpiMsgProps_getPriority); } //----------------------------------------------------------------------------- // cxoMsgProps_getState() // Get the value of the state property. //----------------------------------------------------------------------------- static PyObject *cxoMsgProps_getState(cxoMsgProps *props, void *unused) { dpiMessageState value; if (dpiMsgProps_getState(props->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoMsgProps_setCorrelation() // Set the value of the correlation property. //----------------------------------------------------------------------------- static int cxoMsgProps_setCorrelation(cxoMsgProps *props, PyObject *valueObj, void *unused) { cxoBuffer buffer; int status; if (cxoBuffer_fromObject(&buffer, valueObj, props->encoding)) return -1; status = dpiMsgProps_setCorrelation(props->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoMsgProps_setDelay() // Set the value of the delay property. //----------------------------------------------------------------------------- static int cxoMsgProps_setDelay(cxoMsgProps *props, PyObject *valueObj, void *unused) { return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setDelay); } //----------------------------------------------------------------------------- // cxoMsgProps_setExceptionQ() // Set the value of the exception queue property. //----------------------------------------------------------------------------- static int cxoMsgProps_setExceptionQ(cxoMsgProps *props, PyObject *valueObj, void *unused) { cxoBuffer buffer; int status; if (cxoBuffer_fromObject(&buffer, valueObj, props->encoding)) return -1; status = dpiMsgProps_setExceptionQ(props->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoMsgProps_setExpiration() // Set the value of the expiration property. //----------------------------------------------------------------------------- static int cxoMsgProps_setExpiration(cxoMsgProps *props, PyObject *valueObj, void *unused) { return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setExpiration); } //----------------------------------------------------------------------------- // cxoMsgProps_setPriority() // Set the value of the expiration property. //----------------------------------------------------------------------------- static int cxoMsgProps_setPriority(cxoMsgProps *props, PyObject *valueObj, void *unused) { return cxoMsgProps_setAttrInt32(props, valueObj, dpiMsgProps_setPriority); } //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "payload", T_OBJECT, offsetof(cxoMsgProps, payload), 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "attempts", (getter) cxoMsgProps_getNumAttempts, 0, 0, 0 }, { "correlation", (getter) cxoMsgProps_getCorrelation, (setter) cxoMsgProps_setCorrelation, 0, 0 }, { "delay", (getter) cxoMsgProps_getDelay, (setter) cxoMsgProps_setDelay, 0, 0 }, { "deliverymode", (getter) cxoMsgProps_getDeliveryMode, 0, 0, 0 }, { "enqtime", (getter) cxoMsgProps_getEnqTime, 0, 0, 0 }, { "exceptionq", (getter) cxoMsgProps_getExceptionQ, (setter) cxoMsgProps_setExceptionQ, 0, 0 }, { "expiration", (getter) cxoMsgProps_getExpiration, (setter) cxoMsgProps_setExpiration, 0, 0 }, { "msgid", (getter) cxoMsgProps_getMsgId, 0, 0, 0 }, { "priority", (getter) cxoMsgProps_getPriority, (setter) cxoMsgProps_setPriority, 0, 0 }, { "state", (getter) cxoMsgProps_getState, 0, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // Python type declarations //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeMsgProps = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.MessageProperties", .tp_basicsize = sizeof(cxoMsgProps), .tp_dealloc = (destructor) cxoMsgProps_free, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers }; python-cx_Oracle-8.3.0/src/cxoObject.c000066400000000000000000000550231414105416400176200ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoObject.c // Defines the routines for handling objects in Oracle. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoObject_new() // Create a new object. //----------------------------------------------------------------------------- PyObject *cxoObject_new(cxoObjectType *objectType, dpiObject *handle) { cxoObject *obj; obj = (cxoObject*) cxoPyTypeObject.tp_alloc(&cxoPyTypeObject, 0); if (!obj) return NULL; Py_INCREF(objectType); obj->objectType = objectType; obj->handle = handle; return (PyObject*) obj; } //----------------------------------------------------------------------------- // cxoObject_free() // Free an object. //----------------------------------------------------------------------------- static void cxoObject_free(cxoObject *obj) { if (obj->handle) { dpiObject_release(obj->handle); obj->handle = NULL; } Py_CLEAR(obj->objectType); Py_TYPE(obj)->tp_free((PyObject*) obj); } //----------------------------------------------------------------------------- // cxoObject_repr() // Return a string representation of the object. //----------------------------------------------------------------------------- static PyObject *cxoObject_repr(cxoObject *obj) { PyObject *module, *name, *result; if (cxoUtils_getModuleAndName(Py_TYPE(obj), &module, &name) < 0) return NULL; result = cxoUtils_formatString("<%s.%s %s.%s at %#x>", Py_BuildValue("(OOOOl)", module, name, obj->objectType->schema, obj->objectType->name, obj)); Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // cxoObject_convertFromPython() // Convert a Python value to an Oracle value. //----------------------------------------------------------------------------- static int cxoObject_convertFromPython(cxoObject *obj, PyObject *value, cxoTransformNum transformNum, dpiNativeTypeNum *nativeTypeNum, dpiData *data, cxoBuffer *buffer) { dpiOracleTypeNum oracleTypeNum; // None is treated as null if (value == Py_None) { data->isNull = 1; return 0; } // convert the different Python types cxoTransform_getTypeInfo(transformNum, &oracleTypeNum, nativeTypeNum); if (cxoTransform_fromPython(transformNum, nativeTypeNum, value, &data->value, buffer, obj->objectType->connection->encodingInfo.encoding, obj->objectType->connection->encodingInfo.nencoding, NULL, 0) < 0) return -1; data->isNull = 0; return 0; } //----------------------------------------------------------------------------- // cxoObject_convertToPython() // Convert an Oracle value to a Python value. //----------------------------------------------------------------------------- static PyObject *cxoObject_convertToPython(cxoObject *obj, cxoTransformNum transformNum, dpiData *data, cxoObjectType *objType) { if (data->isNull) Py_RETURN_NONE; return cxoTransform_toPython(transformNum, obj->objectType->connection, objType, &data->value, NULL); } //----------------------------------------------------------------------------- // cxoObject_getAttributeValue() // Retrieve an attribute on the object. //----------------------------------------------------------------------------- static PyObject *cxoObject_getAttributeValue(cxoObject *obj, cxoObjectAttr *attribute) { char numberAsStringBuffer[200], message[120]; dpiOracleTypeNum oracleTypeNum; dpiNativeTypeNum nativeTypeNum; dpiData data; if (attribute->transformNum == CXO_TRANSFORM_UNSUPPORTED) { snprintf(message, sizeof(message), "Oracle type %d not supported.", attribute->oracleTypeNum); return cxoError_raiseFromString(cxoNotSupportedErrorException, message); } cxoTransform_getTypeInfo(attribute->transformNum, &oracleTypeNum, &nativeTypeNum); if (oracleTypeNum == DPI_ORACLE_TYPE_NUMBER && nativeTypeNum == DPI_NATIVE_TYPE_BYTES) { data.value.asBytes.ptr = numberAsStringBuffer; data.value.asBytes.length = sizeof(numberAsStringBuffer); data.value.asBytes.encoding = NULL; } if (dpiObject_getAttributeValue(obj->handle, attribute->handle, nativeTypeNum, &data) < 0) return cxoError_raiseAndReturnNull(); return cxoObject_convertToPython(obj, attribute->transformNum, &data, attribute->objectType); } //----------------------------------------------------------------------------- // cxoObject_setAttributeValue() // Set an attribute on the object. //----------------------------------------------------------------------------- static int cxoObject_setAttributeValue(cxoObject *obj, cxoObjectAttr *attribute, PyObject *value) { dpiNativeTypeNum nativeTypeNum = 0; cxoBuffer buffer; dpiData data; int status; cxoBuffer_init(&buffer); if (cxoObject_convertFromPython(obj, value, attribute->transformNum, &nativeTypeNum, &data, &buffer) < 0) return -1; status = dpiObject_setAttributeValue(obj->handle, attribute->handle, nativeTypeNum, &data); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoObject_getAttr() // Retrieve an attribute on an object. //----------------------------------------------------------------------------- static PyObject *cxoObject_getAttr(cxoObject *obj, PyObject *nameObject) { cxoObjectAttr *attribute; attribute = (cxoObjectAttr*) PyDict_GetItem(obj->objectType->attributesByName, nameObject); if (attribute) return cxoObject_getAttributeValue(obj, attribute); return PyObject_GenericGetAttr( (PyObject*) obj, nameObject); } //----------------------------------------------------------------------------- // cxoObject_setAttr() // Set an attribute on an object. //----------------------------------------------------------------------------- static int cxoObject_setAttr(cxoObject *obj, PyObject *nameObject, PyObject *value) { cxoObjectAttr *attribute; attribute = (cxoObjectAttr*) PyDict_GetItem(obj->objectType->attributesByName, nameObject); if (attribute) return cxoObject_setAttributeValue(obj, attribute, value); return PyObject_GenericSetAttr( (PyObject*) obj, nameObject, value); } //----------------------------------------------------------------------------- // cxoObject_internalAppend() // Append an item to the collection. //----------------------------------------------------------------------------- static int cxoObject_internalAppend(cxoObject *obj, PyObject *value) { dpiNativeTypeNum nativeTypeNum = 0; cxoBuffer buffer; dpiData data; int status; cxoBuffer_init(&buffer); if (cxoObject_convertFromPython(obj, value, obj->objectType->elementTransformNum, &nativeTypeNum, &data, &buffer) < 0) return -1; status = dpiObject_appendElement(obj->handle, nativeTypeNum, &data); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoObject_internalExtend() // Extend the collection by appending each of the items in the sequence. //----------------------------------------------------------------------------- int cxoObject_internalExtend(cxoObject *obj, PyObject *sequence) { PyObject *fastSequence, *element; Py_ssize_t size, i; fastSequence = PySequence_Fast(sequence, "expecting sequence"); if (!fastSequence) return -1; size = PySequence_Fast_GET_SIZE(fastSequence); for (i = 0; i < size; i++) { element = PySequence_Fast_GET_ITEM(fastSequence, i); if (cxoObject_internalAppend(obj, element) < 0) { Py_DECREF(fastSequence); return -1; } } Py_DECREF(fastSequence); return 0; } //----------------------------------------------------------------------------- // cxoObject_append() // Append an item to the collection. //----------------------------------------------------------------------------- static PyObject *cxoObject_append(cxoObject *obj, PyObject *value) { if (cxoObject_internalAppend(obj, value) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_internalGetElementByIndex() // Internal method used for getting an element value for a particular index. //----------------------------------------------------------------------------- static PyObject *cxoObject_internalGetElementByIndex(cxoObject *obj, int32_t index) { char numberAsStringBuffer[200], message[120]; dpiOracleTypeNum oracleTypeNum; dpiNativeTypeNum nativeTypeNum; dpiData data; if (obj->objectType->elementTransformNum == CXO_TRANSFORM_UNSUPPORTED) { snprintf(message, sizeof(message), "Oracle type %d not supported.", obj->objectType->elementOracleTypeNum); return cxoError_raiseFromString(cxoNotSupportedErrorException, message); } cxoTransform_getTypeInfo(obj->objectType->elementTransformNum, &oracleTypeNum, &nativeTypeNum); if (oracleTypeNum == DPI_ORACLE_TYPE_NUMBER && nativeTypeNum == DPI_NATIVE_TYPE_BYTES) { data.value.asBytes.ptr = numberAsStringBuffer; data.value.asBytes.length = sizeof(numberAsStringBuffer); data.value.asBytes.encoding = NULL; } if (dpiObject_getElementValueByIndex(obj->handle, index, nativeTypeNum, &data) < 0) return cxoError_raiseAndReturnNull(); return cxoObject_convertToPython(obj, obj->objectType->elementTransformNum, &data, obj->objectType->elementObjectType); } //----------------------------------------------------------------------------- // cxoObject_asDict() // Returns a collection as a dictionary. If the object is not a collection, // an error is returned. //----------------------------------------------------------------------------- static PyObject *cxoObject_asDict(cxoObject *obj, PyObject *args) { PyObject *dict, *key, *value; int32_t index, nextIndex; int exists; // create the result dictionary dict = PyDict_New(); if (!dict) return NULL; // populate it with each of the elements in the collection if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) { Py_DECREF(dict); return cxoError_raiseAndReturnNull(); } while (exists) { value = cxoObject_internalGetElementByIndex(obj, index); if (!value) { Py_DECREF(dict); return NULL; } key = PyLong_FromLong(index); if (!key) { Py_DECREF(value); Py_DECREF(dict); return NULL; } if (PyDict_SetItem(dict, key, value) < 0) { Py_DECREF(key); Py_DECREF(value); Py_DECREF(dict); return NULL; } Py_DECREF(key); Py_DECREF(value); if (dpiObject_getNextIndex(obj->handle, index, &nextIndex, &exists) < 0) { Py_DECREF(dict); return cxoError_raiseAndReturnNull(); } index = nextIndex; } return dict; } //----------------------------------------------------------------------------- // cxoObject_asList() // Returns a collection as a list of elements. If the object is not a // collection, an error is returned. //----------------------------------------------------------------------------- static PyObject *cxoObject_asList(cxoObject *obj, PyObject *args) { PyObject *list, *elementValue; int32_t index, nextIndex; int exists; // create the result list list = PyList_New(0); if (!list) return NULL; // populate it with each of the elements in the list if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) { Py_DECREF(list); return cxoError_raiseAndReturnNull(); } while (exists) { elementValue = cxoObject_internalGetElementByIndex(obj, index); if (!elementValue) { Py_DECREF(list); return NULL; } if (PyList_Append(list, elementValue) < 0) { Py_DECREF(elementValue); Py_DECREF(list); return NULL; } Py_DECREF(elementValue); if (dpiObject_getNextIndex(obj->handle, index, &nextIndex, &exists) < 0) { Py_DECREF(list); return cxoError_raiseAndReturnNull(); } index = nextIndex; } return list; } //----------------------------------------------------------------------------- // cxoObject_copy() // Return a copy of the object. //----------------------------------------------------------------------------- static PyObject *cxoObject_copy(cxoObject *obj, PyObject *args) { PyObject *copiedObj; dpiObject *handle; if (dpiObject_copy(obj->handle, &handle) < 0) return cxoError_raiseAndReturnNull(); copiedObj = cxoObject_new(obj->objectType, handle); if (!copiedObj) { dpiObject_release(handle); return NULL; } return copiedObj; } //----------------------------------------------------------------------------- // cxoObject_delete() // Delete the element at the specified index in the collection. //----------------------------------------------------------------------------- static PyObject *cxoObject_delete(cxoObject *obj, PyObject *args) { int32_t index; if (!PyArg_ParseTuple(args, "i", &index)) return NULL; if (dpiObject_deleteElementByIndex(obj->handle, index) < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_exists() // Return true or false indicating if an element exists in the collection at // the specified index. //----------------------------------------------------------------------------- static PyObject *cxoObject_exists(cxoObject *obj, PyObject *args) { int32_t index; int exists; if (!PyArg_ParseTuple(args, "i", &index)) return NULL; if (dpiObject_getElementExistsByIndex(obj->handle, index, &exists) < 0) return cxoError_raiseAndReturnNull(); if (exists) Py_RETURN_TRUE; Py_RETURN_FALSE; } //----------------------------------------------------------------------------- // cxoObject_extend() // Extend the collection by appending each of the items in the sequence. //----------------------------------------------------------------------------- static PyObject *cxoObject_extend(cxoObject *obj, PyObject *sequence) { if (cxoObject_internalExtend(obj, sequence) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_getElement() // Return the element at the given position in the collection. //----------------------------------------------------------------------------- static PyObject *cxoObject_getElement(cxoObject *obj, PyObject *args) { int32_t index; if (!PyArg_ParseTuple(args, "i", &index)) return NULL; return cxoObject_internalGetElementByIndex(obj, index); } //----------------------------------------------------------------------------- // cxoObject_getFirstIndex() // Return the index of the first entry in the collection. //----------------------------------------------------------------------------- static PyObject *cxoObject_getFirstIndex(cxoObject *obj, PyObject *args) { int32_t index; int exists; if (dpiObject_getFirstIndex(obj->handle, &index, &exists) < 0) return cxoError_raiseAndReturnNull(); if (exists) return PyLong_FromLong(index); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_getLastIndex() // Return the index of the last entry in the collection. //----------------------------------------------------------------------------- static PyObject *cxoObject_getLastIndex(cxoObject *obj, PyObject *args) { int32_t index; int exists; if (dpiObject_getLastIndex(obj->handle, &index, &exists) < 0) return cxoError_raiseAndReturnNull(); if (exists) return PyLong_FromLong(index); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_getNextIndex() // Return the index of the next entry in the collection following the index // specified. If there is no next entry, None is returned. //----------------------------------------------------------------------------- static PyObject *cxoObject_getNextIndex(cxoObject *obj, PyObject *args) { int32_t index, nextIndex; int exists; if (!PyArg_ParseTuple(args, "i", &index)) return NULL; if (dpiObject_getNextIndex(obj->handle, index, &nextIndex, &exists) < 0) return cxoError_raiseAndReturnNull(); if (exists) return PyLong_FromLong(nextIndex); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_getPrevIndex() // Return the index of the previous entry in the collection preceding the // index specified. If there is no previous entry, None is returned. //----------------------------------------------------------------------------- static PyObject *cxoObject_getPrevIndex(cxoObject *obj, PyObject *args) { int32_t index, prevIndex; int exists; if (!PyArg_ParseTuple(args, "i", &index)) return NULL; if (dpiObject_getPrevIndex(obj->handle, index, &prevIndex, &exists) < 0) return cxoError_raiseAndReturnNull(); if (exists) return PyLong_FromLong(prevIndex); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_getSize() // Return the size of a collection. If the object is not a collection, an // error is returned. //----------------------------------------------------------------------------- static PyObject *cxoObject_getSize(cxoObject *obj, PyObject *args) { int32_t size; if (dpiObject_getSize(obj->handle, &size) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(size); } //----------------------------------------------------------------------------- // cxoObject_setElement() // Set the element at the specified location to the given value. //----------------------------------------------------------------------------- static PyObject *cxoObject_setElement(cxoObject *obj, PyObject *args) { dpiNativeTypeNum nativeTypeNum = 0; cxoBuffer buffer; PyObject *value; int32_t index; dpiData data; int status; if (!PyArg_ParseTuple(args, "iO", &index, &value)) return NULL; cxoBuffer_init(&buffer); if (cxoObject_convertFromPython(obj, value, obj->objectType->elementTransformNum, &nativeTypeNum, &data, &buffer) < 0) return NULL; status = dpiObject_setElementValueByIndex(obj->handle, index, nativeTypeNum, &data); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObject_trim() // Trim a number of elements from the end of the collection. //----------------------------------------------------------------------------- static PyObject *cxoObject_trim(cxoObject *obj, PyObject *args) { int32_t numToTrim; if (!PyArg_ParseTuple(args, "i", &numToTrim)) return NULL; if (dpiObject_trim(obj->handle, numToTrim) < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // declaration of methods for Python type //----------------------------------------------------------------------------- static PyMethodDef cxoObjectMethods[] = { { "append", (PyCFunction) cxoObject_append, METH_O }, { "asdict", (PyCFunction) cxoObject_asDict, METH_NOARGS }, { "aslist", (PyCFunction) cxoObject_asList, METH_NOARGS }, { "copy", (PyCFunction) cxoObject_copy, METH_NOARGS }, { "delete", (PyCFunction) cxoObject_delete, METH_VARARGS }, { "exists", (PyCFunction) cxoObject_exists, METH_VARARGS }, { "extend", (PyCFunction) cxoObject_extend, METH_O }, { "first", (PyCFunction) cxoObject_getFirstIndex, METH_NOARGS }, { "getelement", (PyCFunction) cxoObject_getElement, METH_VARARGS }, { "last", (PyCFunction) cxoObject_getLastIndex, METH_NOARGS }, { "next", (PyCFunction) cxoObject_getNextIndex, METH_VARARGS }, { "prev", (PyCFunction) cxoObject_getPrevIndex, METH_VARARGS }, { "setelement", (PyCFunction) cxoObject_setElement, METH_VARARGS }, { "size", (PyCFunction) cxoObject_getSize, METH_NOARGS }, { "trim", (PyCFunction) cxoObject_trim, METH_VARARGS }, { NULL, NULL } }; //----------------------------------------------------------------------------- // Declaration of members for Python type //----------------------------------------------------------------------------- static PyMemberDef cxoObjectMembers[] = { { "type", T_OBJECT, offsetof(cxoObject, objectType), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // Python type declaration //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeObject = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.Object", .tp_basicsize = sizeof(cxoObject), .tp_dealloc = (destructor) cxoObject_free, .tp_repr = (reprfunc) cxoObject_repr, .tp_getattro = (getattrofunc) cxoObject_getAttr, .tp_setattro = (setattrofunc) cxoObject_setAttr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoObjectMethods, .tp_members = cxoObjectMembers }; python-cx_Oracle-8.3.0/src/cxoObjectAttr.c000066400000000000000000000120561414105416400204520ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoObjectAttr.c // Defines the routines for handling attributes of Oracle types. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoObjectAttr_initialize() // Initialize the new object attribute. //----------------------------------------------------------------------------- static int cxoObjectAttr_initialize(cxoObjectAttr *attr, cxoConnection *connection) { dpiObjectAttrInfo info; if (dpiObjectAttr_getInfo(attr->handle, &info) < 0) return cxoError_raiseAndReturnInt(); attr->transformNum = cxoTransform_getNumFromDataTypeInfo(&info.typeInfo); attr->dbType = cxoDbType_fromTransformNum(attr->transformNum); if (!attr->dbType) return -1; Py_INCREF(attr->dbType); attr->oracleTypeNum = info.typeInfo.oracleTypeNum; attr->name = PyUnicode_Decode(info.name, info.nameLength, connection->encodingInfo.encoding, NULL); if (!attr->name) return -1; if (info.typeInfo.objectType) { attr->objectType = cxoObjectType_new(connection, info.typeInfo.objectType); if (!attr->objectType) return -1; } return 0; } //----------------------------------------------------------------------------- // cxoObjectAttr_new() // Allocate a new object attribute. //----------------------------------------------------------------------------- cxoObjectAttr *cxoObjectAttr_new(cxoConnection *connection, dpiObjectAttr *handle) { cxoObjectAttr *attr; attr = (cxoObjectAttr*) cxoPyTypeObjectAttr.tp_alloc(&cxoPyTypeObjectAttr, 0); if (!attr) { dpiObjectAttr_release(handle); return NULL; } attr->handle = handle; if (cxoObjectAttr_initialize(attr, connection) < 0) { Py_DECREF(attr); return NULL; } return attr; } //----------------------------------------------------------------------------- // cxoObjectAttr_free() // Free the memory associated with an object attribute. //----------------------------------------------------------------------------- static void cxoObjectAttr_free(cxoObjectAttr *attr) { if (attr->handle) { dpiObjectAttr_release(attr->handle); attr->handle = NULL; } Py_CLEAR(attr->name); Py_CLEAR(attr->objectType); Py_CLEAR(attr->dbType); Py_TYPE(attr)->tp_free((PyObject*) attr); } //----------------------------------------------------------------------------- // cxoObjectAttr_getType() // Return the type associated with the attribute. This is either an object // type or one of the database type constants. //----------------------------------------------------------------------------- static PyObject *cxoObjectAttr_getType(cxoObjectAttr *attr, void *unused) { if (attr->objectType) { Py_INCREF(attr->objectType); return (PyObject*) attr->objectType; } Py_INCREF(attr->dbType); return (PyObject*) attr->dbType; } //----------------------------------------------------------------------------- // cxoObjectAttr_repr() // Return a string representation of the object attribute. //----------------------------------------------------------------------------- static PyObject *cxoObjectAttr_repr(cxoObjectAttr *attr) { PyObject *module, *name, *result; if (cxoUtils_getModuleAndName(Py_TYPE(attr), &module, &name) < 0) return NULL; result = cxoUtils_formatString("<%s.%s %s>", PyTuple_Pack(3, module, name, attr->name)); Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "name", T_OBJECT, offsetof(cxoObjectAttr, name), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "type", (getter) cxoObjectAttr_getType, 0, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // Python type declaration //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeObjectAttr = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.ObjectAttribute", .tp_basicsize = sizeof(cxoObjectAttr), .tp_dealloc = (destructor) cxoObjectAttr_free, .tp_repr = (reprfunc) cxoObjectAttr_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers }; python-cx_Oracle-8.3.0/src/cxoObjectType.c000066400000000000000000000276651414105416400204750ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoObjectType.c // Defines the routines for handling Oracle type information. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoObjectType_initialize() // Initialize the object type with the information that is required. //----------------------------------------------------------------------------- static int cxoObjectType_initialize(cxoObjectType *objType, cxoConnection *connection) { dpiObjectAttr **attributes; dpiObjectTypeInfo info; cxoObjectAttr *attr; uint16_t i; // get object type information if (dpiObjectType_getInfo(objType->handle, &info) < 0) return cxoError_raiseAndReturnInt(); Py_INCREF(connection); objType->connection = connection; objType->schema = PyUnicode_Decode(info.schema, info.schemaLength, connection->encodingInfo.encoding, NULL); if (!objType->schema) return -1; objType->name = PyUnicode_Decode(info.name, info.nameLength, connection->encodingInfo.encoding, NULL); if (!objType->name) return -1; objType->isCollection = info.isCollection; if (info.isCollection) { objType->elementOracleTypeNum = info.elementTypeInfo.oracleTypeNum; objType->elementTransformNum = cxoTransform_getNumFromDataTypeInfo(&info.elementTypeInfo); objType->elementDbType = cxoDbType_fromTransformNum(objType->elementTransformNum); if (!objType->elementDbType) return -1; Py_INCREF(objType->elementDbType); if (info.elementTypeInfo.objectType) { objType->elementObjectType = cxoObjectType_new(connection, info.elementTypeInfo.objectType); if (!objType->elementObjectType) return -1; } } // allocate the attribute list (temporary and permanent) and dictionary objType->attributes = PyList_New(info.numAttributes); if (!objType->attributes) return -1; objType->attributesByName = PyDict_New(); if (!objType->attributesByName) return -1; // get the list of attributes from DPI attributes = PyMem_Malloc(sizeof(dpiObjectAttr*) * info.numAttributes); if (!attributes) { PyErr_NoMemory(); return -1; } if (dpiObjectType_getAttributes(objType->handle, info.numAttributes, attributes) < 0) { PyMem_Free(attributes); return cxoError_raiseAndReturnInt(); } // create attribute information for each attribute for (i = 0; i < info.numAttributes; i++) { attr = cxoObjectAttr_new(connection, attributes[i]); if (!attr) { PyMem_Free(attributes); return -1; } PyList_SET_ITEM(objType->attributes, i, (PyObject*) attr); if (PyDict_SetItem(objType->attributesByName, attr->name, (PyObject*) attr) < 0) { PyMem_Free(attributes); return -1; } } PyMem_Free(attributes); return 0; } //----------------------------------------------------------------------------- // cxoObjectType_new() // Allocate a new object type. //----------------------------------------------------------------------------- cxoObjectType *cxoObjectType_new(cxoConnection *connection, dpiObjectType *handle) { cxoObjectType *objType; objType = (cxoObjectType*) cxoPyTypeObjectType.tp_alloc(&cxoPyTypeObjectType, 0); if (!objType) return NULL; if (dpiObjectType_addRef(handle) < 0) { Py_DECREF(objType); cxoError_raiseAndReturnNull(); return NULL; } objType->handle = handle; if (cxoObjectType_initialize(objType, connection) < 0) { Py_DECREF(objType); return NULL; } return objType; } //----------------------------------------------------------------------------- // cxoObjectType_newByName() // Create a new object type given its name. //----------------------------------------------------------------------------- cxoObjectType *cxoObjectType_newByName(cxoConnection *connection, PyObject *name) { cxoObjectType *objType; dpiObjectType *handle; cxoBuffer buffer; int status; if (cxoBuffer_fromObject(&buffer, name, connection->encodingInfo.encoding) < 0) return NULL; status = dpiConn_getObjectType(connection->handle, buffer.ptr, buffer.size, &handle); cxoBuffer_clear(&buffer); if (status < 0) return (cxoObjectType*) cxoError_raiseAndReturnNull(); objType = cxoObjectType_new(connection, handle); dpiObjectType_release(handle); return objType; } //----------------------------------------------------------------------------- // cxoObjectType_free() // Free the memory associated with an object type. //----------------------------------------------------------------------------- static void cxoObjectType_free(cxoObjectType *objType) { if (objType->handle) { dpiObjectType_release(objType->handle); objType->handle = NULL; } Py_CLEAR(objType->connection); Py_CLEAR(objType->schema); Py_CLEAR(objType->name); Py_CLEAR(objType->attributes); Py_CLEAR(objType->attributesByName); Py_CLEAR(objType->elementObjectType); Py_CLEAR(objType->elementDbType); Py_TYPE(objType)->tp_free((PyObject*) objType); } //----------------------------------------------------------------------------- // cxoObjectType_getElementType() // Return the element type associated with a collection. This is either an // object type or one of the database type constants. If the object type is not // a collection, None is returned. //----------------------------------------------------------------------------- static PyObject *cxoObjectType_getElementType(cxoObjectType *type, void *unused) { if (type->elementObjectType) { Py_INCREF(type->elementObjectType); return (PyObject*) type->elementObjectType; } if (type->elementDbType) { Py_INCREF(type->elementDbType); return (PyObject*) type->elementDbType; } Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoObjectType_repr() // Return a string representation of the object type. //----------------------------------------------------------------------------- static PyObject *cxoObjectType_repr(cxoObjectType *objType) { PyObject *module, *name, *result; if (cxoUtils_getModuleAndName(Py_TYPE(objType), &module, &name) < 0) return NULL; result = cxoUtils_formatString("<%s.%s %s.%s>", PyTuple_Pack(4, module, name, objType->schema, objType->name)); Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // cxoObjectType_richCompare() // Peforms a comparison between the object type and another Python object. // Equality (and inequality) are suppported to match object types; no other // operations are supported. //----------------------------------------------------------------------------- static PyObject *cxoObjectType_richCompare(cxoObjectType* objType, PyObject* otherObj, int op) { cxoObjectType *otherObjType; int status, equal = 0; // only equality and inequality can be checked if (op != Py_EQ && op != Py_NE) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } // check to see if the other object is an object type, too status = PyObject_IsInstance(otherObj, (PyObject*) &cxoPyTypeObjectType); if (status < 0) return NULL; if (status == 1) { otherObjType = (cxoObjectType*) otherObj; if (otherObjType->connection == objType->connection || otherObjType->connection->sessionPool == objType->connection->sessionPool) { equal = PyObject_RichCompareBool(otherObjType->schema, objType->schema, Py_EQ); if (equal < 0) return NULL; if (equal) { equal = PyObject_RichCompareBool(otherObjType->name, objType->name, Py_EQ); if (equal < 0) return NULL; } } } // determine return value if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } //----------------------------------------------------------------------------- // cxoObjectType_newObject() // Factory function for creating objects of the type which can be bound. //----------------------------------------------------------------------------- static PyObject *cxoObjectType_newObject(cxoObjectType *objType, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "value", NULL }; PyObject *initialValue; dpiObject *handle; cxoObject *obj; // parse arguments initialValue = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|O", keywordList, &initialValue)) return NULL; // get handle to newly created object if (dpiObjectType_createObject(objType->handle, &handle) < 0) return cxoError_raiseAndReturnNull(); // create the object obj = (cxoObject*) cxoObject_new(objType, handle); if (!obj) { dpiObject_release(handle); return NULL; } // populate collection, if applicable if (initialValue) { if (cxoObject_internalExtend(obj, initialValue) < 0) { Py_DECREF(obj); return NULL; } } return (PyObject*) obj; } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "newobject", (PyCFunction) cxoObjectType_newObject, METH_VARARGS | METH_KEYWORDS }, { NULL } }; //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "schema", T_OBJECT, offsetof(cxoObjectType, schema), READONLY }, { "name", T_OBJECT, offsetof(cxoObjectType, name), READONLY }, { "attributes", T_OBJECT, offsetof(cxoObjectType, attributes), READONLY }, { "iscollection", T_BOOL, offsetof(cxoObjectType, isCollection), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "element_type", (getter) cxoObjectType_getElementType, 0, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // Python type declarations //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeObjectType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.ObjectType", .tp_basicsize = sizeof(cxoObjectType), .tp_dealloc = (destructor) cxoObjectType_free, .tp_repr = (reprfunc) cxoObjectType_repr, .tp_call = (ternaryfunc) cxoObjectType_newObject, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoMethods, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers, .tp_richcompare = (richcmpfunc) cxoObjectType_richCompare }; python-cx_Oracle-8.3.0/src/cxoQueue.c000066400000000000000000000315121414105416400174730ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoQueue.c // Defines the routines for handling queues (advanced queuing). These queues // permit sending and receiving messages defined by the database. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoQueue_new() // Create a new queue (advanced queuing). //----------------------------------------------------------------------------- cxoQueue *cxoQueue_new(cxoConnection *conn, dpiQueue *handle) { dpiDeqOptions *deqOptions; dpiEnqOptions *enqOptions; cxoQueue *queue; // create queue and populate basic attributes queue = (cxoQueue*) cxoPyTypeQueue.tp_alloc(&cxoPyTypeQueue, 0); if (!queue) { dpiQueue_release(handle); return NULL; } Py_INCREF(conn); queue->conn = conn; queue->handle = handle; // get dequeue options if (dpiQueue_getDeqOptions(queue->handle, &deqOptions) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(queue); return NULL; } queue->deqOptions = (PyObject*) cxoDeqOptions_new(conn, deqOptions); if (!queue->deqOptions) { Py_DECREF(queue); return NULL; } // get enqueue options if (dpiQueue_getEnqOptions(queue->handle, &enqOptions) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(queue); return NULL; } queue->enqOptions = (PyObject*) cxoEnqOptions_new(conn, enqOptions); if (!queue->enqOptions) { Py_DECREF(queue); return NULL; } return queue; } //----------------------------------------------------------------------------- // cxoQueue_free() // Free the memory associated with a queue. //----------------------------------------------------------------------------- static void cxoQueue_free(cxoQueue *queue) { if (queue->handle) { dpiQueue_release(queue->handle); queue->handle = NULL; } Py_CLEAR(queue->conn); Py_CLEAR(queue->name); Py_CLEAR(queue->payloadType); Py_CLEAR(queue->deqOptions); Py_CLEAR(queue->enqOptions); Py_TYPE(queue)->tp_free((PyObject*) queue); } //----------------------------------------------------------------------------- // cxoQueue_repr() // Return a string representation of a queue. //----------------------------------------------------------------------------- static PyObject *cxoQueue_repr(cxoQueue *queue) { PyObject *module, *name, *result; if (cxoUtils_getModuleAndName(Py_TYPE(queue), &module, &name) < 0) return NULL; result = cxoUtils_formatString("<%s.%s %r>", PyTuple_Pack(3, module, name, queue->name)); Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // cxoQueue_deqHelper() // Helper for dequeuing messages from a queue. //----------------------------------------------------------------------------- int cxoQueue_deqHelper(cxoQueue *queue, uint32_t *numProps, cxoMsgProps **props) { uint32_t bufferLength, i, j; dpiMsgProps **handles; dpiObject *objHandle; const char *buffer; cxoMsgProps *temp; cxoObject *obj; int ok, status; // use the same array to store the intermediate values provided by ODPI-C; // by doing so there is no need to allocate an additional array and any // values created by this helper routine are cleaned up on error handles = (dpiMsgProps**) props; // perform dequeue Py_BEGIN_ALLOW_THREADS status = dpiQueue_deqMany(queue->handle, numProps, handles); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnInt(); // create objects that are returned to the user for (i = 0; i < *numProps; i++) { // create message property object temp = cxoMsgProps_new(queue->conn, handles[i]); ok = (temp) ? 1 : 0; props[i] = temp; // get payload from ODPI-C message property if (ok && dpiMsgProps_getPayload(temp->handle, &objHandle, &buffer, &bufferLength) < 0) { cxoError_raiseAndReturnInt(); ok = 0; } // store payload on cx_Oracle message property if (ok && objHandle) { obj = (cxoObject*) cxoObject_new(queue->payloadType, objHandle); if (obj && dpiObject_addRef(objHandle) < 0) { cxoError_raiseAndReturnInt(); obj->handle = NULL; Py_CLEAR(obj); ok = 0; } temp->payload = (PyObject*) obj; } else if (ok) { temp->payload = PyBytes_FromStringAndSize(buffer, bufferLength); } // if an error occurred, do some cleanup if (!ok || !temp->payload) { Py_XDECREF(temp); for (j = 0; j < i; j++) Py_DECREF(props[j]); for (j = i + 1; j < *numProps; j++) dpiMsgProps_release(handles[j]); return -1; } } return 0; } //----------------------------------------------------------------------------- // cxoQueue_enqHelper() // Helper for enqueuing messages from a queue. //----------------------------------------------------------------------------- int cxoQueue_enqHelper(cxoQueue *queue, uint32_t numProps, cxoMsgProps **props) { dpiMsgProps **handles, *tempHandle; cxoBuffer buffer; cxoObject *obj; uint32_t i; int status; // use the same array to store the intermediate values required by ODPI-C; // by doing so there is no need to allocate an additional array handles = (dpiMsgProps**) props; // process array for (i = 0; i < numProps; i++) { // verify that the message property object has a payload if (!props[i]->payload || props[i]->payload == Py_None) { cxoError_raiseFromString(cxoProgrammingErrorException, "message has no payload"); return -1; } // transfer payload to message properties object tempHandle = props[i]->handle; if (PyObject_IsInstance(props[i]->payload, (PyObject*) &cxoPyTypeObject)) { obj = (cxoObject*) props[i]->payload; if (dpiMsgProps_setPayloadObject(props[i]->handle, obj->handle) < 0) return cxoError_raiseAndReturnInt(); } else { if (cxoBuffer_fromObject(&buffer, props[i]->payload, props[i]->encoding) < 0) return -1; status = dpiMsgProps_setPayloadBytes(props[i]->handle, buffer.ptr, buffer.size); cxoBuffer_clear(&buffer); if (status < 0) return cxoError_raiseAndReturnInt(); } handles[i] = tempHandle; } // perform enqueue Py_BEGIN_ALLOW_THREADS status = dpiQueue_enqMany(queue->handle, numProps, handles); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoQueue_deqMany() // Dequeue a single message to the queue. //----------------------------------------------------------------------------- static PyObject *cxoQueue_deqMany(cxoQueue *queue, PyObject *args) { unsigned int numPropsFromPython; uint32_t numProps, i; cxoMsgProps **props; PyObject *result; if (!PyArg_ParseTuple(args, "I", &numPropsFromPython)) return NULL; numProps = (uint32_t) numPropsFromPython; props = PyMem_Malloc(numProps * sizeof(cxoMsgProps*)); if (!props) return NULL; if (cxoQueue_deqHelper(queue, &numProps, props) < 0) { PyMem_Free(props); return NULL; } result = PyList_New(numProps); if (!result) { for (i = 0; i < numProps; i++) Py_DECREF(props[i]); PyMem_Free(props); return NULL; } for (i = 0; i < numProps; i++) PyList_SET_ITEM(result, i, (PyObject*) props[i]); PyMem_Free(props); return result; } //----------------------------------------------------------------------------- // cxoQueue_deqOne() // Dequeue a single message to the queue. //----------------------------------------------------------------------------- static PyObject *cxoQueue_deqOne(cxoQueue *queue, PyObject *args) { uint32_t numProps = 1; cxoMsgProps *props; if (cxoQueue_deqHelper(queue, &numProps, &props) < 0) return NULL; if (numProps > 0) return (PyObject*) props; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoQueue_enqMany() // Enqueue multiple messages to the queue. //----------------------------------------------------------------------------- static PyObject *cxoQueue_enqMany(cxoQueue *queue, PyObject *args) { PyObject *seq, *seqCheck, *temp; Py_ssize_t seqLength, i; cxoMsgProps **props; int status; // validate arguments if (!PyArg_ParseTuple(args, "O", &seqCheck)) return NULL; seq = PySequence_Fast(seqCheck, "expecting sequence"); if (!seq) return NULL; // zero messages means nothing to do seqLength = PySequence_Length(seq); if (seqLength == 0) { Py_DECREF(seq); Py_RETURN_NONE; } // populate array of properties props = PyMem_Malloc(seqLength * sizeof(cxoMsgProps*)); if (!props) { PyErr_NoMemory(); Py_DECREF(seq); return NULL; } for (i = 0; i < seqLength; i++) { temp = PySequence_Fast_GET_ITEM(seq, i); if (Py_TYPE(temp) != &cxoPyTypeMsgProps) { Py_DECREF(seq); PyMem_Free(props); PyErr_SetString(PyExc_TypeError, "expecting sequence of message property objects"); return NULL; } props[i] = (cxoMsgProps*) temp; } // perform enqueue status = cxoQueue_enqHelper(queue, (uint32_t) seqLength, props); Py_DECREF(seq); PyMem_Free(props); if (status < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoQueue_enqOne() // Enqueue a single message to the queue. //----------------------------------------------------------------------------- static PyObject *cxoQueue_enqOne(cxoQueue *queue, PyObject *args) { cxoMsgProps *props; if (!PyArg_ParseTuple(args, "O!", &cxoPyTypeMsgProps, &props)) return NULL; if (cxoQueue_enqHelper(queue, 1, &props) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "deqmany", (PyCFunction) cxoQueue_deqMany, METH_VARARGS }, { "deqone", (PyCFunction) cxoQueue_deqOne, METH_NOARGS }, { "enqmany", (PyCFunction) cxoQueue_enqMany, METH_VARARGS }, { "enqone", (PyCFunction) cxoQueue_enqOne, METH_VARARGS }, { "deqMany", (PyCFunction) cxoQueue_deqMany, METH_VARARGS }, { "deqOne", (PyCFunction) cxoQueue_deqOne, METH_NOARGS }, { "enqMany", (PyCFunction) cxoQueue_enqMany, METH_VARARGS }, { "enqOne", (PyCFunction) cxoQueue_enqOne, METH_VARARGS }, { NULL } }; //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "connection", T_OBJECT, offsetof(cxoQueue, conn), READONLY }, { "deqoptions", T_OBJECT, offsetof(cxoQueue, deqOptions), READONLY }, { "enqoptions", T_OBJECT, offsetof(cxoQueue, enqOptions), READONLY }, { "name", T_OBJECT, offsetof(cxoQueue, name), READONLY }, { "payload_type", T_OBJECT, offsetof(cxoQueue, payloadType), READONLY }, { "deqOptions", T_OBJECT, offsetof(cxoQueue, deqOptions), READONLY }, { "enqOptions", T_OBJECT, offsetof(cxoQueue, enqOptions), READONLY }, { "payloadType", T_OBJECT, offsetof(cxoQueue, payloadType), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // Python type declarations //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeQueue = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.Queue", .tp_basicsize = sizeof(cxoQueue), .tp_dealloc = (destructor) cxoQueue_free, .tp_repr = (reprfunc) cxoQueue_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoMethods, .tp_members = cxoMembers }; python-cx_Oracle-8.3.0/src/cxoSessionPool.c000066400000000000000000000773011414105416400206720ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoSessionPool.c // Handles session pooling. //----------------------------------------------------------------------------- #include "cxoModule.h" // forward declarations int cxoSessionPool_reconfigureHelper(cxoSessionPool *pool, const char *attrName, PyObject *value); //----------------------------------------------------------------------------- // cxoSessionPool_new() // Create a new session pool object. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_new(PyTypeObject *type, PyObject *args, PyObject *keywordArgs) { return type->tp_alloc(type, 0); } //----------------------------------------------------------------------------- // cxoSessionPool_init() // Initialize the session pool object. //----------------------------------------------------------------------------- static int cxoSessionPool_init(cxoSessionPool *pool, PyObject *args, PyObject *keywordArgs) { uint32_t minSessions, maxSessions, sessionIncrement, maxSessionsPerShard; uint32_t waitTimeoutDeprecated, maxSessionsPerShardDeprecated; cxoBuffer userNameBuffer, passwordBuffer, dsnBuffer, editionBuffer; PyObject *usernameObj, *dsnObj, *sessionCallbackObj, *passwordObj; PyObject *editionObj, *sessionCallbackObjDeprecated; dpiCommonCreateParams dpiCommonParams; uint32_t maxLifetimeSessionDeprecated; dpiPoolCreateParams dpiCreateParams; cxoBuffer sessionCallbackBuffer; int status, threaded, events; PyTypeObject *connectionType; unsigned int stmtCacheSize; const char *encoding; // define keyword arguments static char *keywordList[] = { "user", "password", "dsn", "min", "max", "increment", "connectiontype", "threaded", "getmode", "events", "homogeneous", "externalauth", "encoding", "nencoding", "edition", "timeout", "wait_timeout", "max_lifetime_session", "session_callback", "max_sessions_per_shard", "soda_metadata_cache", "stmtcachesize", "ping_interval", "waitTimeout", "maxLifetimeSession", "sessionCallback", "maxSessionsPerShard", NULL }; // parse arguments and keywords usernameObj = passwordObj = dsnObj = editionObj = Py_None; sessionCallbackObj = sessionCallbackObjDeprecated = passwordObj = NULL; connectionType = &cxoPyTypeConnection; minSessions = 1; maxSessions = 2; sessionIncrement = 1; maxSessionsPerShard = maxSessionsPerShardDeprecated = 0; waitTimeoutDeprecated = maxLifetimeSessionDeprecated = 0; maxSessionsPerShardDeprecated = 0; stmtCacheSize = DPI_DEFAULT_STMT_CACHE_SIZE; if (cxoUtils_initializeDPI(NULL) < 0) return -1; if (dpiContext_initCommonCreateParams(cxoDpiContext, &dpiCommonParams) < 0) return cxoError_raiseAndReturnInt(); if (dpiContext_initPoolCreateParams(cxoDpiContext, &dpiCreateParams) < 0) return cxoError_raiseAndReturnInt(); if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OOOiiiOpbpppssOiiiOipIiiiOi", keywordList, &usernameObj, &passwordObj, &dsnObj, &minSessions, &maxSessions, &sessionIncrement, &connectionType, &threaded, &dpiCreateParams.getMode, &events, &dpiCreateParams.homogeneous, &dpiCreateParams.externalAuth, &dpiCommonParams.encoding, &dpiCommonParams.nencoding, &editionObj, &dpiCreateParams.timeout, &dpiCreateParams.waitTimeout, &dpiCreateParams.maxLifetimeSession, &sessionCallbackObj, &maxSessionsPerShard, &dpiCommonParams.sodaMetadataCache, &stmtCacheSize, &dpiCreateParams.pingInterval, &waitTimeoutDeprecated, &maxLifetimeSessionDeprecated, &sessionCallbackObjDeprecated, &maxSessionsPerShardDeprecated)) return -1; if (!PyType_Check(connectionType)) { cxoError_raiseFromString(cxoProgrammingErrorException, "connectiontype must be a type"); return -1; } if (!PyType_IsSubtype(connectionType, &cxoPyTypeConnection)) { cxoError_raiseFromString(cxoProgrammingErrorException, "connectiontype must be a subclass of Connection"); return -1; } if (threaded) dpiCommonParams.createMode |= DPI_MODE_CREATE_THREADED; if (events) dpiCommonParams.createMode |= DPI_MODE_CREATE_EVENTS; // check duplicate parameters to ensure that both are not specified if (waitTimeoutDeprecated > 0) { if (dpiCreateParams.waitTimeout > 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "waitTimeout and wait_timeout cannot both be specified"); return -1; } dpiCreateParams.waitTimeout = waitTimeoutDeprecated; } if (maxLifetimeSessionDeprecated > 0) { if (dpiCreateParams.maxLifetimeSession > 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "maxLifetimeSession and max_lifetime_session cannot both " "be specified"); return -1; } dpiCreateParams.maxLifetimeSession = maxLifetimeSessionDeprecated; } if (sessionCallbackObjDeprecated) { if (sessionCallbackObj > 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "sessionCallback and session_callback cannot both " "be specified"); return -1; } sessionCallbackObj = sessionCallbackObjDeprecated; } if (maxSessionsPerShardDeprecated > 0) { if (maxSessionsPerShard > 0) { cxoError_raiseFromString(cxoProgrammingErrorException, "maxSessionsPerShard and max_sessions_per_shard cannot " "both be specified"); return -1; } maxSessionsPerShard = maxSessionsPerShardDeprecated; } // initialize the object's members Py_INCREF(connectionType); pool->connectionType = connectionType; Py_INCREF(dsnObj); pool->dsn = dsnObj; Py_INCREF(usernameObj); pool->username = usernameObj; pool->minSessions = minSessions; pool->maxSessions = maxSessions; pool->sessionIncrement = sessionIncrement; pool->homogeneous = dpiCreateParams.homogeneous; pool->externalAuth = dpiCreateParams.externalAuth; Py_XINCREF(sessionCallbackObj); pool->sessionCallback = sessionCallbackObj; // populate parameters encoding = cxoUtils_getAdjustedEncoding(dpiCommonParams.encoding); cxoBuffer_init(&userNameBuffer); cxoBuffer_init(&passwordBuffer); cxoBuffer_init(&dsnBuffer); cxoBuffer_init(&editionBuffer); cxoBuffer_init(&sessionCallbackBuffer); if (sessionCallbackObj && !PyCallable_Check(sessionCallbackObj) && cxoBuffer_fromObject(&sessionCallbackBuffer, sessionCallbackObj, encoding) < 0) return -1; if (cxoBuffer_fromObject(&userNameBuffer, usernameObj, encoding) < 0 || cxoBuffer_fromObject(&passwordBuffer, passwordObj, encoding) < 0 || cxoBuffer_fromObject(&dsnBuffer, dsnObj, encoding) < 0 || cxoBuffer_fromObject(&editionBuffer, editionObj, encoding) < 0) { cxoBuffer_clear(&userNameBuffer); cxoBuffer_clear(&passwordBuffer); cxoBuffer_clear(&dsnBuffer); cxoBuffer_clear(&sessionCallbackBuffer); return -1; } dpiCreateParams.minSessions = minSessions; dpiCreateParams.maxSessions = maxSessions; dpiCreateParams.sessionIncrement = sessionIncrement; dpiCreateParams.plsqlFixupCallback = sessionCallbackBuffer.ptr; dpiCreateParams.plsqlFixupCallbackLength = sessionCallbackBuffer.size; dpiCreateParams.maxSessionsPerShard = maxSessionsPerShard; dpiCommonParams.edition = editionBuffer.ptr; dpiCommonParams.editionLength = editionBuffer.size; dpiCommonParams.stmtCacheSize = stmtCacheSize; // create pool Py_BEGIN_ALLOW_THREADS status = dpiPool_create(cxoDpiContext, userNameBuffer.ptr, userNameBuffer.size, passwordBuffer.ptr, passwordBuffer.size, dsnBuffer.ptr, dsnBuffer.size, &dpiCommonParams, &dpiCreateParams, &pool->handle); Py_END_ALLOW_THREADS cxoBuffer_clear(&userNameBuffer); cxoBuffer_clear(&passwordBuffer); cxoBuffer_clear(&dsnBuffer); cxoBuffer_clear(&editionBuffer); if (status < 0) return cxoError_raiseAndReturnInt(); // get encodings and name if (dpiPool_getEncodingInfo(pool->handle, &pool->encodingInfo) < 0) return cxoError_raiseAndReturnInt(); pool->encodingInfo.encoding = cxoUtils_getAdjustedEncoding(pool->encodingInfo.encoding); pool->encodingInfo.nencoding = cxoUtils_getAdjustedEncoding(pool->encodingInfo.nencoding); pool->name = PyUnicode_Decode(dpiCreateParams.outPoolName, dpiCreateParams.outPoolNameLength, pool->encodingInfo.encoding, NULL); if (!pool->name) return -1; return 0; } //----------------------------------------------------------------------------- // cxoSessionPool_free() // Deallocate the session pool. //----------------------------------------------------------------------------- static void cxoSessionPool_free(cxoSessionPool *pool) { if (pool->handle) { dpiPool_release(pool->handle); pool->handle = NULL; } Py_CLEAR(pool->username); Py_CLEAR(pool->dsn); Py_CLEAR(pool->name); Py_CLEAR(pool->sessionCallback); Py_TYPE(pool)->tp_free((PyObject*) pool); } //----------------------------------------------------------------------------- // cxoSessionPool_acquire() // Create a new connection within the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_acquire(cxoSessionPool *pool, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "user", "password", "cclass", "purity", "tag", "matchanytag", "shardingkey", "supershardingkey", NULL }; PyObject *createKeywordArgs, *result, *cclassObj, *purityObj, *tagObj; PyObject *shardingKeyObj, *superShardingKeyObj; Py_ssize_t usernameLength, passwordLength; char *username, *password; PyObject *matchAnyTagObj; // parse arguments if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|s#s#OOOOOO", keywordList, &username, &usernameLength, &password, &passwordLength, &cclassObj, &purityObj, &tagObj, &matchAnyTagObj, &shardingKeyObj, &superShardingKeyObj)) return NULL; // create arguments if (keywordArgs) createKeywordArgs = PyDict_Copy(keywordArgs); else createKeywordArgs = PyDict_New(); if (!createKeywordArgs) return NULL; if (PyDict_SetItemString(createKeywordArgs, "pool", (PyObject*) pool) < 0) { Py_DECREF(createKeywordArgs); return NULL; } // create the connection object result = PyObject_Call( (PyObject*) pool->connectionType, args, createKeywordArgs); Py_DECREF(createKeywordArgs); return result; } //----------------------------------------------------------------------------- // cxoSessionPool_close() // Close the session pool and make it unusable. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_close(cxoSessionPool *pool, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "force", NULL }; uint32_t closeMode; int status, force; // parse arguments force = 0; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|p", keywordList, &force)) return NULL; closeMode = (force) ? DPI_MODE_POOL_CLOSE_FORCE : DPI_MODE_POOL_CLOSE_DEFAULT; // close pool Py_BEGIN_ALLOW_THREADS status = dpiPool_close(pool->handle, closeMode); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSessionPool_drop() // Release a connection back to the session pool, dropping it so that a new // connection will be created if needed. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_drop(cxoSessionPool *pool, PyObject *args) { cxoConnection *connection; int status; // connection is expected if (!PyArg_ParseTuple(args, "O!", &cxoPyTypeConnection, &connection)) return NULL; // release the connection Py_BEGIN_ALLOW_THREADS status = dpiConn_close(connection->handle, DPI_MODE_CONN_CLOSE_DROP, NULL, 0); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); // mark connection as closed Py_CLEAR(connection->sessionPool); dpiConn_release(connection->handle); connection->handle = NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSessionPool_reconfigure() // Reconfigure properties of the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_reconfigure(cxoSessionPool *pool, PyObject *args, PyObject *keywordArgs) { PyObject *timeout, *waitTimeout, *maxLifetimeSession, *maxSessionsPerShard; PyObject *sodaMetadataCache, *stmtcachesize, *pingInterval, *getMode; uint32_t minSessions, maxSessions, sessionIncrement; // define keyword arguments static char *keywordList[] = { "min", "max", "increment", "getmode", "timeout", "wait_timeout", "max_lifetime_session", "max_sessions_per_shard", "soda_metadata_cache", "stmtcachesize", "ping_interval", NULL }; // set up default values minSessions = pool->minSessions; maxSessions = pool->maxSessions; sessionIncrement = pool->sessionIncrement; timeout = waitTimeout = maxLifetimeSession = maxSessionsPerShard = NULL; sodaMetadataCache = stmtcachesize = pingInterval = getMode = NULL; // parse arguments and keywords if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|iiiOOOOOOOO", keywordList, &minSessions, &maxSessions, &sessionIncrement, &getMode, &timeout, &waitTimeout, &maxLifetimeSession, &maxSessionsPerShard, &sodaMetadataCache, &stmtcachesize, &pingInterval)) return NULL; // perform reconfiguration of the pool itself if needed if (minSessions != pool->minSessions || maxSessions != pool->maxSessions || sessionIncrement != pool->sessionIncrement) { if (dpiPool_reconfigure(pool->handle, minSessions, maxSessions, sessionIncrement) < 0) return cxoError_raiseAndReturnNull(); pool->minSessions = minSessions; pool->maxSessions = maxSessions; pool->sessionIncrement = sessionIncrement; } // adjust attributes if (cxoSessionPool_reconfigureHelper(pool, "getmode", getMode) < 0) return NULL; if (cxoSessionPool_reconfigureHelper(pool, "timeout", timeout) < 0) return NULL; if (cxoSessionPool_reconfigureHelper(pool, "wait_timeout", waitTimeout) < 0) return NULL; if (cxoSessionPool_reconfigureHelper(pool, "max_lifetime_session", maxLifetimeSession) < 0) return NULL; if (cxoSessionPool_reconfigureHelper(pool, "max_sessions_per_shard", maxSessionsPerShard) < 0) return NULL; if (cxoSessionPool_reconfigureHelper(pool, "soda_metadata_cache", sodaMetadataCache) < 0) return NULL; if (cxoSessionPool_reconfigureHelper(pool, "stmtcachesize", stmtcachesize) < 0) return NULL; if (cxoSessionPool_reconfigureHelper(pool, "ping_interval", pingInterval) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSessionPool_reconfigureHelpe() // Helper function that calls the setter for the session pool's property, // after first checking that a value was supplied and not None. //----------------------------------------------------------------------------- int cxoSessionPool_reconfigureHelper(cxoSessionPool *pool, const char *attrName, PyObject *value) { if (value != NULL && value != Py_None) { if (PyObject_SetAttrString((PyObject*) pool, attrName, value) < 0) return cxoError_raiseAndReturnInt(); } return 0; } //----------------------------------------------------------------------------- // cxoSessionPool_release() // Release a connection back to the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_release(cxoSessionPool *pool, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "connection", "tag", NULL }; cxoConnection *conn; cxoBuffer tagBuffer; PyObject *tagObj; uint32_t mode; int status; // parse arguments tagObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|O", keywordList, &cxoPyTypeConnection, &conn, &tagObj)) return NULL; if (!tagObj) tagObj = conn->tag; if (cxoBuffer_fromObject(&tagBuffer, tagObj, pool->encodingInfo.encoding) < 0) return NULL; mode = DPI_MODE_CONN_CLOSE_DEFAULT; if (tagObj && tagObj != Py_None) mode |= DPI_MODE_CONN_CLOSE_RETAG; Py_BEGIN_ALLOW_THREADS status = dpiConn_close(conn->handle, mode, (char*) tagBuffer.ptr, tagBuffer.size); Py_END_ALLOW_THREADS cxoBuffer_clear(&tagBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); // mark connection as closed Py_CLEAR(conn->sessionPool); dpiConn_release(conn->handle); conn->handle = NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSessionPool_getAttribute() // Return the value for the attribute. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getAttribute(cxoSessionPool *pool, int (*func)(dpiPool *pool, uint32_t *value)) { uint32_t value; if ((*func)(pool->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromUnsignedLong(value); } //----------------------------------------------------------------------------- // cxoSessionPool_setAttribute() // Set the value of the OCI attribute. //----------------------------------------------------------------------------- static int cxoSessionPool_setAttribute(cxoSessionPool *pool, PyObject *value, int (*func)(dpiPool *pool, uint32_t value)) { uint32_t cValue; if (!PyLong_Check(value)) { PyErr_SetString(PyExc_TypeError, "value must be an integer"); return -1; } cValue = PyLong_AsUnsignedLong(value); if (PyErr_Occurred()) return -1; if ((*func)(pool->handle, cValue) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoSessionPool_getBusyCount() // Return the number of busy connections in the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getBusyCount(cxoSessionPool *pool, void *unused) { return cxoSessionPool_getAttribute(pool, dpiPool_getBusyCount); } //----------------------------------------------------------------------------- // cxoSessionPool_getGetMode() // Return the "get" mode for connections in the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getGetMode(cxoSessionPool *pool, void *unused) { dpiPoolGetMode value; if (dpiPool_getGetMode(pool->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoSessionPool_getMaxLifetimeSession() // Return the maximum lifetime session of connections in the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getMaxLifetimeSession(cxoSessionPool *pool, void *unused) { return cxoSessionPool_getAttribute(pool, dpiPool_getMaxLifetimeSession); } //----------------------------------------------------------------------------- // cxoSessionPool_getMaxSessionsPerShard() // Return the maximum sessions per shard in the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getMaxSessionsPerShard(cxoSessionPool *pool, void *unused) { return cxoSessionPool_getAttribute(pool, dpiPool_getMaxSessionsPerShard); } //----------------------------------------------------------------------------- // cxoSessionPool_getOpenCount() // Return the number of open connections in the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getOpenCount(cxoSessionPool *pool, void *unused) { return cxoSessionPool_getAttribute(pool, dpiPool_getOpenCount); } //----------------------------------------------------------------------------- // cxoSessionPool_getPingInterval() // Return the current value of the ping interval set for the pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getPingInterval(cxoSessionPool *pool, void *unused) { int value; if (dpiPool_getPingInterval(pool->handle, &value) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(value); } //----------------------------------------------------------------------------- // cxoSessionPool_getSodaMetadataCache() // Return a boolean indicating if the SODA metadata cache is enabled or not. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getSodaMetadataCache(cxoSessionPool *pool, void *unused) { int enabled; if (dpiPool_getSodaMetadataCache(pool->handle, &enabled) < 0) return cxoError_raiseAndReturnNull(); return PyBool_FromLong(enabled); } //----------------------------------------------------------------------------- // cxoSessionPool_getStmtCacheSize() // Return the size of the statement cache to use in connections that are // acquired from the pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getStmtCacheSize(cxoSessionPool *pool, void *unused) { return cxoSessionPool_getAttribute(pool, dpiPool_getStmtCacheSize); } //----------------------------------------------------------------------------- // cxoSessionPool_getTimeout() // Return the timeout for connections in the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getTimeout(cxoSessionPool *pool, void *unused) { return cxoSessionPool_getAttribute(pool, dpiPool_getTimeout); } //----------------------------------------------------------------------------- // cxoSessionPool_getWaitTimeout() // Return the wait timeout for connections in the session pool. //----------------------------------------------------------------------------- static PyObject *cxoSessionPool_getWaitTimeout(cxoSessionPool *pool, void *unused) { return cxoSessionPool_getAttribute(pool, dpiPool_getWaitTimeout); } //----------------------------------------------------------------------------- // cxoSessionPool_setGetMode() // Set the "get" mode for connections in the session pool. //----------------------------------------------------------------------------- static int cxoSessionPool_setGetMode(cxoSessionPool *pool, PyObject *value, void *unused) { dpiPoolGetMode cValue; cValue = PyLong_AsLong(value); if (PyErr_Occurred()) return -1; if (dpiPool_setGetMode(pool->handle, cValue) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoSessionPool_setMaxLifetimeSession() // Set the maximum lifetime for connections in the session pool. //----------------------------------------------------------------------------- static int cxoSessionPool_setMaxLifetimeSession(cxoSessionPool *pool, PyObject *value, void *unused) { return cxoSessionPool_setAttribute(pool, value, dpiPool_setMaxLifetimeSession); } //----------------------------------------------------------------------------- // cxoSessionPool_setMaxSessionsPerShard() // Set the maximum lifetime for connections in the session pool. //----------------------------------------------------------------------------- static int cxoSessionPool_setMaxSessionsPerShard(cxoSessionPool *pool, PyObject *value, void *unused) { return cxoSessionPool_setAttribute(pool, value, dpiPool_setMaxSessionsPerShard); } //----------------------------------------------------------------------------- // cxoSessionPool_setPingInterval() // Set the value of the OCI attribute. //----------------------------------------------------------------------------- static int cxoSessionPool_setPingInterval(cxoSessionPool *pool, PyObject *value, void *unused) { long cValue; if (!PyLong_Check(value)) { PyErr_SetString(PyExc_TypeError, "value must be an integer"); return -1; } cValue = PyLong_AsLong(value); if (PyErr_Occurred()) return -1; if (dpiPool_setPingInterval(pool->handle, (int) cValue) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoSessionPool_setSodaMetadataCache() // Set whether the SODA metadata cache is enabled or not. //----------------------------------------------------------------------------- static int cxoSessionPool_setSodaMetadataCache(cxoSessionPool *pool, PyObject *value, void *unused) { int cValue; if (value == Py_True) { cValue = 1; } else if (value == Py_False) { cValue = 0; } else { PyErr_SetString(PyExc_TypeError, "value must be a boolean"); return -1; } if (dpiPool_setSodaMetadataCache(pool->handle, cValue) < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoSessionPool_setStmtCacheSize() // Set the default size of the statement cache used for connections that are // acquired from the pool. //----------------------------------------------------------------------------- static int cxoSessionPool_setStmtCacheSize(cxoSessionPool *pool, PyObject *value, void *unused) { return cxoSessionPool_setAttribute(pool, value, dpiPool_setStmtCacheSize); } //----------------------------------------------------------------------------- // cxoSessionPool_setTimeout() // Set the timeout for connections in the session pool. //----------------------------------------------------------------------------- static int cxoSessionPool_setTimeout(cxoSessionPool *pool, PyObject *value, void *unused) { return cxoSessionPool_setAttribute(pool, value, dpiPool_setTimeout); } //----------------------------------------------------------------------------- // cxoSessionPool_setWaitTimeout() // Set the wait timeout for connections in the session pool. //----------------------------------------------------------------------------- static int cxoSessionPool_setWaitTimeout(cxoSessionPool *pool, PyObject *value, void *unused) { return cxoSessionPool_setAttribute(pool, value, dpiPool_setWaitTimeout); } //----------------------------------------------------------------------------- // declaration of methods for Python type //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "acquire", (PyCFunction) cxoSessionPool_acquire, METH_VARARGS | METH_KEYWORDS }, { "close", (PyCFunction) cxoSessionPool_close, METH_VARARGS | METH_KEYWORDS }, { "drop", (PyCFunction) cxoSessionPool_drop, METH_VARARGS }, { "reconfigure", (PyCFunction) cxoSessionPool_reconfigure, METH_VARARGS | METH_KEYWORDS }, { "release", (PyCFunction) cxoSessionPool_release, METH_VARARGS | METH_KEYWORDS }, { NULL } }; //----------------------------------------------------------------------------- // declaration of members for Python type //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "username", T_OBJECT, offsetof(cxoSessionPool, username), READONLY }, { "dsn", T_OBJECT, offsetof(cxoSessionPool, dsn), READONLY }, { "tnsentry", T_OBJECT, offsetof(cxoSessionPool, dsn), READONLY }, { "name", T_OBJECT, offsetof(cxoSessionPool, name), READONLY }, { "max", T_INT, offsetof(cxoSessionPool, maxSessions), READONLY }, { "min", T_INT, offsetof(cxoSessionPool, minSessions), READONLY }, { "increment", T_INT, offsetof(cxoSessionPool, sessionIncrement), READONLY }, { "homogeneous", T_INT, offsetof(cxoSessionPool, homogeneous), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members for Python type //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "opened", (getter) cxoSessionPool_getOpenCount, 0, 0, 0 }, { "busy", (getter) cxoSessionPool_getBusyCount, 0, 0, 0 }, { "timeout", (getter) cxoSessionPool_getTimeout, (setter) cxoSessionPool_setTimeout, 0, 0 }, { "getmode", (getter) cxoSessionPool_getGetMode, (setter) cxoSessionPool_setGetMode, 0, 0 }, { "max_lifetime_session", (getter) cxoSessionPool_getMaxLifetimeSession, (setter) cxoSessionPool_setMaxLifetimeSession, 0, 0 }, { "max_sessions_per_shard", (getter) cxoSessionPool_getMaxSessionsPerShard, (setter) cxoSessionPool_setMaxSessionsPerShard, 0, 0 }, { "ping_interval", (getter) cxoSessionPool_getPingInterval, (setter) cxoSessionPool_setPingInterval, 0, 0 }, { "soda_metadata_cache", (getter) cxoSessionPool_getSodaMetadataCache, (setter) cxoSessionPool_setSodaMetadataCache, 0, 0 }, { "stmtcachesize", (getter) cxoSessionPool_getStmtCacheSize, (setter) cxoSessionPool_setStmtCacheSize, 0, 0 }, { "wait_timeout", (getter) cxoSessionPool_getWaitTimeout, (setter) cxoSessionPool_setWaitTimeout, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeSessionPool = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.SessionPool", .tp_basicsize = sizeof(cxoSessionPool), .tp_dealloc = (destructor) cxoSessionPool_free, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_methods = cxoMethods, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers, .tp_init = (initproc) cxoSessionPool_init, .tp_new = (newfunc) cxoSessionPool_new }; python-cx_Oracle-8.3.0/src/cxoSodaCollection.c000066400000000000000000000533311414105416400213140ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoSodaCollection.c // Defines the routines for handling the SODA collection. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoSodaCollection_processOptions() // Populate the SODA operations structure with the information provided by // the user. //----------------------------------------------------------------------------- static int cxoSodaCollection_processOptions(cxoSodaCollection *coll, dpiSodaOperOptions *options, PyObject *hintObj, cxoBuffer *hintBuffer) { if (dpiContext_initSodaOperOptions(cxoDpiContext, options) < 0) return cxoError_raiseAndReturnInt(); if (cxoBuffer_fromObject(hintBuffer, hintObj, coll->db->connection->encodingInfo.encoding) < 0) return -1; options->hint = hintBuffer->ptr; options->hintLength = hintBuffer->size; return 0; } //----------------------------------------------------------------------------- // cxoSodaCollection_initialize() // Initialize a new collection with its attributes. //----------------------------------------------------------------------------- static int cxoSodaCollection_initialize(cxoSodaCollection *coll, cxoSodaDatabase *db, const char *encoding, dpiSodaColl *handle) { uint32_t nameLength; const char *name; // get name from ODPI-C if (dpiSodaColl_getName(handle, &name, &nameLength) < 0) return cxoError_raiseAndReturnInt(); coll->name = PyUnicode_Decode(name, nameLength, encoding, NULL); if (!coll->name) return -1; // set base attributes (handle should not be added until there is no // possibility of further failure) coll->handle = handle; Py_INCREF(db); coll->db = db; return 0; } //----------------------------------------------------------------------------- // cxoSodaCollection_new() // Create a new SODA collection object. //----------------------------------------------------------------------------- cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db, dpiSodaColl *handle) { cxoSodaCollection *coll; coll = (cxoSodaCollection*) cxoPyTypeSodaCollection.tp_alloc(&cxoPyTypeSodaCollection, 0); if (!coll) return NULL; if (cxoSodaCollection_initialize(coll, db, db->connection->encodingInfo.encoding, handle) < 0) { Py_DECREF(coll); return NULL; } return coll; } //----------------------------------------------------------------------------- // cxoSodaCollection_free() // Free the memory associated with a SODA collection. //----------------------------------------------------------------------------- static void cxoSodaCollection_free(cxoSodaCollection *coll) { if (coll->handle) { dpiSodaColl_release(coll->handle); coll->handle = NULL; } Py_CLEAR(coll->db); Py_CLEAR(coll->name); Py_TYPE(coll)->tp_free((PyObject*) coll); } //----------------------------------------------------------------------------- // cxoSodaCollection_repr() // Return a string representation of a SODA collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_repr(cxoSodaCollection *coll) { PyObject *module, *name, *result; if (cxoUtils_getModuleAndName(Py_TYPE(coll), &module, &name) < 0) return NULL; result = cxoUtils_formatString("<%s.%s %s>", PyTuple_Pack(3, module, name, coll->name)); Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // cxoSodaCollection_createIndex() // Create an index on a SODA collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_createIndex(cxoSodaCollection *coll, PyObject *specObj) { cxoBuffer specBuffer; uint32_t flags; int status; if (cxoUtils_processJsonArg(specObj, &specBuffer) < 0) return NULL; if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_createIndex(coll->handle, specBuffer.ptr, specBuffer.size, flags); Py_END_ALLOW_THREADS cxoBuffer_clear(&specBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaCollection_drop() // Create a SODA collection and return it. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_drop(cxoSodaCollection *coll, PyObject *args) { uint32_t flags; int isDropped; if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; if (dpiSodaColl_drop(coll->handle, flags, &isDropped) < 0) return cxoError_raiseAndReturnNull(); if (isDropped) Py_RETURN_TRUE; Py_RETURN_FALSE; } //----------------------------------------------------------------------------- // cxoSodaCollection_dropIndex() // Drop an index on a SODA collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_dropIndex(cxoSodaCollection *coll, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "name", "force", NULL }; int status, isDropped, force; cxoBuffer nameBuffer; PyObject *nameObj; uint32_t flags; // parse arguments force = 0; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|p", keywordList, &nameObj, &force)) return NULL; // drop index if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; if (force) flags |= DPI_SODA_FLAGS_INDEX_DROP_FORCE; if (cxoBuffer_fromObject(&nameBuffer, nameObj, coll->db->connection->encodingInfo.encoding) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_dropIndex(coll->handle, nameBuffer.ptr, nameBuffer.size, flags, &isDropped); Py_END_ALLOW_THREADS cxoBuffer_clear(&nameBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); if (isDropped) Py_RETURN_TRUE; Py_RETURN_FALSE; } //----------------------------------------------------------------------------- // cxoSodaCollection_find() // Creates an operation options object which can be used to perform a number // of operations on the collection using the criteria set on the object. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_find(cxoSodaCollection *coll, PyObject *args) { return (PyObject*) cxoSodaOperation_new(coll); } //----------------------------------------------------------------------------- // cxoSodaCollection_getDataGuide() // Return the data guide associated with the collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_getDataGuide(cxoSodaCollection *coll, PyObject *args) { dpiSodaDoc *handle; cxoSodaDoc *doc; uint32_t flags; int status; if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_getDataGuide(coll->handle, flags, &handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); if (handle) { doc = cxoSodaDoc_new(coll->db, handle); if (!doc) return NULL; return (PyObject*) doc; } Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaCollection_insertManyHelper() // Helper method to perform bulk insert of SODA documents into a collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_insertManyHelper(cxoSodaCollection *coll, PyObject *docs, Py_ssize_t numDocs, dpiSodaDoc **handles, dpiSodaDoc **returnHandles, dpiSodaOperOptions *options) { PyObject *element, *returnDocs; Py_ssize_t i, j; cxoSodaDoc *doc; uint32_t flags; int status; // determine flags to use if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; // populate array of document handles for (i = 0; i < numDocs; i++) { element = PyList_GET_ITEM(docs, i); if (cxoUtils_processSodaDocArg(coll->db, element, &handles[i]) < 0) { for (j = 0; j < i; j++) dpiSodaDoc_release(handles[j]); return NULL; } } // perform bulk insert Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_insertManyWithOptions(coll->handle, (uint32_t) numDocs, handles, options, flags, returnHandles); Py_END_ALLOW_THREADS if (status < 0) cxoError_raiseAndReturnNull(); for (i = 0; i < numDocs; i++) dpiSodaDoc_release(handles[i]); if (status < 0) return NULL; // if no documents are to be returned, None is returned if (!returnHandles) Py_RETURN_NONE; // otherwise, return list of documents returnDocs = PyList_New(numDocs); if (!returnDocs) { for (i = 0; i < numDocs; i++) dpiSodaDoc_release(returnHandles[i]); return NULL; } for (i = 0; i < numDocs; i++) { doc = cxoSodaDoc_new(coll->db, returnHandles[i]); if (!doc) { for (j = i; j < numDocs; j++) dpiSodaDoc_release(returnHandles[j]); Py_DECREF(returnDocs); return NULL; } PyList_SET_ITEM(returnDocs, i, (PyObject*) doc); } return returnDocs; } //----------------------------------------------------------------------------- // cxoSodaCollection_insertMany() // Inserts multilple document into the collection at one time. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_insertMany(cxoSodaCollection *coll, PyObject *arg) { dpiSodaDoc **handles; Py_ssize_t numDocs; PyObject *result; if (!PyList_Check(arg)) { PyErr_SetString(PyExc_TypeError, "expecting list"); return NULL; } numDocs = PyList_GET_SIZE(arg); handles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*)); if (!handles) { PyErr_NoMemory(); return NULL; } result = cxoSodaCollection_insertManyHelper(coll, arg, numDocs, handles, NULL, NULL); PyMem_Free(handles); return result; } //----------------------------------------------------------------------------- // cxoSodaCollection_insertManyAndGet() // Inserts multiple documents into the collection at one time and return a // list of documents containing all but the content itself. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_insertManyAndGet(cxoSodaCollection *coll, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "docs", "hint", NULL }; dpiSodaOperOptions options, *optionsPtr = NULL; dpiSodaDoc **handles, **returnHandles; PyObject *docsObj, *hintObj, *result; cxoBuffer hintBuffer; Py_ssize_t numDocs; // parse arguments docsObj = hintObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList, &docsObj, &hintObj)) return NULL; if (!PyList_Check(docsObj)) { PyErr_SetString(PyExc_TypeError, "expecting list"); return NULL; } // setup for actual work cxoBuffer_init(&hintBuffer); if (hintObj && hintObj != Py_None) { optionsPtr = &options; if (cxoSodaCollection_processOptions(coll, &options, hintObj, &hintBuffer) < 0) return NULL; } numDocs = PyList_GET_SIZE(docsObj); handles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*)); if (!handles) { PyErr_NoMemory(); cxoBuffer_clear(&hintBuffer); return NULL; } returnHandles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*)); if (!returnHandles) { PyErr_NoMemory(); PyMem_Free(handles); cxoBuffer_clear(&hintBuffer); return NULL; } result = cxoSodaCollection_insertManyHelper(coll, docsObj, numDocs, handles, returnHandles, optionsPtr); PyMem_Free(handles); PyMem_Free(returnHandles); cxoBuffer_clear(&hintBuffer); return result; } //----------------------------------------------------------------------------- // cxoSodaCollection_insertOne() // Insert a single document into the collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_insertOne(cxoSodaCollection *coll, PyObject *arg) { dpiSodaDoc *handle; uint32_t flags; int status; if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0) return NULL; if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_insertOne(coll->handle, handle, flags, NULL); Py_END_ALLOW_THREADS if (status < 0) cxoError_raiseAndReturnNull(); dpiSodaDoc_release(handle); if (status < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaCollection_insertOneAndGet() // Insert a single document into the collection and return a document // containing all but the content itself. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_insertOneAndGet(cxoSodaCollection *coll, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "doc", "hint", NULL }; dpiSodaOperOptions options, *optionsPtr = NULL; dpiSodaDoc *handle, *returnedHandle; PyObject *docObj, *hintObj; cxoBuffer hintBuffer; uint32_t flags; int status; // parse arguments docObj = hintObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList, &docObj, &hintObj)) return NULL; // setup for actual work if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; if (cxoUtils_processSodaDocArg(coll->db, docObj, &handle) < 0) return NULL; cxoBuffer_init(&hintBuffer); if (hintObj && hintObj != Py_None) { optionsPtr = &options; if (cxoSodaCollection_processOptions(coll, &options, hintObj, &hintBuffer) < 0) { dpiSodaDoc_release(handle); return NULL; } } // perform actual work Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_insertOneWithOptions(coll->handle, handle, optionsPtr, flags, &returnedHandle); Py_END_ALLOW_THREADS if (status < 0) cxoError_raiseAndReturnNull(); dpiSodaDoc_release(handle); cxoBuffer_clear(&hintBuffer); if (status < 0) return NULL; return (PyObject*) cxoSodaDoc_new(coll->db, returnedHandle); } //----------------------------------------------------------------------------- // cxoSodaCollection_getMetadata() // Retrieve the metadata for the collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_getMetadata(cxoSodaCollection *coll, PyObject *unused) { PyObject *str, *result; uint32_t valueLength; const char *value; if (dpiSodaColl_getMetadata(coll->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); str = PyUnicode_Decode(value, valueLength, coll->db->connection->encodingInfo.encoding, NULL); if (!str) return NULL; result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL); Py_DECREF(str); return result; } //----------------------------------------------------------------------------- // cxoSodaCollection_save() // Insert a single document into the collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_save(cxoSodaCollection *coll, PyObject *arg) { dpiSodaDoc *handle; uint32_t flags; int status; if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0) return NULL; if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_save(coll->handle, handle, flags, NULL); Py_END_ALLOW_THREADS if (status < 0) cxoError_raiseAndReturnNull(); dpiSodaDoc_release(handle); if (status < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaCollection_saveAndGet() // Insert a single document into the collection and return a document // containing all but the content itself. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_saveAndGet(cxoSodaCollection *coll, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "doc", "hint", NULL }; dpiSodaOperOptions options, *optionsPtr = NULL; dpiSodaDoc *handle, *returnedHandle; PyObject *docObj, *hintObj; cxoBuffer hintBuffer; uint32_t flags; int status; // parse arguments docObj = hintObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList, &docObj, &hintObj)) return NULL; // setup for actual work if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0) return NULL; if (cxoUtils_processSodaDocArg(coll->db, docObj, &handle) < 0) return NULL; cxoBuffer_init(&hintBuffer); if (hintObj && hintObj != Py_None) { optionsPtr = &options; if (cxoSodaCollection_processOptions(coll, &options, hintObj, &hintBuffer) < 0) { dpiSodaDoc_release(handle); return NULL; } } // perform actual work Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_saveWithOptions(coll->handle, handle, optionsPtr, flags, &returnedHandle); Py_END_ALLOW_THREADS if (status < 0) cxoError_raiseAndReturnNull(); dpiSodaDoc_release(handle); cxoBuffer_clear(&hintBuffer); if (status < 0) return NULL; return (PyObject*) cxoSodaDoc_new(coll->db, returnedHandle); } //----------------------------------------------------------------------------- // cxoSodaCollection_truncate() // Remove all of the documents from the SODA collection. //----------------------------------------------------------------------------- static PyObject *cxoSodaCollection_truncate(cxoSodaCollection *coll, PyObject *arg) { int status; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_truncate(coll->handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "createIndex", (PyCFunction) cxoSodaCollection_createIndex, METH_O }, { "drop", (PyCFunction) cxoSodaCollection_drop, METH_NOARGS }, { "dropIndex", (PyCFunction) cxoSodaCollection_dropIndex, METH_VARARGS | METH_KEYWORDS }, { "find", (PyCFunction) cxoSodaCollection_find, METH_NOARGS }, { "getDataGuide", (PyCFunction) cxoSodaCollection_getDataGuide, METH_NOARGS }, { "insertOne", (PyCFunction) cxoSodaCollection_insertOne, METH_O }, { "insertOneAndGet", (PyCFunction) cxoSodaCollection_insertOneAndGet, METH_VARARGS | METH_KEYWORDS }, { "insertMany", (PyCFunction) cxoSodaCollection_insertMany, METH_O }, { "insertManyAndGet", (PyCFunction) cxoSodaCollection_insertManyAndGet, METH_VARARGS | METH_KEYWORDS }, { "save", (PyCFunction) cxoSodaCollection_save, METH_O }, { "saveAndGet", (PyCFunction) cxoSodaCollection_saveAndGet, METH_VARARGS | METH_KEYWORDS }, { "truncate", (PyCFunction) cxoSodaCollection_truncate, METH_NOARGS }, { NULL } }; //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "name", T_OBJECT, offsetof(cxoSodaCollection, name), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "metadata", (getter) cxoSodaCollection_getMetadata, 0, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // Python type declarations //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeSodaCollection = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.SodaCollection", .tp_basicsize = sizeof(cxoSodaCollection), .tp_dealloc = (destructor) cxoSodaCollection_free, .tp_repr = (reprfunc) cxoSodaCollection_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoMethods, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers }; python-cx_Oracle-8.3.0/src/cxoSodaDatabase.c000066400000000000000000000262641414105416400207320ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoSodaDatabase.c // Defines the routines for handling the SODA database. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoSodaDatabase_new() // Create a new SODA database object. //----------------------------------------------------------------------------- cxoSodaDatabase *cxoSodaDatabase_new(cxoConnection *connection) { cxoSodaDatabase *db; PyObject *module; // load JSON dump/load functions, if needed if (!cxoJsonDumpFunction || !cxoJsonLoadFunction) { module = PyImport_ImportModule("json"); if (!module) return NULL; if (!cxoJsonDumpFunction) { cxoJsonDumpFunction = PyObject_GetAttrString(module, "dumps"); if (!cxoJsonDumpFunction) return NULL; } if (!cxoJsonLoadFunction) { cxoJsonLoadFunction = PyObject_GetAttrString(module, "loads"); if (!cxoJsonLoadFunction) return NULL; } } // create SODA database object db = (cxoSodaDatabase*) cxoPyTypeSodaDatabase.tp_alloc(&cxoPyTypeSodaDatabase, 0); if (!db) return NULL; if (dpiConn_getSodaDb(connection->handle, &db->handle) < 0) { Py_DECREF(db); cxoError_raiseAndReturnNull(); return NULL; } Py_INCREF(connection); db->connection = connection; return db; } //----------------------------------------------------------------------------- // cxoSodaDatabase_free() // Free the memory associated with a SODA database. //----------------------------------------------------------------------------- static void cxoSodaDatabase_free(cxoSodaDatabase *db) { if (db->handle) { dpiSodaDb_release(db->handle); db->handle = NULL; } Py_CLEAR(db->connection); Py_TYPE(db)->tp_free((PyObject*) db); } //----------------------------------------------------------------------------- // cxoSodaDatabase_repr() // Return a string representation of a SODA database. //----------------------------------------------------------------------------- static PyObject *cxoSodaDatabase_repr(cxoSodaDatabase *db) { PyObject *connectionRepr, *module, *name, *result; connectionRepr = PyObject_Repr((PyObject*) db->connection); if (!connectionRepr) return NULL; if (cxoUtils_getModuleAndName(Py_TYPE(db), &module, &name) < 0) { Py_DECREF(connectionRepr); return NULL; } result = cxoUtils_formatString("<%s.%s on %s>", PyTuple_Pack(3, module, name, connectionRepr)); Py_DECREF(module); Py_DECREF(name); Py_DECREF(connectionRepr); return result; } //----------------------------------------------------------------------------- // cxoSodaDatabase_createCollection() // Create a SODA collection and return it. //----------------------------------------------------------------------------- static PyObject *cxoSodaDatabase_createCollection(cxoSodaDatabase *db, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "name", "metadata", "mapMode", NULL }; cxoBuffer nameBuffer, metadataBuffer; PyObject *nameObj, *metadataObj; cxoSodaCollection *coll; const char *encoding; dpiSodaColl *handle; int status, mapMode; uint32_t flags; // parse arguments mapMode = 0; nameObj = metadataObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|Op", keywordList, &nameObj, &metadataObj, &mapMode)) return NULL; encoding = db->connection->encodingInfo.encoding; if (cxoBuffer_fromObject(&nameBuffer, nameObj, encoding) < 0) return NULL; if (cxoUtils_processJsonArg(metadataObj, &metadataBuffer) < 0) { cxoBuffer_clear(&nameBuffer); return NULL; } // create collection if (cxoConnection_getSodaFlags(db->connection, &flags) < 0) return NULL; if (mapMode) flags |= DPI_SODA_FLAGS_CREATE_COLL_MAP; Py_BEGIN_ALLOW_THREADS status = dpiSodaDb_createCollection(db->handle, nameBuffer.ptr, nameBuffer.size, metadataBuffer.ptr, metadataBuffer.size, flags, &handle); Py_END_ALLOW_THREADS cxoBuffer_clear(&nameBuffer); cxoBuffer_clear(&metadataBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); coll = cxoSodaCollection_new(db, handle); if (!coll) { dpiSodaColl_release(handle); return NULL; } return (PyObject*) coll; } //----------------------------------------------------------------------------- // cxoSodaDatabase_createDocument() // Create a SODA document with the specified key, content and media type. //----------------------------------------------------------------------------- static PyObject *cxoSodaDatabase_createDocument(cxoSodaDatabase *db, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "content", "key", "mediaType", NULL }; cxoBuffer contentBuffer, keyBuffer, mediaTypeBuffer; PyObject *contentObj, *keyObj, *mediaTypeObj; const char *encoding; dpiSodaDoc *doc; int status; // parse arguments keyObj = mediaTypeObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList, &contentObj, &keyObj, &mediaTypeObj)) return NULL; // content must be converted to string if it is a dictionary if (PyDict_Check(contentObj)) { contentObj = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, contentObj, NULL); if (!contentObj) return NULL; } // get buffers for each of the content, key and media type parameters if (cxoUtils_processJsonArg(contentObj, &contentBuffer) < 0) return NULL; encoding = db->connection->encodingInfo.encoding; if (cxoBuffer_fromObject(&keyBuffer, keyObj, encoding) < 0) { cxoBuffer_clear(&contentBuffer); return NULL; } if (cxoBuffer_fromObject(&mediaTypeBuffer, mediaTypeObj, encoding) < 0) { cxoBuffer_clear(&contentBuffer); cxoBuffer_clear(&keyBuffer); return NULL; } // create SODA document status = dpiSodaDb_createDocument(db->handle, keyBuffer.ptr, keyBuffer.size, contentBuffer.ptr, contentBuffer.size, mediaTypeBuffer.ptr, mediaTypeBuffer.size, DPI_SODA_FLAGS_DEFAULT, &doc); cxoBuffer_clear(&contentBuffer); cxoBuffer_clear(&keyBuffer); cxoBuffer_clear(&mediaTypeBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); return (PyObject*) cxoSodaDoc_new(db, doc); } //----------------------------------------------------------------------------- // cxoSodaDatabase_getCollectionNames() // Return a list of the names of the collections found in the database. //----------------------------------------------------------------------------- static PyObject *cxoSodaDatabase_getCollectionNames(cxoSodaDatabase *db, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "startName", "limit", NULL }; PyObject *startName, *result, *temp; dpiSodaCollNames collNames; cxoBuffer startNameBuffer; uint32_t limit, i, flags; const char *encoding; int status; // parse arguments limit = 0; startName = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|Oi", keywordList, &startName, &limit)) return NULL; // get collection names from the database encoding = db->connection->encodingInfo.encoding; if (cxoBuffer_fromObject(&startNameBuffer, startName, encoding) < 0) return NULL; if (cxoConnection_getSodaFlags(db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaDb_getCollectionNames(db->handle, (const char*) startNameBuffer.ptr, startNameBuffer.size, limit, flags, &collNames); Py_END_ALLOW_THREADS cxoBuffer_clear(&startNameBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); // transform results into a Python list result = PyList_New(collNames.numNames); if (!result) return NULL; for (i = 0; i < collNames.numNames; i++) { temp = PyUnicode_Decode(collNames.names[i], collNames.nameLengths[i], encoding, NULL); if (!temp) { Py_DECREF(result); return NULL; } PyList_SET_ITEM(result, i, temp); } if (dpiSodaDb_freeCollectionNames(db->handle, &collNames) < 0) { Py_DECREF(result); return cxoError_raiseAndReturnNull(); } return result; } //----------------------------------------------------------------------------- // cxoSodaDatabase_openCollection() // Open a SODA collection and return it. //----------------------------------------------------------------------------- static PyObject *cxoSodaDatabase_openCollection(cxoSodaDatabase *db, PyObject *nameObj) { cxoSodaCollection *coll; cxoBuffer nameBuffer; dpiSodaColl *handle; uint32_t flags; int status; // open collection if (cxoBuffer_fromObject(&nameBuffer, nameObj, db->connection->encodingInfo.encoding) < 0) return NULL; if (cxoConnection_getSodaFlags(db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaDb_openCollection(db->handle, nameBuffer.ptr, nameBuffer.size, flags, &handle); Py_END_ALLOW_THREADS cxoBuffer_clear(&nameBuffer); if (status < 0) return cxoError_raiseAndReturnNull(); if (!handle) Py_RETURN_NONE; coll = cxoSodaCollection_new(db, handle); if (!coll) { dpiSodaColl_release(handle); return NULL; } return (PyObject*) coll; } //----------------------------------------------------------------------------- // declaration of methods for Python type //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "createCollection", (PyCFunction) cxoSodaDatabase_createCollection, METH_VARARGS | METH_KEYWORDS }, { "createDocument", (PyCFunction) cxoSodaDatabase_createDocument, METH_VARARGS | METH_KEYWORDS }, { "getCollectionNames", (PyCFunction) cxoSodaDatabase_getCollectionNames, METH_VARARGS | METH_KEYWORDS }, { "openCollection", (PyCFunction) cxoSodaDatabase_openCollection, METH_O }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeSodaDatabase = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.SodaDatabase", .tp_basicsize = sizeof(cxoSodaDatabase), .tp_dealloc = (destructor) cxoSodaDatabase_free, .tp_repr = (reprfunc) cxoSodaDatabase_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoMethods }; python-cx_Oracle-8.3.0/src/cxoSodaDoc.c000066400000000000000000000222621414105416400177250ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoSodaDoc.c // Defines the routines for handling SODA documents. //----------------------------------------------------------------------------- #include "cxoModule.h" // forward declarations static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc *doc, PyObject *args); //----------------------------------------------------------------------------- // cxoSodaDoc_new() // Create a new SODA document. //----------------------------------------------------------------------------- cxoSodaDoc *cxoSodaDoc_new(cxoSodaDatabase *db, dpiSodaDoc *handle) { cxoSodaDoc *doc; doc = (cxoSodaDoc*) cxoPyTypeSodaDoc.tp_alloc(&cxoPyTypeSodaDoc, 0); if (!doc) { dpiSodaDoc_release(handle); return NULL; } Py_INCREF(db); doc->db = db; doc->handle = handle; return doc; } //----------------------------------------------------------------------------- // cxoSodaDoc_free() // Free the memory associated with a SODA document. //----------------------------------------------------------------------------- static void cxoSodaDoc_free(cxoSodaDoc *doc) { if (doc->handle) { dpiSodaDoc_release(doc->handle); doc->handle = NULL; } Py_CLEAR(doc->db); Py_TYPE(doc)->tp_free((PyObject*) doc); } //----------------------------------------------------------------------------- // cxoSodaDoc_repr() // Return a string representation of a SODA document. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_repr(cxoSodaDoc *doc) { PyObject *module, *name, *result, *keyObj; uint32_t keyLength; const char *key; if (dpiSodaDoc_getKey(doc->handle, &key, &keyLength) < 0) return cxoError_raiseAndReturnNull(); keyObj = PyUnicode_Decode(key, keyLength, doc->db->connection->encodingInfo.encoding, NULL); if (!keyObj) return NULL; if (cxoUtils_getModuleAndName(Py_TYPE(doc), &module, &name) < 0) { Py_DECREF(keyObj); return NULL; } result = cxoUtils_formatString("<%s.%s with key %s>", PyTuple_Pack(3, module, name, keyObj)); Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // cxoSodaDoc_getCreatedOn() // Retrieve the time the SODA document was created, as a string in ISO 8601 // format. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getCreatedOn(cxoSodaDoc *doc, void *unused) { uint32_t valueLength; const char *value; if (dpiSodaDoc_getCreatedOn(doc->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (valueLength > 0) return PyUnicode_Decode(value, valueLength, doc->db->connection->encodingInfo.encoding, NULL); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaDoc_getKey() // Retrieve the key for the SODA document. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getKey(cxoSodaDoc *doc, void *unused) { uint32_t valueLength; const char *value; if (dpiSodaDoc_getKey(doc->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (valueLength > 0) return PyUnicode_Decode(value, valueLength, doc->db->connection->encodingInfo.encoding, NULL); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaDoc_getLastModified() // Retrieve the time the SODA document was last modified, as a string in ISO // 8601 format. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getLastModified(cxoSodaDoc *doc, void *unused) { uint32_t valueLength; const char *value; if (dpiSodaDoc_getLastModified(doc->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (valueLength > 0) return PyUnicode_Decode(value, valueLength, doc->db->connection->encodingInfo.encoding, NULL); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaDoc_getMediaType() // Retrieve the media type of the SODA document. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getMediaType(cxoSodaDoc *doc, void *unused) { uint32_t valueLength; const char *value; if (dpiSodaDoc_getMediaType(doc->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (valueLength > 0) return PyUnicode_Decode(value, valueLength, doc->db->connection->encodingInfo.encoding, NULL); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaDoc_getVersion() // Retrieve the version for the SODA document. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getVersion(cxoSodaDoc *doc, void *unused) { uint32_t valueLength; const char *value; if (dpiSodaDoc_getVersion(doc->handle, &value, &valueLength) < 0) return cxoError_raiseAndReturnNull(); if (valueLength > 0) return PyUnicode_Decode(value, valueLength, doc->db->connection->encodingInfo.encoding, NULL); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaDoc_getContent() // Get the content from the document and return a Python object. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getContent(cxoSodaDoc *doc, PyObject *args) { PyObject *str, *result; str = cxoSodaDoc_getContentAsString(doc, args); if (!str) return NULL; if (str == Py_None) return str; result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL); Py_DECREF(str); return result; } //----------------------------------------------------------------------------- // cxoSodaDoc_getContentAsBytes() // Get the content from the document and return a bytes object. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getContentAsBytes(cxoSodaDoc *doc, PyObject *args) { const char *content, *encoding; uint32_t contentLength; if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength, &encoding) < 0) return cxoError_raiseAndReturnNull(); if (contentLength > 0) return PyBytes_FromStringAndSize(content, contentLength); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaDoc_getContentAsString() // Get the content from the document and return a string. //----------------------------------------------------------------------------- static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc *doc, PyObject *args) { const char *content, *encoding; uint32_t contentLength; if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength, &encoding) < 0) return cxoError_raiseAndReturnNull(); if (contentLength > 0) return PyUnicode_Decode(content, contentLength, encoding, NULL); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "getContent", (PyCFunction) cxoSodaDoc_getContent, METH_NOARGS }, { "getContentAsBytes", (PyCFunction) cxoSodaDoc_getContentAsBytes, METH_NOARGS }, { "getContentAsString", (PyCFunction) cxoSodaDoc_getContentAsString, METH_NOARGS }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "createdOn", (getter) cxoSodaDoc_getCreatedOn, 0, 0, 0 }, { "key", (getter) cxoSodaDoc_getKey, 0, 0, 0 }, { "lastModified", (getter) cxoSodaDoc_getLastModified, 0, 0, 0 }, { "mediaType", (getter) cxoSodaDoc_getMediaType, 0, 0, 0 }, { "version", (getter) cxoSodaDoc_getVersion, 0, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeSodaDoc = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.SodaDoc", .tp_basicsize = sizeof(cxoSodaDoc), .tp_dealloc = (destructor) cxoSodaDoc_free, .tp_repr = (reprfunc) cxoSodaDoc_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoMethods, .tp_getset = cxoCalcMembers }; python-cx_Oracle-8.3.0/src/cxoSodaDocCursor.c000066400000000000000000000114221414105416400211170ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoSodaDocCursor.c // Defines the routines for handling SODA document cursors. These cursors // permit iterating over the documents that match the criteria that was // specified by the user. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoSodaDocCursor_new() // Create a new SODA document cursor. //----------------------------------------------------------------------------- cxoSodaDocCursor *cxoSodaDocCursor_new(cxoSodaDatabase *db, dpiSodaDocCursor *handle) { cxoSodaDocCursor *cursor; cursor = (cxoSodaDocCursor*) cxoPyTypeSodaDocCursor.tp_alloc(&cxoPyTypeSodaDocCursor, 0); if (!cursor) { dpiSodaDocCursor_release(handle); return NULL; } Py_INCREF(db); cursor->db = db; cursor->handle = handle; return cursor; } //----------------------------------------------------------------------------- // cxoSodaDocCursor_free() // Free the memory associated with a SODA document cursor. //----------------------------------------------------------------------------- static void cxoSodaDocCursor_free(cxoSodaDocCursor *cursor) { if (cursor->handle) { dpiSodaDocCursor_release(cursor->handle); cursor->handle = NULL; } Py_CLEAR(cursor->db); Py_TYPE(cursor)->tp_free((PyObject*) cursor); } //----------------------------------------------------------------------------- // cxoSodaDocCursor_repr() // Return a string representation of a SODA document cursor. //----------------------------------------------------------------------------- static PyObject *cxoSodaDocCursor_repr(cxoSodaDocCursor *cursor) { PyObject *module, *name, *result; if (cxoUtils_getModuleAndName(Py_TYPE(cursor), &module, &name) < 0) return NULL; result = cxoUtils_formatString("<%s.%s>", PyTuple_Pack(2, module, name)); Py_DECREF(module); Py_DECREF(name); return result; } //----------------------------------------------------------------------------- // cxoSodaDocCursor_close() // Create a SODA collection and return it. //----------------------------------------------------------------------------- static PyObject *cxoSodaDocCursor_close(cxoSodaDocCursor *cursor, PyObject *args) { if (dpiSodaDocCursor_close(cursor->handle) < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaDocCursor_getIter() // Return a reference to the cursor which supports the iterator protocol. //----------------------------------------------------------------------------- static PyObject *cxoSodaDocCursor_getIter(cxoSodaDocCursor *cursor) { Py_INCREF(cursor); return (PyObject*) cursor; } //----------------------------------------------------------------------------- // cxoSodaDocCursor_getNext() // Return the next document from the cursor. //----------------------------------------------------------------------------- static PyObject *cxoSodaDocCursor_getNext(cxoSodaDocCursor *cursor) { dpiSodaDoc *handle; cxoSodaDoc *doc; int status; Py_BEGIN_ALLOW_THREADS status = dpiSodaDocCursor_getNext(cursor->handle, DPI_SODA_FLAGS_DEFAULT, &handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); if (!handle) return NULL; doc = cxoSodaDoc_new(cursor->db, handle); if (!doc) return NULL; return (PyObject*) doc; } //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "close", (PyCFunction) cxoSodaDocCursor_close, METH_NOARGS }, { NULL } }; //----------------------------------------------------------------------------- // Python type declarations //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeSodaDocCursor = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.SodaDocCursor", .tp_basicsize = sizeof(cxoSodaDocCursor), .tp_dealloc = (destructor) cxoSodaDocCursor_free, .tp_repr = (reprfunc) cxoSodaDocCursor_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_iter = (getiterfunc) cxoSodaDocCursor_getIter, .tp_iternext = (iternextfunc) cxoSodaDocCursor_getNext, .tp_methods = cxoMethods }; python-cx_Oracle-8.3.0/src/cxoSodaOperation.c000066400000000000000000000444761414105416400211730ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoSodaOperation.c // Defines the routines for the various operations performed on SODA // collections. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoSodaOperation_clearKeys() // Clear the keys set on the operation object, if applicable. //----------------------------------------------------------------------------- void cxoSodaOperation_clearKeys(cxoSodaOperation *op) { uint32_t i; if (op->keyBuffers) { for (i = 0; i < op->numKeyBuffers; i++) cxoBuffer_clear(&op->keyBuffers[i]); PyMem_Free(op->keyBuffers); op->keyBuffers = NULL; } op->numKeyBuffers = 0; op->options.numKeys = 0; if (op->options.keys) { PyMem_Free(op->options.keys); op->options.keys = NULL; } if (op->options.keyLengths) { PyMem_Free(op->options.keyLengths); op->options.keyLengths = NULL; } } //----------------------------------------------------------------------------- // cxoSodaOperation_new() // Create a new SODA operation object. //----------------------------------------------------------------------------- cxoSodaOperation *cxoSodaOperation_new(cxoSodaCollection *coll) { cxoSodaOperation *op; op = (cxoSodaOperation*) cxoPyTypeSodaOperation.tp_alloc(&cxoPyTypeSodaOperation, 0); if (!op) return NULL; if (dpiContext_initSodaOperOptions(cxoDpiContext, &op->options) < 0) { Py_DECREF(op); return NULL; } cxoBuffer_init(&op->keyBuffer); cxoBuffer_init(&op->versionBuffer); cxoBuffer_init(&op->filterBuffer); Py_INCREF(coll); op->coll = coll; return op; } //----------------------------------------------------------------------------- // cxoSodaOperation_free() // Free the memory associated with a SODA operation object. //----------------------------------------------------------------------------- static void cxoSodaOperation_free(cxoSodaOperation *op) { cxoSodaOperation_clearKeys(op); cxoBuffer_clear(&op->keyBuffer); cxoBuffer_clear(&op->versionBuffer); cxoBuffer_clear(&op->filterBuffer); Py_CLEAR(op->coll); Py_TYPE(op)->tp_free((PyObject*) op); } //----------------------------------------------------------------------------- // cxoSodaOperation_repr() // Return a string representation of a SODA operation object. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_repr(cxoSodaOperation *op) { PyObject *collRepr, *module, *name, *result; collRepr = PyObject_Repr((PyObject*) op->coll); if (!collRepr) return NULL; if (cxoUtils_getModuleAndName(Py_TYPE(op), &module, &name) < 0) { Py_DECREF(collRepr); return NULL; } result = cxoUtils_formatString("<%s.%s on %s>", PyTuple_Pack(3, module, name, collRepr)); Py_DECREF(module); Py_DECREF(name); Py_DECREF(collRepr); return result; } //----------------------------------------------------------------------------- // cxoSodaOperation_filter() // Set the filter to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_filter(cxoSodaOperation *op, PyObject *filterObj) { PyObject *convertedFilterObj = NULL; cxoBuffer_clear(&op->filterBuffer); if (PyDict_Check(filterObj)) { convertedFilterObj = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, filterObj, NULL); if (!convertedFilterObj) return NULL; filterObj = convertedFilterObj; } if (cxoBuffer_fromObject(&op->filterBuffer, filterObj, op->coll->db->connection->encodingInfo.encoding) < 0) return NULL; Py_CLEAR(convertedFilterObj); op->options.filter = op->filterBuffer.ptr; op->options.filterLength = op->filterBuffer.size; Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // cxoSodaOperation_hint() // Set the hint to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_hint(cxoSodaOperation *op, PyObject *hintObj) { cxoBuffer_clear(&op->hintBuffer); if (cxoBuffer_fromObject(&op->hintBuffer, hintObj, op->coll->db->connection->encodingInfo.encoding) < 0) return NULL; op->options.hint = op->hintBuffer.ptr; op->options.hintLength = op->hintBuffer.size; Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // cxoSodaOperation_key() // Set the key to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_key(cxoSodaOperation *op, PyObject *keyObj) { cxoBuffer_clear(&op->keyBuffer); if (cxoBuffer_fromObject(&op->keyBuffer, keyObj, op->coll->db->connection->encodingInfo.encoding) < 0) return NULL; op->options.key = op->keyBuffer.ptr; op->options.keyLength = op->keyBuffer.size; Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // cxoSodaOperation_keys() // Set the keys to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_keys(cxoSodaOperation *op, PyObject *keysObj) { Py_ssize_t size, i; PyObject *element; // determine size of sequence passed to method size = PySequence_Size(keysObj); if (PyErr_Occurred()) return NULL; // clear original keys, if applicable cxoSodaOperation_clearKeys(op); // zero-length arrays don't need any further processing if (size == 0) { Py_INCREF(op); return (PyObject*) op; } // initialize memory op->keyBuffers = PyMem_Malloc(size * sizeof(cxoBuffer)); if (!op->keyBuffers) return NULL; op->numKeyBuffers = (uint32_t) size; for (i = 0; i < size; i++) cxoBuffer_init(&op->keyBuffers[i]); op->options.keys = PyMem_Malloc(size * sizeof(const char *)); op->options.keyLengths = PyMem_Malloc(size * sizeof(uint32_t)); if (!op->options.keys || !op->options.keyLengths) { cxoSodaOperation_clearKeys(op); return NULL; } op->options.numKeys = op->numKeyBuffers; // process each of the elements of the sequence for (i = 0; i < size; i++) { element = PySequence_GetItem(keysObj, i); if (!element) { cxoSodaOperation_clearKeys(op); return NULL; } if (cxoBuffer_fromObject(&op->keyBuffers[i], element, op->coll->db->connection->encodingInfo.encoding) < 0) { Py_DECREF(element); cxoSodaOperation_clearKeys(op); return NULL; } Py_DECREF(element); op->options.keys[i] = op->keyBuffers[i].ptr; op->options.keyLengths[i] = op->keyBuffers[i].size; } Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // cxoSodaOperation_limit() // Set the limit value to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_limit(cxoSodaOperation *op, PyObject *limitObj) { op->options.limit = PyLong_AsUnsignedLong(limitObj); if (PyErr_Occurred()) return NULL; Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // cxoSodaOperation_skip() // Set the skip value to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_skip(cxoSodaOperation *op, PyObject *skipObj) { op->options.skip = PyLong_AsUnsignedLong(skipObj); if (PyErr_Occurred()) return NULL; Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // cxoSodaOperation_version() // Set the version to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_version(cxoSodaOperation *op, PyObject *versionObj) { cxoBuffer_clear(&op->versionBuffer); if (cxoBuffer_fromObject(&op->versionBuffer, versionObj, op->coll->db->connection->encodingInfo.encoding) < 0) return NULL; op->options.version = op->versionBuffer.ptr; op->options.versionLength = op->versionBuffer.size; Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // cxoSodaOperation_count() // Returns the number of documents that match the criteria. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_count(cxoSodaOperation *op, PyObject *args) { uint64_t count; uint32_t flags; int status; if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_getDocCount(op->coll->handle, &op->options, flags, &count); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromUnsignedLongLong(count); } //----------------------------------------------------------------------------- // cxoSodaOperation_getCursor() // Returns a document cursor which can be used to iterate over the documents // that match the criteria. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_getCursor(cxoSodaOperation *op, PyObject *args) { dpiSodaDocCursor *handle; cxoSodaDocCursor *cursor; uint32_t flags; int status; if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_find(op->coll->handle, &op->options, flags, &handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); cursor = cxoSodaDocCursor_new(op->coll->db, handle); if (!cursor) return NULL; return (PyObject*) cursor; } //----------------------------------------------------------------------------- // cxoSodaOperation_getDocuments() // Returns a list of documents that match the criteria. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_getDocuments(cxoSodaOperation *op, PyObject *args) { dpiSodaDocCursor *cursor; PyObject *docObj; dpiSodaDoc *doc; PyObject *list; uint32_t flags; int status; // acquire cursor if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_find(op->coll->handle, &op->options, flags, &cursor); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); // iterate cursor and create array of documents list = PyList_New(0); if (!list) { dpiSodaDocCursor_release(cursor); return NULL; } while (1) { Py_BEGIN_ALLOW_THREADS status = dpiSodaDocCursor_getNext(cursor, flags, &doc); Py_END_ALLOW_THREADS if (status < 0) { cxoError_raiseAndReturnNull(); dpiSodaDocCursor_release(cursor); return NULL; } if (!doc) break; docObj = (PyObject*) cxoSodaDoc_new(op->coll->db, doc); if (!docObj) { dpiSodaDocCursor_release(cursor); return NULL; } if (PyList_Append(list, docObj) < 0) { Py_DECREF(docObj); dpiSodaDocCursor_release(cursor); return NULL; } Py_DECREF(docObj); } dpiSodaDocCursor_release(cursor); return list; } //----------------------------------------------------------------------------- // cxoSodaOperation_getOne() // Returns a single document that matches the criteria or None if no // documents match the criteria. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_getOne(cxoSodaOperation *op, PyObject *args) { dpiSodaDoc *handle; uint32_t flags; int status; if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_findOne(op->coll->handle, &op->options, flags, &handle); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); if (handle) return (PyObject*) cxoSodaDoc_new(op->coll->db, handle); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaOperation_remove() // Remove all of the documents that match the criteria. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_remove(cxoSodaOperation *op, PyObject *args) { uint64_t count; uint32_t flags; int status; if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_remove(op->coll->handle, &op->options, flags, &count); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromUnsignedLongLong(count); } //----------------------------------------------------------------------------- // cxoSodaOperation_replaceOne() // Replace a single document in the collection with the provided replacement. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_replaceOne(cxoSodaOperation *op, PyObject *arg) { int status, replaced; dpiSodaDoc *handle; uint32_t flags; if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0) return NULL; if (cxoUtils_processSodaDocArg(op->coll->db, arg, &handle) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_replaceOne(op->coll->handle, &op->options, handle, flags, &replaced, NULL); Py_END_ALLOW_THREADS if (status < 0) cxoError_raiseAndReturnNull(); dpiSodaDoc_release(handle); if (status < 0) return NULL; if (replaced) Py_RETURN_TRUE; Py_RETURN_FALSE; } //----------------------------------------------------------------------------- // cxoSodaOperation_replaceOneAndGet() // Replace a single document in the collection with the provided replacement // and return a document (without the content) to the caller. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_replaceOneAndGet(cxoSodaOperation *op, PyObject *arg) { dpiSodaDoc *handle, *replacedHandle; uint32_t flags; int status; if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0) return NULL; if (cxoUtils_processSodaDocArg(op->coll->db, arg, &handle) < 0) return NULL; Py_BEGIN_ALLOW_THREADS status = dpiSodaColl_replaceOne(op->coll->handle, &op->options, handle, flags, NULL, &replacedHandle); Py_END_ALLOW_THREADS if (status < 0) cxoError_raiseAndReturnNull(); dpiSodaDoc_release(handle); if (status < 0) return NULL; if (replacedHandle) return (PyObject*) cxoSodaDoc_new(op->coll->db, replacedHandle); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoSodaOperation_fetchArraySize() // Set the fetch array size to be used for the operation. //----------------------------------------------------------------------------- static PyObject *cxoSodaOperation_fetchArraySize(cxoSodaOperation *op, PyObject *fetchArraySizeObj) { op->options.fetchArraySize = PyLong_AsUnsignedLong(fetchArraySizeObj); if (PyErr_Occurred()) return NULL; Py_INCREF(op); return (PyObject*) op; } //----------------------------------------------------------------------------- // declaration of methods for Python type //----------------------------------------------------------------------------- static PyMethodDef cxoMethods[] = { { "filter", (PyCFunction) cxoSodaOperation_filter, METH_O }, { "key", (PyCFunction) cxoSodaOperation_key, METH_O }, { "keys", (PyCFunction) cxoSodaOperation_keys, METH_O }, { "limit", (PyCFunction) cxoSodaOperation_limit, METH_O }, { "skip", (PyCFunction) cxoSodaOperation_skip, METH_O }, { "version", (PyCFunction) cxoSodaOperation_version, METH_O }, { "count", (PyCFunction) cxoSodaOperation_count, METH_NOARGS }, { "getCursor", (PyCFunction) cxoSodaOperation_getCursor, METH_NOARGS }, { "getDocuments", (PyCFunction) cxoSodaOperation_getDocuments, METH_NOARGS }, { "getOne", (PyCFunction) cxoSodaOperation_getOne, METH_NOARGS }, { "hint", (PyCFunction) cxoSodaOperation_hint, METH_O }, { "remove", (PyCFunction) cxoSodaOperation_remove, METH_NOARGS }, { "replaceOne", (PyCFunction) cxoSodaOperation_replaceOne, METH_O }, { "replaceOneAndGet", (PyCFunction) cxoSodaOperation_replaceOneAndGet, METH_O }, { "fetchArraySize", (PyCFunction) cxoSodaOperation_fetchArraySize, METH_O }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeSodaOperation = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.SodaOperation", .tp_basicsize = sizeof(cxoSodaOperation), .tp_dealloc = (destructor) cxoSodaOperation_free, .tp_repr = (reprfunc) cxoSodaOperation_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoMethods }; python-cx_Oracle-8.3.0/src/cxoSubscr.c000066400000000000000000000440621414105416400176540ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoSubscr.c // Defines the routines for handling Oracle subscription information. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoMessageRow_initialize() // Initialize a new message row with the information from the descriptor. //----------------------------------------------------------------------------- static int cxoMessageRow_initialize(cxoMessageRow *rowObj, const char *encoding, dpiSubscrMessageRow *row) { rowObj->operation = row->operation; rowObj->rowid = PyUnicode_Decode(row->rowid, row->rowidLength, encoding, NULL); if (!rowObj->rowid) return -1; return 0; } //----------------------------------------------------------------------------- // cxoMessageTable_initialize() // Initialize a new message table with the information from the descriptor. //----------------------------------------------------------------------------- static int cxoMessageTable_initialize(cxoMessageTable *tableObj, const char *encoding, dpiSubscrMessageTable *table) { cxoMessageRow *row; uint32_t i; tableObj->operation = table->operation; tableObj->name = PyUnicode_Decode(table->name, table->nameLength, encoding, NULL); tableObj->rows = PyList_New(table->numRows); if (!tableObj->rows) return -1; for (i = 0; i < table->numRows; i++) { row = (cxoMessageRow*) cxoPyTypeMessageRow.tp_alloc(&cxoPyTypeMessageRow, 0); if (!row) return -1; PyList_SET_ITEM(tableObj->rows, i, (PyObject*) row); if (cxoMessageRow_initialize(row, encoding, &table->rows[i]) < 0) return -1; } return 0; } //----------------------------------------------------------------------------- // cxoMessageQuery_initialize() // Initialize a new message query with the information from the descriptor. //----------------------------------------------------------------------------- static int cxoMessageQuery_initialize(cxoMessageQuery *queryObj, const char *encoding, dpiSubscrMessageQuery *query) { cxoMessageTable *table; uint32_t i; queryObj->id = query->id; queryObj->operation = query->operation; queryObj->tables = PyList_New(query->numTables); if (!queryObj->tables) return -1; for (i = 0; i < query->numTables; i++) { table = (cxoMessageTable*) cxoPyTypeMessageTable.tp_alloc(&cxoPyTypeMessageTable, 0); if (!table) return -1; PyList_SET_ITEM(queryObj->tables, i, (PyObject*) table); if (cxoMessageTable_initialize(table, encoding, &query->tables[i]) < 0) return -1; } return 0; } //----------------------------------------------------------------------------- // cxoMessage_initialize() // Initialize a new message with the information from the descriptor. //----------------------------------------------------------------------------- static int cxoMessage_initialize(cxoMessage *messageObj, cxoSubscr *subscription, dpiSubscrMessage *message) { cxoMessageTable *table; cxoMessageQuery *query; const char *encoding; uint32_t i; Py_INCREF(subscription); messageObj->subscription = subscription; encoding = subscription->connection->encodingInfo.encoding; messageObj->type = message->eventType; messageObj->registered = message->registered; messageObj->dbname = PyUnicode_Decode(message->dbName, message->dbNameLength, encoding, NULL); if (!messageObj->dbname) return -1; if (message->txId) { messageObj->txId = PyBytes_FromStringAndSize(message->txId, message->txIdLength); if (!messageObj->txId) return -1; } if (message->queueName) { messageObj->queueName = PyUnicode_Decode(message->queueName, message->queueNameLength, encoding, NULL); if (!messageObj->queueName) return -1; } if (message->consumerName) { messageObj->consumerName = PyUnicode_Decode(message->consumerName, message->consumerNameLength, encoding, NULL); if (!messageObj->consumerName) return -1; } switch (message->eventType) { case DPI_EVENT_OBJCHANGE: messageObj->tables = PyList_New(message->numTables); if (!messageObj->tables) return -1; for (i = 0; i < message->numTables; i++) { table = (cxoMessageTable*) cxoPyTypeMessageTable.tp_alloc(&cxoPyTypeMessageTable, 0); if (!table) return -1; PyList_SET_ITEM(messageObj->tables, i, (PyObject*) table); if (cxoMessageTable_initialize(table, encoding, &message->tables[i]) < 0) return -1; } break; case DPI_EVENT_QUERYCHANGE: messageObj->queries = PyList_New(message->numQueries); if (!messageObj->queries) return -1; for (i = 0; i < message->numQueries; i++) { query = (cxoMessageQuery*) cxoPyTypeMessageQuery.tp_alloc(&cxoPyTypeMessageQuery, 0); if (!query) return -1; PyList_SET_ITEM(messageObj->queries, i, (PyObject*) query); if (cxoMessageQuery_initialize(query, encoding, &message->queries[i]) < 0) return -1; } break; default: break; } return 0; } //----------------------------------------------------------------------------- // cxoSubscr_callbackHandler() // Routine that performs the actual call. //----------------------------------------------------------------------------- static int cxoSubscr_callbackHandler(cxoSubscr *subscr, dpiSubscrMessage *message) { PyObject *result, *args; cxoMessage *messageObj; // create the message messageObj = (cxoMessage*) cxoPyTypeMessage.tp_alloc(&cxoPyTypeMessage, 0); if (!messageObj) return -1; if (cxoMessage_initialize(messageObj, subscr, message) < 0) { Py_DECREF(messageObj); return -1; } // create the arguments for the call args = PyTuple_Pack(1, messageObj); Py_DECREF(messageObj); if (!args) return -1; // make the actual call result = PyObject_Call(subscr->callback, args, NULL); Py_DECREF(args); if (!result) return -1; Py_DECREF(result); return 0; } //----------------------------------------------------------------------------- // cxoSubscr_callback() // Routine that is called when a callback needs to be invoked. //----------------------------------------------------------------------------- void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message) { #ifdef WITH_THREAD PyGILState_STATE gstate = PyGILState_Ensure(); #endif if (message->errorInfo) { cxoError_raiseFromInfo(message->errorInfo); PyErr_Print(); } else if (cxoSubscr_callbackHandler(subscr, message) < 0) PyErr_Print(); #ifdef WITH_THREAD PyGILState_Release(gstate); #endif } //----------------------------------------------------------------------------- // cxoSubscr_free() // Free the memory associated with a subscription. //----------------------------------------------------------------------------- static void cxoSubscr_free(cxoSubscr *subscr) { if (subscr->handle) { dpiSubscr_release(subscr->handle); subscr->handle = NULL; } Py_CLEAR(subscr->connection); Py_CLEAR(subscr->callback); Py_CLEAR(subscr->name); Py_CLEAR(subscr->ipAddress); Py_TYPE(subscr)->tp_free((PyObject*) subscr); } //----------------------------------------------------------------------------- // cxoSubscr_repr() // Return a string representation of the subscription. //----------------------------------------------------------------------------- static PyObject *cxoSubscr_repr(cxoSubscr *subscription) { PyObject *connectionRepr, *module, *name, *result; connectionRepr = PyObject_Repr((PyObject*) subscription->connection); if (!connectionRepr) return NULL; if (cxoUtils_getModuleAndName(Py_TYPE(subscription), &module, &name) < 0) { Py_DECREF(connectionRepr); return NULL; } result = cxoUtils_formatString("<%s.%s on %s>", PyTuple_Pack(3, module, name, connectionRepr)); Py_DECREF(module); Py_DECREF(name); Py_DECREF(connectionRepr); return result; } //----------------------------------------------------------------------------- // cxoSubscr_registerQuery() // Register a query for database change notification. //----------------------------------------------------------------------------- static PyObject *cxoSubscr_registerQuery(cxoSubscr *subscr, PyObject *args) { PyObject *statement, *executeArgs; cxoBuffer statementBuffer; uint32_t numQueryColumns; cxoCursor *cursor; uint64_t queryId; int status; // parse arguments executeArgs = NULL; if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs)) return NULL; if (executeArgs) { if (!PyDict_Check(executeArgs) && !PySequence_Check(executeArgs)) { PyErr_SetString(PyExc_TypeError, "expecting a dictionary or sequence"); return NULL; } } // create cursor to perform query cursor = (cxoCursor*) PyObject_CallMethod((PyObject*) subscr->connection, "cursor", NULL); if (!cursor) return NULL; // prepare the statement for execution if (cxoBuffer_fromObject(&statementBuffer, statement, subscr->connection->encodingInfo.encoding) < 0) { Py_DECREF(cursor); return NULL; } status = dpiSubscr_prepareStmt(subscr->handle, statementBuffer.ptr, statementBuffer.size, &cursor->handle); cxoBuffer_clear(&statementBuffer); if (status < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(cursor); return NULL; } // perform binds if (executeArgs && cxoCursor_setBindVariables(cursor, executeArgs, 1, 0, 0) < 0) { Py_DECREF(cursor); return NULL; } if (cxoCursor_performBind(cursor) < 0) { Py_DECREF(cursor); return NULL; } // perform the execute (which registers the query) Py_BEGIN_ALLOW_THREADS status = dpiStmt_execute(cursor->handle, DPI_MODE_EXEC_DEFAULT, &numQueryColumns); Py_END_ALLOW_THREADS if (status < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(cursor); return NULL; } // return the query id, if applicable if (subscr->qos & DPI_SUBSCR_QOS_QUERY) { if (dpiStmt_getSubscrQueryId(cursor->handle, &queryId) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(cursor); return NULL; } Py_DECREF(cursor); return PyLong_FromLong((long) queryId); } Py_DECREF(cursor); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoMessage_free() // Free the memory associated with a message. //----------------------------------------------------------------------------- static void cxoMessage_free(cxoMessage *message) { Py_CLEAR(message->subscription); Py_CLEAR(message->dbname); Py_CLEAR(message->txId); Py_CLEAR(message->tables); Py_CLEAR(message->queries); Py_CLEAR(message->queueName); Py_CLEAR(message->consumerName); Py_TYPE(message)->tp_free((PyObject*) message); } //----------------------------------------------------------------------------- // cxoMessageQuery_free() // Free the memory associated with a query in a message. //----------------------------------------------------------------------------- static void cxoMessageQuery_free(cxoMessageQuery *query) { Py_CLEAR(query->tables); Py_TYPE(query)->tp_free((PyObject*) query); } //----------------------------------------------------------------------------- // cxoMessageRow_free() // Free the memory associated with a row in a message. //----------------------------------------------------------------------------- static void cxoMessageRow_free(cxoMessageRow *row) { Py_CLEAR(row->rowid); Py_TYPE(row)->tp_free((PyObject*) row); } //----------------------------------------------------------------------------- // cxoMessageTable_free() // Free the memory associated with a table in a message. //----------------------------------------------------------------------------- static void cxoMessageTable_free(cxoMessageTable *table) { Py_CLEAR(table->name); Py_CLEAR(table->rows); Py_TYPE(table)->tp_free((PyObject*) table); } //----------------------------------------------------------------------------- // declaration of members for Python types //----------------------------------------------------------------------------- static PyMemberDef cxoSubscrTypeMembers[] = { { "callback", T_OBJECT, offsetof(cxoSubscr, callback), READONLY }, { "connection", T_OBJECT, offsetof(cxoSubscr, connection), READONLY }, { "namespace", T_UINT, offsetof(cxoSubscr, namespace), READONLY }, { "name", T_OBJECT, offsetof(cxoSubscr, name), READONLY }, { "protocol", T_UINT, offsetof(cxoSubscr, protocol), READONLY }, { "ip_address", T_OBJECT, offsetof(cxoSubscr, ipAddress), READONLY }, { "port", T_UINT, offsetof(cxoSubscr, port), READONLY }, { "timeout", T_UINT, offsetof(cxoSubscr, timeout), READONLY }, { "operations", T_UINT, offsetof(cxoSubscr, operations), READONLY }, { "qos", T_UINT, offsetof(cxoSubscr, qos), READONLY }, { "id", T_ULONG, offsetof(cxoSubscr, id), READONLY }, // deprecated { "ipAddress", T_OBJECT, offsetof(cxoSubscr, ipAddress), READONLY }, { NULL } }; static PyMemberDef cxoMessageTypeMembers[] = { { "subscription", T_OBJECT, offsetof(cxoMessage, subscription), READONLY }, { "type", T_INT, offsetof(cxoMessage, type), READONLY }, { "dbname", T_OBJECT, offsetof(cxoMessage, dbname), READONLY }, { "txid", T_OBJECT, offsetof(cxoMessage, txId), READONLY }, { "tables", T_OBJECT, offsetof(cxoMessage, tables), READONLY }, { "queries", T_OBJECT, offsetof(cxoMessage, queries), READONLY }, { "queue_name", T_OBJECT, offsetof(cxoMessage, queueName), READONLY }, { "consumer_name", T_OBJECT, offsetof(cxoMessage, consumerName), READONLY }, { "registered", T_BOOL, offsetof(cxoMessage, registered), READONLY }, // deprecated { "queueName", T_OBJECT, offsetof(cxoMessage, queueName), READONLY }, { "consumerName", T_OBJECT, offsetof(cxoMessage, consumerName), READONLY }, { NULL } }; static PyMemberDef cxoMessageTableTypeMembers[] = { { "name", T_OBJECT, offsetof(cxoMessageTable, name), READONLY }, { "rows", T_OBJECT, offsetof(cxoMessageTable, rows), READONLY }, { "operation", T_INT, offsetof(cxoMessageTable, operation), READONLY }, { NULL } }; static PyMemberDef cxoMessageRowTypeMembers[] = { { "rowid", T_OBJECT, offsetof(cxoMessageRow, rowid), READONLY }, { "operation", T_INT, offsetof(cxoMessageRow, operation), READONLY }, { NULL } }; static PyMemberDef cxoMessageQueryTypeMembers[] = { { "id", T_INT, offsetof(cxoMessageQuery, id), READONLY }, { "operation", T_INT, offsetof(cxoMessageQuery, operation), READONLY }, { "tables", T_OBJECT, offsetof(cxoMessageQuery, tables), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // declaration of methods for Python types //----------------------------------------------------------------------------- static PyMethodDef cxoSubscrTypeMethods[] = { { "registerquery", (PyCFunction) cxoSubscr_registerQuery, METH_VARARGS }, { NULL, NULL } }; //----------------------------------------------------------------------------- // Python type declarations //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeSubscr = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.Subscription", .tp_basicsize = sizeof(cxoSubscr), .tp_dealloc = (destructor) cxoSubscr_free, .tp_repr = (reprfunc) cxoSubscr_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoSubscrTypeMethods, .tp_members = cxoSubscrTypeMembers }; PyTypeObject cxoPyTypeMessage = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.Message", .tp_basicsize = sizeof(cxoMessage), .tp_dealloc = (destructor) cxoMessage_free, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMessageTypeMembers }; PyTypeObject cxoPyTypeMessageTable = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.MessageTable", .tp_basicsize = sizeof(cxoMessageTable), .tp_dealloc = (destructor) cxoMessageTable_free, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMessageTableTypeMembers }; PyTypeObject cxoPyTypeMessageRow = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.MessageRow", .tp_basicsize = sizeof(cxoMessageRow), .tp_dealloc = (destructor) cxoMessageRow_free, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMessageRowTypeMembers }; PyTypeObject cxoPyTypeMessageQuery = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.MessageQuery", .tp_basicsize = sizeof(cxoMessageQuery), .tp_dealloc = (destructor) cxoMessageQuery_free, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = cxoMessageQueryTypeMembers }; python-cx_Oracle-8.3.0/src/cxoTransform.c000066400000000000000000001070761414105416400203730ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoTransform.c // Defines routines used to transform Python objects to database values and // database values to Python objects. //----------------------------------------------------------------------------- #include "cxoModule.h" #include "datetime.h" // PyPy compatibility #ifndef PyDateTime_DELTA_GET_DAYS #define PyDateTime_DELTA_GET_DAYS(x) ((x)->days) #endif #ifndef PyDateTime_DELTA_GET_SECONDS #define PyDateTime_DELTA_GET_SECONDS(x) ((x)->seconds) #endif #ifndef PyDateTime_DELTA_GET_MICROSECONDS #define PyDateTime_DELTA_GET_MICROSECONDS(x) ((x)->microseconds) #endif // forward declarations static Py_ssize_t cxoTransform_calculateSize(PyObject *value, cxoTransformNum transformNum); static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type); static PyObject *cxoTransform_toPythonFromJson(cxoConnection *connection, dpiJsonNode *node, const char *encodingErrors); //----------------------------------------------------------------------------- // Types //----------------------------------------------------------------------------- typedef struct { cxoTransformNum transformNum; dpiOracleTypeNum oracleTypeNum; dpiNativeTypeNum nativeTypeNum; } cxoTransform; //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- PyTypeObject *cxoPyTypeDate; PyTypeObject *cxoPyTypeDateTime; static PyTypeObject *cxoPyTypeDecimal; static const cxoTransform cxoAllTransforms[] = { { CXO_TRANSFORM_NONE, DPI_ORACLE_TYPE_VARCHAR, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_BINARY, DPI_ORACLE_TYPE_RAW, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_BFILE, DPI_ORACLE_TYPE_BFILE, DPI_NATIVE_TYPE_LOB }, { CXO_TRANSFORM_BLOB, DPI_ORACLE_TYPE_BLOB, DPI_NATIVE_TYPE_LOB }, { CXO_TRANSFORM_BOOLEAN, DPI_ORACLE_TYPE_BOOLEAN, DPI_NATIVE_TYPE_BOOLEAN }, { CXO_TRANSFORM_CLOB, DPI_ORACLE_TYPE_CLOB, DPI_NATIVE_TYPE_LOB }, { CXO_TRANSFORM_CURSOR, DPI_ORACLE_TYPE_STMT, DPI_NATIVE_TYPE_STMT }, { CXO_TRANSFORM_DATE, DPI_ORACLE_TYPE_DATE, DPI_NATIVE_TYPE_TIMESTAMP }, { CXO_TRANSFORM_DATETIME, DPI_ORACLE_TYPE_DATE, DPI_NATIVE_TYPE_TIMESTAMP }, { CXO_TRANSFORM_DECIMAL, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_FIXED_CHAR, DPI_ORACLE_TYPE_CHAR, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_FIXED_NCHAR, DPI_ORACLE_TYPE_NCHAR, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_FLOAT, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_INT, DPI_ORACLE_TYPE_NUMBER, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_LONG_BINARY, DPI_ORACLE_TYPE_LONG_RAW, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_LONG_STRING, DPI_ORACLE_TYPE_LONG_VARCHAR, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_NATIVE_DOUBLE, DPI_ORACLE_TYPE_NATIVE_DOUBLE, DPI_NATIVE_TYPE_DOUBLE }, { CXO_TRANSFORM_NATIVE_FLOAT, DPI_ORACLE_TYPE_NATIVE_FLOAT, DPI_NATIVE_TYPE_FLOAT }, { CXO_TRANSFORM_NATIVE_INT, DPI_ORACLE_TYPE_NATIVE_INT, DPI_NATIVE_TYPE_INT64 }, { CXO_TRANSFORM_NCLOB, DPI_ORACLE_TYPE_NCLOB, DPI_NATIVE_TYPE_LOB }, { CXO_TRANSFORM_NSTRING, DPI_ORACLE_TYPE_NVARCHAR, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_OBJECT, DPI_ORACLE_TYPE_OBJECT, DPI_NATIVE_TYPE_OBJECT }, { CXO_TRANSFORM_ROWID, DPI_ORACLE_TYPE_ROWID, DPI_NATIVE_TYPE_ROWID }, { CXO_TRANSFORM_STRING, DPI_ORACLE_TYPE_VARCHAR, DPI_NATIVE_TYPE_BYTES }, { CXO_TRANSFORM_TIMEDELTA, DPI_ORACLE_TYPE_INTERVAL_DS, DPI_NATIVE_TYPE_INTERVAL_DS }, { CXO_TRANSFORM_TIMESTAMP, DPI_ORACLE_TYPE_TIMESTAMP, DPI_NATIVE_TYPE_TIMESTAMP }, { CXO_TRANSFORM_TIMESTAMP_LTZ, DPI_ORACLE_TYPE_TIMESTAMP_LTZ, DPI_NATIVE_TYPE_TIMESTAMP }, { CXO_TRANSFORM_TIMESTAMP_TZ, DPI_ORACLE_TYPE_TIMESTAMP_TZ, DPI_NATIVE_TYPE_TIMESTAMP }, { CXO_TRANSFORM_JSON, DPI_ORACLE_TYPE_JSON, DPI_NATIVE_TYPE_JSON } }; //----------------------------------------------------------------------------- // cxoTransform_calculateSize() // Calculate the size to use with the specified transform and Python value. // This function is only called by cxoTransform_getNumFromValue() and no // attempt is made to verify the value further. //----------------------------------------------------------------------------- static Py_ssize_t cxoTransform_calculateSize(PyObject *value, cxoTransformNum transformNum) { switch (transformNum) { case CXO_TRANSFORM_NONE: return 1; case CXO_TRANSFORM_BINARY: return PyBytes_GET_SIZE(value); case CXO_TRANSFORM_NSTRING: case CXO_TRANSFORM_STRING: return PyUnicode_GET_LENGTH(value); default: break; } return 0; } //----------------------------------------------------------------------------- // cxoTransform_dateFromTicks() // Creates a date from ticks (number of seconds since Unix epoch). //----------------------------------------------------------------------------- PyObject *cxoTransform_dateFromTicks(PyObject *args) { return PyDate_FromTimestamp(args); } //----------------------------------------------------------------------------- // cxoTransform_fromPython() // Transforms a Python object into its corresponding database value. //----------------------------------------------------------------------------- int cxoTransform_fromPython(cxoTransformNum transformNum, dpiNativeTypeNum *nativeTypeNum, PyObject *pyValue, dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding, const char *nencoding, cxoVar *var, uint32_t arrayPos) { cxoJsonBuffer jsonBuffer; dpiIntervalDS *interval; PyDateTime_Delta *delta; int32_t deltaSeconds; PyObject *tempValue; cxoObject *obj; cxoLob *lob; int status; switch (transformNum) { case CXO_TRANSFORM_BOOLEAN: dbValue->asBoolean = PyObject_IsTrue(pyValue); if (PyErr_Occurred()) return -1; return 0; case CXO_TRANSFORM_BINARY: case CXO_TRANSFORM_FIXED_CHAR: case CXO_TRANSFORM_LONG_BINARY: case CXO_TRANSFORM_LONG_STRING: case CXO_TRANSFORM_STRING: if (cxoBuffer_fromObject(buffer, pyValue, encoding) < 0) return -1; dbValue->asBytes.ptr = (char*) buffer->ptr; dbValue->asBytes.length = buffer->size; return 0; case CXO_TRANSFORM_FIXED_NCHAR: case CXO_TRANSFORM_NSTRING: if (cxoBuffer_fromObject(buffer, pyValue, nencoding) < 0) return -1; dbValue->asBytes.ptr = (char*) buffer->ptr; dbValue->asBytes.length = buffer->size; return 0; case CXO_TRANSFORM_BLOB: case CXO_TRANSFORM_CLOB: case CXO_TRANSFORM_NCLOB: if (Py_TYPE(pyValue) == &cxoPyTypeLob) { lob = (cxoLob*) pyValue; if ((lob->dbType == cxoDbTypeBlob && transformNum != CXO_TRANSFORM_BLOB) || (lob->dbType == cxoDbTypeClob && transformNum != CXO_TRANSFORM_CLOB) || (lob->dbType == cxoDbTypeNclob && transformNum != CXO_TRANSFORM_NCLOB)) { PyErr_SetString(PyExc_TypeError, "LOB must be of the correct type"); return -1; } if (var) { if (dpiVar_setFromLob(var->handle, arrayPos, lob->handle) < 0) return cxoError_raiseAndReturnInt(); } else dbValue->asLOB = lob->handle; return 0; } if (transformNum == CXO_TRANSFORM_NCLOB) encoding = nencoding; if (cxoBuffer_fromObject(buffer, pyValue, encoding) < 0) return -1; if (var) { Py_BEGIN_ALLOW_THREADS status = dpiLob_setFromBytes(dbValue->asLOB, buffer->ptr, buffer->size); Py_END_ALLOW_THREADS if (status < 0) return cxoError_raiseAndReturnInt(); } else { *nativeTypeNum = DPI_NATIVE_TYPE_BYTES; dbValue->asBytes.ptr = (char*) buffer->ptr; dbValue->asBytes.length = buffer->size; } return 0; case CXO_TRANSFORM_NATIVE_INT: if (PyBool_Check(pyValue)) { dbValue->asInt64 = (pyValue == Py_True); return 0; } if (!PyFloat_Check(pyValue) && !PyLong_Check(pyValue) && !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal)) { PyErr_SetString(PyExc_TypeError, "expecting number or boolean"); return -1; } tempValue = PyObject_CallFunctionObjArgs((PyObject*) &PyLong_Type, pyValue, NULL); if (!tempValue) return -1; dbValue->asInt64 = PyLong_AsLong(tempValue); status = (PyErr_Occurred()) ? -1 : 0; Py_DECREF(tempValue); return status; case CXO_TRANSFORM_INT: case CXO_TRANSFORM_DECIMAL: case CXO_TRANSFORM_FLOAT: if (PyBool_Check(pyValue)) { buffer->ptr = (pyValue == Py_True) ? "1" : "0"; buffer->size = 1; buffer->numCharacters = 1; } else { if (!PyFloat_Check(pyValue) && !PyLong_Check(pyValue) && !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal)) { PyErr_SetString(PyExc_TypeError, "expecting number"); return -1; } tempValue = PyObject_Str(pyValue); if (!tempValue) return -1; status = cxoBuffer_fromObject(buffer, tempValue, encoding); Py_DECREF(tempValue); if (status < 0) return -1; } dbValue->asBytes.ptr = (char*) buffer->ptr; dbValue->asBytes.length = buffer->size; return 0; case CXO_TRANSFORM_NATIVE_DOUBLE: case CXO_TRANSFORM_NATIVE_FLOAT: if (!PyFloat_Check(pyValue) && !PyObject_TypeCheck(pyValue, cxoPyTypeDecimal) && !PyLong_Check(pyValue)) { PyErr_SetString(PyExc_TypeError, "expecting float"); return -1; } if (transformNum == CXO_TRANSFORM_NATIVE_FLOAT) dbValue->asFloat = (float) PyFloat_AsDouble(pyValue); else dbValue->asDouble = PyFloat_AsDouble(pyValue); if (PyErr_Occurred()) return -1; return 0; case CXO_TRANSFORM_OBJECT: if (Py_TYPE(pyValue) != &cxoPyTypeObject) { PyErr_SetString(PyExc_TypeError, "expecting cx_Oracle.Object"); return -1; } obj = (cxoObject*) pyValue; if (var) { if (dpiVar_setFromObject(var->handle, arrayPos, obj->handle) < 0) return cxoError_raiseAndReturnInt(); } else dbValue->asObject = obj->handle; return 0; case CXO_TRANSFORM_DATE: case CXO_TRANSFORM_DATETIME: case CXO_TRANSFORM_TIMESTAMP: case CXO_TRANSFORM_TIMESTAMP_LTZ: case CXO_TRANSFORM_TIMESTAMP_TZ: if (PyDateTime_Check(pyValue)) { memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp)); dbValue->asTimestamp.year = PyDateTime_GET_YEAR(pyValue); dbValue->asTimestamp.month = PyDateTime_GET_MONTH(pyValue); dbValue->asTimestamp.day = PyDateTime_GET_DAY(pyValue); dbValue->asTimestamp.hour = PyDateTime_DATE_GET_HOUR(pyValue); dbValue->asTimestamp.minute = PyDateTime_DATE_GET_MINUTE(pyValue); dbValue->asTimestamp.second = PyDateTime_DATE_GET_SECOND(pyValue); dbValue->asTimestamp.fsecond = PyDateTime_DATE_GET_MICROSECOND(pyValue) * 1000; } else if (PyDate_Check(pyValue)) { memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp)); dbValue->asTimestamp.year = PyDateTime_GET_YEAR(pyValue); dbValue->asTimestamp.month = PyDateTime_GET_MONTH(pyValue); dbValue->asTimestamp.day = PyDateTime_GET_DAY(pyValue); } else { PyErr_SetString(PyExc_TypeError, "expecting date or datetime"); return -1; } return 0; case CXO_TRANSFORM_TIMEDELTA: if (!PyDelta_Check(pyValue)) { PyErr_SetString(PyExc_TypeError, "expecting timedelta"); return -1; } delta = (PyDateTime_Delta*) pyValue; interval = &dbValue->asIntervalDS; deltaSeconds = PyDateTime_DELTA_GET_SECONDS(delta); interval->days = PyDateTime_DELTA_GET_DAYS(delta); interval->hours = deltaSeconds / 3600; interval->seconds = deltaSeconds % 3600; interval->minutes = interval->seconds / 60; interval->seconds = interval->seconds % 60; interval->fseconds = PyDateTime_DELTA_GET_MICROSECONDS(delta) * 1000; return 0; case CXO_TRANSFORM_JSON: status = cxoJsonBuffer_fromObject(&jsonBuffer, pyValue); if (status < 0) { cxoJsonBuffer_free(&jsonBuffer); return -1; } status = dpiJson_setValue(dbValue->asJson, &jsonBuffer.topNode); cxoJsonBuffer_free(&jsonBuffer); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; default: break; } cxoError_raiseFromString(cxoNotSupportedErrorException, "Python value cannot be converted to a database value"); return -1; } //----------------------------------------------------------------------------- // cxoTransform_getDefaultSize() // Return the default size for the specified transform. //----------------------------------------------------------------------------- uint32_t cxoTransform_getDefaultSize(cxoTransformNum transformNum) { switch (transformNum) { case CXO_TRANSFORM_NONE: return 1; case CXO_TRANSFORM_BINARY: case CXO_TRANSFORM_NSTRING: case CXO_TRANSFORM_STRING: return 4000; case CXO_TRANSFORM_DECIMAL: case CXO_TRANSFORM_FLOAT: case CXO_TRANSFORM_INT: return 1000; case CXO_TRANSFORM_FIXED_CHAR: case CXO_TRANSFORM_FIXED_NCHAR: return 2000; case CXO_TRANSFORM_LONG_BINARY: case CXO_TRANSFORM_LONG_STRING: return 128 * 1024; default: break; } return 0; } //----------------------------------------------------------------------------- // cxoTransform_getNumFromDataTypeInfo() // Get the default transformation to use for the specified data type. //----------------------------------------------------------------------------- cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info) { switch (info->oracleTypeNum) { case DPI_ORACLE_TYPE_VARCHAR: return CXO_TRANSFORM_STRING; case DPI_ORACLE_TYPE_NVARCHAR: return CXO_TRANSFORM_NSTRING; case DPI_ORACLE_TYPE_CHAR: return CXO_TRANSFORM_FIXED_CHAR; case DPI_ORACLE_TYPE_NCHAR: return CXO_TRANSFORM_FIXED_NCHAR; case DPI_ORACLE_TYPE_ROWID: return CXO_TRANSFORM_ROWID; case DPI_ORACLE_TYPE_RAW: return CXO_TRANSFORM_BINARY; case DPI_ORACLE_TYPE_NATIVE_DOUBLE: return CXO_TRANSFORM_NATIVE_DOUBLE; case DPI_ORACLE_TYPE_NATIVE_FLOAT: return CXO_TRANSFORM_NATIVE_FLOAT; case DPI_ORACLE_TYPE_NUMBER: if (info->scale == 0 || (info->scale == -127 && info->precision == 0)) return CXO_TRANSFORM_INT; return CXO_TRANSFORM_FLOAT; case DPI_ORACLE_TYPE_NATIVE_INT: return CXO_TRANSFORM_NATIVE_INT; case DPI_ORACLE_TYPE_DATE: return CXO_TRANSFORM_DATETIME; case DPI_ORACLE_TYPE_TIMESTAMP: return CXO_TRANSFORM_TIMESTAMP; case DPI_ORACLE_TYPE_TIMESTAMP_TZ: return CXO_TRANSFORM_TIMESTAMP_TZ; case DPI_ORACLE_TYPE_TIMESTAMP_LTZ: return CXO_TRANSFORM_TIMESTAMP_LTZ; case DPI_ORACLE_TYPE_INTERVAL_DS: return CXO_TRANSFORM_TIMEDELTA; case DPI_ORACLE_TYPE_CLOB: return CXO_TRANSFORM_CLOB; case DPI_ORACLE_TYPE_NCLOB: return CXO_TRANSFORM_NCLOB; case DPI_ORACLE_TYPE_BLOB: return CXO_TRANSFORM_BLOB; case DPI_ORACLE_TYPE_BFILE: return CXO_TRANSFORM_BFILE; case DPI_ORACLE_TYPE_STMT: return CXO_TRANSFORM_CURSOR; case DPI_ORACLE_TYPE_OBJECT: return CXO_TRANSFORM_OBJECT; case DPI_ORACLE_TYPE_LONG_VARCHAR: return CXO_TRANSFORM_LONG_STRING; case DPI_ORACLE_TYPE_LONG_RAW: return CXO_TRANSFORM_LONG_BINARY; case DPI_ORACLE_TYPE_BOOLEAN: return CXO_TRANSFORM_BOOLEAN; case DPI_ORACLE_TYPE_JSON: return CXO_TRANSFORM_JSON; default: break; } return CXO_TRANSFORM_UNSUPPORTED; } //----------------------------------------------------------------------------- // cxoTransform_getNumFromPythonType() // Get the appropriate transformation to use for the specified Python type. //----------------------------------------------------------------------------- static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type) { if (type == &PyUnicode_Type) return CXO_TRANSFORM_STRING; if (type == &PyBytes_Type) return CXO_TRANSFORM_BINARY; if (type == &PyFloat_Type) return CXO_TRANSFORM_FLOAT; if (type == &PyLong_Type) return CXO_TRANSFORM_INT; if (type == cxoPyTypeDecimal) return CXO_TRANSFORM_DECIMAL; if (type == &PyBool_Type) return CXO_TRANSFORM_BOOLEAN; if (type == PyDateTimeAPI->DateType) return CXO_TRANSFORM_DATE; if (type == PyDateTimeAPI->DateTimeType) return CXO_TRANSFORM_DATETIME; if (type == PyDateTimeAPI->DeltaType) return CXO_TRANSFORM_TIMEDELTA; return CXO_TRANSFORM_UNSUPPORTED; } //----------------------------------------------------------------------------- // cxoTransform_getNumFromPythonValue() // Get the appropriate transformation to use for the specified Python value. //----------------------------------------------------------------------------- cxoTransformNum cxoTransform_getNumFromPythonValue(PyObject *value, int plsql) { cxoLob *lob; if (value == Py_None) return CXO_TRANSFORM_NONE; if (PyBool_Check(value)) { if (cxoClientVersionInfo.versionNum < 12 || !plsql) return CXO_TRANSFORM_NATIVE_INT; return CXO_TRANSFORM_BOOLEAN; } if (PyUnicode_Check(value)) return CXO_TRANSFORM_STRING; if (PyBytes_Check(value)) return CXO_TRANSFORM_BINARY; if (PyLong_Check(value)) return CXO_TRANSFORM_INT; if (PyFloat_Check(value)) return CXO_TRANSFORM_FLOAT; if (PyDateTime_Check(value)) return CXO_TRANSFORM_DATETIME; if (PyDate_Check(value)) return CXO_TRANSFORM_DATE; if (PyDelta_Check(value)) return CXO_TRANSFORM_TIMEDELTA; if (PyObject_TypeCheck(value, &cxoPyTypeCursor)) return CXO_TRANSFORM_CURSOR; if (PyObject_TypeCheck(value, cxoPyTypeDecimal)) return CXO_TRANSFORM_DECIMAL; if (PyObject_TypeCheck(value, &cxoPyTypeObject)) return CXO_TRANSFORM_OBJECT; if (PyObject_TypeCheck(value, &cxoPyTypeLob)) { lob = (cxoLob*) value; return lob->dbType->defaultTransformNum; } return CXO_TRANSFORM_UNSUPPORTED; } //----------------------------------------------------------------------------- // cxoTransform_getNumFromType() // Get the appropriate transformation to use for the specified type. This // can be either a database type constant defined at the module level or a // Python type. //----------------------------------------------------------------------------- int cxoTransform_getNumFromType(PyObject *type, cxoTransformNum *transformNum, cxoObjectType **objType) { PyTypeObject *pyType; cxoApiType *apiType; cxoDbType *dbType; char message[250]; int status; // check to see if a database type constant has been specified status = PyObject_IsInstance(type, (PyObject*) &cxoPyTypeDbType); if (status < 0) return -1; if (status == 1) { dbType = (cxoDbType*) type; *transformNum = dbType->defaultTransformNum; *objType = NULL; return 0; } // check to see if a DB API type constant has been specified status = PyObject_IsInstance(type, (PyObject*) &cxoPyTypeApiType); if (status < 0) return -1; if (status == 1) { apiType = (cxoApiType*) type; *transformNum = apiType->defaultTransformNum; *objType = NULL; return 0; } // check to see if an object type has been specified if (Py_TYPE(type) == &cxoPyTypeObjectType) { *transformNum = CXO_TRANSFORM_OBJECT; *objType = (cxoObjectType*) type; return 0; } // check to see if a Python type has been specified if (Py_TYPE(type) != &PyType_Type) { PyErr_SetString(PyExc_TypeError, "expecting type"); return -1; } // check to see if the Python type is a supported type pyType = (PyTypeObject*) type; *objType = NULL; *transformNum = cxoTransform_getNumFromPythonType(pyType); if (*transformNum != CXO_TRANSFORM_UNSUPPORTED) return 0; // no valid type specified snprintf(message, sizeof(message), "Python type %s not supported.", pyType->tp_name); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return -1; } //----------------------------------------------------------------------------- // cxoTransform_getNumFromValue() // Get the appropriate transformation to use for the specified value. If the // value is an array, determine the transformation that can be used for all of // the elements in that array. //----------------------------------------------------------------------------- int cxoTransform_getNumFromValue(PyObject *value, int *isArray, Py_ssize_t *size, Py_ssize_t *numElements, int plsql, cxoTransformNum *transformNum) { cxoTransformNum tempTransformNum; PyObject *elementValue; Py_ssize_t i, tempSize; char message[250]; // initialization (except numElements which always has a valid value and is // only overridden when a an array is encountered) *size = 0; *isArray = 0; // handle arrays if (PyList_Check(value)) { *transformNum = CXO_TRANSFORM_NONE; for (i = 0; i < PyList_GET_SIZE(value); i++) { elementValue = PyList_GET_ITEM(value, i); tempTransformNum = cxoTransform_getNumFromPythonValue(elementValue, 1); if (tempTransformNum == CXO_TRANSFORM_UNSUPPORTED) { snprintf(message, sizeof(message), "element %u value of type %s is not supported", (unsigned) i, Py_TYPE(value)->tp_name); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return -1; } else if (*transformNum == CXO_TRANSFORM_NONE) { *transformNum = tempTransformNum; } else if (*transformNum != tempTransformNum) { snprintf(message, sizeof(message), "element %u value is not the same type as previous " "elements", (unsigned) i); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return -1; } tempSize = cxoTransform_calculateSize(elementValue, *transformNum); if (tempSize > *size) *size = tempSize; } *isArray = 1; *numElements = PyList_GET_SIZE(value); return 0; } // handle scalar values *transformNum = cxoTransform_getNumFromPythonValue(value, plsql); if (*transformNum == CXO_TRANSFORM_UNSUPPORTED) { snprintf(message, sizeof(message), "Python value of type %s not supported.", Py_TYPE(value)->tp_name); cxoError_raiseFromString(cxoNotSupportedErrorException, message); return -1; } *size = cxoTransform_calculateSize(value, *transformNum); return 0; } //----------------------------------------------------------------------------- // cxoTransform_getTypeInfo() // Get type information for the specified transform. The transform number is // assumed to be a valid value at this point (not CXO_TRANSFORM_UNSUPPORTED). //----------------------------------------------------------------------------- void cxoTransform_getTypeInfo(cxoTransformNum transformNum, dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum) { const cxoTransform *transform; transform = &cxoAllTransforms[transformNum]; *oracleTypeNum = transform->oracleTypeNum; *nativeTypeNum = transform->nativeTypeNum; } //----------------------------------------------------------------------------- // cxoTransform_init() // Import the necessary modules for performing transformations. //----------------------------------------------------------------------------- int cxoTransform_init(void) { PyObject *module; // import the datetime module for datetime support PyDateTime_IMPORT; if (PyErr_Occurred()) return -1; cxoPyTypeDate = PyDateTimeAPI->DateType; cxoPyTypeDateTime = PyDateTimeAPI->DateTimeType; // import the decimal module for decimal support module = PyImport_ImportModule("decimal"); if (!module) return -1; cxoPyTypeDecimal = (PyTypeObject*) PyObject_GetAttrString(module, "Decimal"); Py_DECREF(module); if (!cxoPyTypeDecimal) return -1; return 0; } //----------------------------------------------------------------------------- // cxoTransform_timestampFromTicks() // Creates a timestamp from ticks (number of seconds since Unix epoch). //----------------------------------------------------------------------------- PyObject *cxoTransform_timestampFromTicks(PyObject *args) { return PyDateTime_FromTimestamp(args); } //----------------------------------------------------------------------------- // cxoTransform_toPython() // Transforms a database value into its corresponding Python object. //----------------------------------------------------------------------------- PyObject *cxoTransform_toPython(cxoTransformNum transformNum, cxoConnection *connection, cxoObjectType *objType, dpiDataBuffer *dbValue, const char *encodingErrors) { PyObject *stringObj, *result; dpiIntervalDS *intervalDS; dpiTimestamp *timestamp; dpiJsonNode *jsonNode; uint32_t rowidLength; cxoDbType *dbType; const char *rowid; cxoCursor *cursor; dpiBytes *bytes; int32_t seconds; switch (transformNum) { case CXO_TRANSFORM_BINARY: case CXO_TRANSFORM_LONG_BINARY: bytes = &dbValue->asBytes; return PyBytes_FromStringAndSize(bytes->ptr, bytes->length); case CXO_TRANSFORM_BFILE: case CXO_TRANSFORM_BLOB: case CXO_TRANSFORM_CLOB: case CXO_TRANSFORM_NCLOB: dbType = cxoDbType_fromTransformNum(transformNum); return cxoLob_new(connection, dbType, dbValue->asLOB); case CXO_TRANSFORM_BOOLEAN: if (dbValue->asBoolean) Py_RETURN_TRUE; Py_RETURN_FALSE; case CXO_TRANSFORM_CURSOR: cursor = (cxoCursor*) PyObject_CallMethod((PyObject*) connection, "cursor", NULL); if (!cursor) return NULL; cursor->handle = dbValue->asStmt; dpiStmt_addRef(cursor->handle); cursor->fixupRefCursor = 1; return (PyObject*) cursor; case CXO_TRANSFORM_DATE: timestamp = &dbValue->asTimestamp; return PyDate_FromDate(timestamp->year, timestamp->month, timestamp->day); case CXO_TRANSFORM_DATETIME: case CXO_TRANSFORM_TIMESTAMP: case CXO_TRANSFORM_TIMESTAMP_LTZ: case CXO_TRANSFORM_TIMESTAMP_TZ: timestamp = &dbValue->asTimestamp; return PyDateTime_FromDateAndTime(timestamp->year, timestamp->month, timestamp->day, timestamp->hour, timestamp->minute, timestamp->second, timestamp->fsecond / 1000); case CXO_TRANSFORM_FIXED_CHAR: case CXO_TRANSFORM_FIXED_NCHAR: case CXO_TRANSFORM_LONG_STRING: case CXO_TRANSFORM_NSTRING: case CXO_TRANSFORM_STRING: bytes = &dbValue->asBytes; return PyUnicode_Decode(bytes->ptr, bytes->length, bytes->encoding, encodingErrors); case CXO_TRANSFORM_NATIVE_DOUBLE: return PyFloat_FromDouble(dbValue->asDouble); case CXO_TRANSFORM_NATIVE_FLOAT: return PyFloat_FromDouble(dbValue->asFloat); case CXO_TRANSFORM_NATIVE_INT: return PyLong_FromLongLong(dbValue->asInt64); case CXO_TRANSFORM_DECIMAL: case CXO_TRANSFORM_INT: case CXO_TRANSFORM_FLOAT: bytes = &dbValue->asBytes; stringObj = PyUnicode_Decode(bytes->ptr, bytes->length, bytes->encoding, encodingErrors); if (!stringObj) return NULL; if (transformNum == CXO_TRANSFORM_INT && memchr(bytes->ptr, '.', bytes->length) == NULL) { result = PyNumber_Long(stringObj); } else if (transformNum == CXO_TRANSFORM_DECIMAL) { result = PyObject_CallFunctionObjArgs( (PyObject*) cxoPyTypeDecimal, stringObj, NULL); } else { result = PyNumber_Float(stringObj); } Py_DECREF(stringObj); return result; case CXO_TRANSFORM_OBJECT: return cxoObject_new(objType, dbValue->asObject); case CXO_TRANSFORM_ROWID: if (dpiRowid_getStringValue(dbValue->asRowid, &rowid, &rowidLength) < 0) return cxoError_raiseAndReturnNull(); return PyUnicode_Decode(rowid, rowidLength, connection->encodingInfo.encoding, NULL); case CXO_TRANSFORM_JSON: if (dpiJson_getValue(dbValue->asJson, DPI_JSON_OPT_NUMBER_AS_STRING, &jsonNode) < 0) return cxoError_raiseAndReturnNull(); return cxoTransform_toPythonFromJson(connection, jsonNode, encodingErrors); case CXO_TRANSFORM_TIMEDELTA: intervalDS = &dbValue->asIntervalDS; seconds = intervalDS->hours * 60 * 60 + intervalDS->minutes * 60 + intervalDS->seconds; return PyDelta_FromDSU(intervalDS->days, seconds, intervalDS->fseconds / 1000); default: break; } return cxoError_raiseFromString(cxoNotSupportedErrorException, "Database value cannot be converted to a Python value"); } //----------------------------------------------------------------------------- // cxoTransform_toPythonFromJson() // Transforms a JSON node to its equivalent Python value. //----------------------------------------------------------------------------- static PyObject *cxoTransform_toPythonFromJson(cxoConnection *connection, dpiJsonNode *node, const char *encodingErrors) { PyObject *result, *temp, *name; cxoTransformNum transformNum; dpiJsonArray *array; dpiJsonObject *obj; uint32_t i; // null is a special case if (node->nativeTypeNum == DPI_NATIVE_TYPE_NULL) Py_RETURN_NONE; switch (node->oracleTypeNum) { case DPI_ORACLE_TYPE_NUMBER: transformNum = (node->nativeTypeNum == DPI_NATIVE_TYPE_DOUBLE) ? CXO_TRANSFORM_NATIVE_DOUBLE : CXO_TRANSFORM_DECIMAL; break; case DPI_ORACLE_TYPE_VARCHAR: transformNum = CXO_TRANSFORM_STRING; break; case DPI_ORACLE_TYPE_RAW: transformNum = CXO_TRANSFORM_BINARY; break; case DPI_ORACLE_TYPE_DATE: case DPI_ORACLE_TYPE_TIMESTAMP: transformNum = CXO_TRANSFORM_DATETIME; break; case DPI_ORACLE_TYPE_BOOLEAN: transformNum = CXO_TRANSFORM_BOOLEAN; break; case DPI_ORACLE_TYPE_INTERVAL_DS: transformNum = CXO_TRANSFORM_TIMEDELTA; break; case DPI_ORACLE_TYPE_JSON_OBJECT: obj = &node->value->asJsonObject; result = PyDict_New(); for (i = 0; i < obj->numFields; i++) { name = PyUnicode_DecodeUTF8(obj->fieldNames[i], obj->fieldNameLengths[i], NULL); if (!name) return NULL; temp = cxoTransform_toPythonFromJson(connection, &obj->fields[i], encodingErrors); if (!temp) return NULL; if (PyDict_SetItem(result, name, temp) < 0) { Py_DECREF(name); Py_DECREF(temp); return NULL; } Py_DECREF(name); Py_DECREF(temp); } return result; case DPI_ORACLE_TYPE_JSON_ARRAY: array = &node->value->asJsonArray; result = PyList_New(array->numElements); for (i = 0; i < array->numElements; i++) { temp = cxoTransform_toPythonFromJson(connection, &array->elements[i], encodingErrors); if (!temp) { Py_DECREF(result); return NULL; } PyList_SET_ITEM(result, i, temp); } return result; default: transformNum = CXO_TRANSFORM_UNSUPPORTED; } return cxoTransform_toPython(transformNum, connection, NULL, node->value, encodingErrors); } python-cx_Oracle-8.3.0/src/cxoUtils.c000066400000000000000000000300371414105416400175100ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoUtils.c // Utility functions used in cx_Oracle. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoUtils_convertOciAttrToPythonValue() // Convert the OCI attribute value to an equivalent Python value using the // specified type. //----------------------------------------------------------------------------- PyObject *cxoUtils_convertOciAttrToPythonValue(unsigned attrType, dpiDataBuffer *value, uint32_t valueLength, const char *encoding) { switch (attrType) { case CXO_OCI_ATTR_TYPE_STRING: if (!value->asString) { Py_RETURN_NONE; } return PyUnicode_Decode(value->asString, valueLength, encoding, NULL); case CXO_OCI_ATTR_TYPE_BOOLEAN: if (value->asBoolean) { Py_RETURN_TRUE; } Py_RETURN_FALSE; case CXO_OCI_ATTR_TYPE_UINT8: return PyLong_FromUnsignedLong(value->asUint8); case CXO_OCI_ATTR_TYPE_UINT16: return PyLong_FromUnsignedLong(value->asUint16); case CXO_OCI_ATTR_TYPE_UINT32: return PyLong_FromUnsignedLong(value->asUint32); case CXO_OCI_ATTR_TYPE_UINT64: return PyLong_FromUnsignedLongLong(value->asUint64); } return cxoError_raiseFromString(cxoProgrammingErrorException, "invalid attribute type specified"); } //----------------------------------------------------------------------------- // cxoUtils_convertPythonValueToOciAttr() // Convert the Python value to an equivalent OCI attribute value using the // specified type. //----------------------------------------------------------------------------- int cxoUtils_convertPythonValueToOciAttr(PyObject *value, unsigned attrType, cxoBuffer *buffer, dpiDataBuffer *ociBuffer, void **ociValue, uint32_t *ociValueLength, const char *encoding) { unsigned long tempValue; switch (attrType) { case CXO_OCI_ATTR_TYPE_STRING: if (cxoBuffer_fromObject(buffer, value, encoding) < 0) return -1; *ociValue = (void*) buffer->ptr; *ociValueLength = (uint32_t) buffer->size; break; case CXO_OCI_ATTR_TYPE_BOOLEAN: ociBuffer->asBoolean = PyObject_IsTrue(value); if (PyErr_Occurred()) return -1; *ociValue = &ociBuffer->asBoolean; *ociValueLength = sizeof(ociBuffer->asBoolean); break; case CXO_OCI_ATTR_TYPE_UINT8: tempValue = PyLong_AsUnsignedLong(value); if (PyErr_Occurred()) return -1; if (tempValue > UINT8_MAX) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to uint8_t"); return -1; } ociBuffer->asUint8 = (uint8_t) tempValue; *ociValue = &ociBuffer->asUint8; *ociValueLength = sizeof(ociBuffer->asUint8); break; case CXO_OCI_ATTR_TYPE_UINT16: tempValue = PyLong_AsUnsignedLong(value); if (PyErr_Occurred()) return -1; if (tempValue > UINT16_MAX) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to uint16_t"); return -1; } ociBuffer->asUint16 = (uint16_t) tempValue; *ociValue = &ociBuffer->asUint16; *ociValueLength = sizeof(ociBuffer->asUint16); break; case CXO_OCI_ATTR_TYPE_UINT32: tempValue = PyLong_AsUnsignedLong(value); if (PyErr_Occurred()) return -1; if (tempValue > UINT32_MAX) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to uint32_t"); return -1; } ociBuffer->asUint32 = (uint32_t) tempValue; *ociValue = &ociBuffer->asUint32; *ociValueLength = sizeof(ociBuffer->asUint32); break; case CXO_OCI_ATTR_TYPE_UINT64: ociBuffer->asUint64 = (uint64_t) PyLong_AsUnsignedLongLong(value); if (PyErr_Occurred()) return -1; *ociValue = &ociBuffer->asUint64; *ociValueLength = sizeof(ociBuffer->asUint64); break; default: cxoError_raiseFromString(cxoProgrammingErrorException, "invalid attribute type specified"); return -1; } return 0; } //----------------------------------------------------------------------------- // cxoUtils_formatString() // Return a Python string formatted using the given format string and // arguments. The arguments have a reference taken from them after they have // been used (which should mean that they are destroyed). //----------------------------------------------------------------------------- PyObject *cxoUtils_formatString(const char *format, PyObject *args) { PyObject *formatObj, *result; // assume that a NULL value for arguments implies building the arguments // failed and a Python exception has already been raised if (!args) return NULL; // convert string format to Python object formatObj = PyUnicode_DecodeASCII(format, strlen(format), NULL); if (!formatObj) { Py_DECREF(args); return NULL; } // create formatted result result = PyUnicode_Format(formatObj, args); Py_DECREF(args); Py_DECREF(formatObj); return result; } //----------------------------------------------------------------------------- // cxoUtils_getAdjustedEncoding() // Return the adjusted encoding to use when encoding and decoding strings // that are passed to and from the Oracle database. The Oracle client interface // does not support the inclusion of a BOM in the encoded string but assumes // native endian order for UTF-16. Python generates a BOM at the beginning of // the encoded string if plain UTF-16 is specified. For this reason, the // correct byte order must be determined and used inside Python so that the // Oracle client receives the data it expects. //----------------------------------------------------------------------------- const char *cxoUtils_getAdjustedEncoding(const char *encoding) { static const union { unsigned char bytes[4]; uint32_t value; } hostOrder = { { 0, 1, 2, 3 } }; if (!encoding || strcmp(encoding, "UTF-16") != 0) return encoding; return (hostOrder.value == 0x03020100) ? "UTF-16LE" : "UTF-16BE"; } //----------------------------------------------------------------------------- // cxoUtils_getModuleAndName() // Return the module and name for the type. //----------------------------------------------------------------------------- int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module, PyObject **name) { *module = PyObject_GetAttrString( (PyObject*) type, "__module__"); if (!*module) return -1; *name = PyObject_GetAttrString( (PyObject*) type, "__name__"); if (!*name) { Py_DECREF(*module); return -1; } return 0; } //----------------------------------------------------------------------------- // cxoUtils_initializeDPI() // Initialize the ODPI-C library. This is done when the first standalone // connection or session pool is created, rather than when the module is first // imported so that manipulating environment variables such as NLS_LANG will // work as expected. It also has the additional benefit of reducing the number // of errors that can take place when the module is imported. //----------------------------------------------------------------------------- int cxoUtils_initializeDPI(dpiContextCreateParams *params) { dpiContextCreateParams localParams; dpiErrorInfo errorInfo; dpiContext *context; // if already initialized and parameters were passed, raise an exception; // otherwise do nothing as this is implicitly called when creating a // standalone connection or session pool and when getting the Oracle Client // library version if (cxoDpiContext) { if (!params) return 0; cxoError_raiseFromString(cxoProgrammingErrorException, "Oracle Client library has already been initialized"); return -1; } // set up parameters used for initializing ODPI-C if (params) { memcpy(&localParams, params, sizeof(dpiContextCreateParams)); } else { memset(&localParams, 0, sizeof(dpiContextCreateParams)); } localParams.defaultEncoding = "UTF-8"; if (!localParams.defaultDriverName) localParams.defaultDriverName = CXO_DRIVER_NAME; if (!localParams.loadErrorUrl) localParams.loadErrorUrl = "https://cx-oracle.readthedocs.io/en/" "latest/user_guide/installation.html"; // create ODPI-C context with the specified parameters if (dpiContext_createWithParams(DPI_MAJOR_VERSION, DPI_MINOR_VERSION, &localParams, &context, &errorInfo) < 0) return cxoError_raiseFromInfo(&errorInfo); if (dpiContext_getClientVersion(context, &cxoClientVersionInfo) < 0) { cxoError_raiseAndReturnInt(); dpiContext_destroy(context); return -1; } cxoDpiContext = context; return 0; } //----------------------------------------------------------------------------- // cxoUtils_processJsonArg() // Process the argument which is expected to be either a string or bytes, or // a dictionary or list which is converted to a string via the json.dumps() // method. All strings are encoded to UTF-8 which is what SODA expects. //----------------------------------------------------------------------------- int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer) { int converted = 0; if (arg && (PyDict_Check(arg) || PyList_Check(arg))) { arg = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, arg, NULL); if (!arg) return -1; converted = 1; } if (cxoBuffer_fromObject(buffer, arg, "UTF-8") < 0) return -1; if (converted) Py_DECREF(arg); return 0; } //----------------------------------------------------------------------------- // cxoUtils_processSodaDocArg() // Process a SODA document argument. This is expectd to be an actual SODA // document object or a dictionary. If the argument refers to a dictionary or // list, a new SODA document will be created with the given content and without // a key or media type specified. //----------------------------------------------------------------------------- int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg, dpiSodaDoc **handle) { cxoBuffer buffer; cxoSodaDoc *doc; if (PyObject_TypeCheck(arg, &cxoPyTypeSodaDoc)) { doc = (cxoSodaDoc*) arg; if (dpiSodaDoc_addRef(doc->handle) < 0) return cxoError_raiseAndReturnInt(); *handle = doc->handle; } else if (PyDict_Check(arg) || PyList_Check(arg)) { arg = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, arg, NULL); if (!arg) return -1; if (cxoBuffer_fromObject(&buffer, arg, "UTF-8") < 0) { Py_DECREF(arg); return -1; } Py_DECREF(arg); if (dpiSodaDb_createDocument(db->handle, NULL, 0, buffer.ptr, buffer.size, NULL, 0, DPI_SODA_FLAGS_DEFAULT, handle) < 0) { cxoBuffer_clear(&buffer); return cxoError_raiseAndReturnInt(); } cxoBuffer_clear(&buffer); } else { PyErr_SetString(PyExc_TypeError, "value must be a SODA document or a dictionary or list"); return -1; } return 0; } python-cx_Oracle-8.3.0/src/cxoVar.c000066400000000000000000000651561414105416400171520ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. // // Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. // // Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, // Canada. All rights reserved. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // cxoVar.c // Defines Python types for Oracle variables. //----------------------------------------------------------------------------- #include "cxoModule.h" //----------------------------------------------------------------------------- // cxoVar_new() // Allocate a new variable. //----------------------------------------------------------------------------- cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoTransformNum transformNum, Py_ssize_t size, int isArray, cxoObjectType *objType) { dpiObjectType *typeHandle = NULL; dpiOracleTypeNum oracleTypeNum; cxoVar *var; // attempt to allocate the object var = (cxoVar*) cxoPyTypeVar.tp_alloc(&cxoPyTypeVar, 0); if (!var) return NULL; // perform basic initialization Py_INCREF(cursor->connection); var->connection = cursor->connection; if (objType) { Py_INCREF(objType); var->objectType = objType; typeHandle = objType->handle; } if (numElements == 0) numElements = 1; var->allocatedElements = (uint32_t) numElements; var->transformNum = transformNum; var->size = (uint32_t) size; if (var->size == 0) var->size = cxoTransform_getDefaultSize(transformNum); var->isArray = isArray; // determine database type var->dbType = cxoDbType_fromTransformNum(var->transformNum); if (!var->dbType) { Py_DECREF(var); return NULL; } Py_INCREF(var->dbType); // acquire and initialize DPI variable cxoTransform_getTypeInfo(transformNum, &oracleTypeNum, &var->nativeTypeNum); if (dpiConn_newVar(cursor->connection->handle, oracleTypeNum, var->nativeTypeNum, var->allocatedElements, var->size, 0, isArray, typeHandle, &var->handle, &var->data) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(var); return NULL; } // get buffer size for information if (dpiVar_getSizeInBytes(var->handle, &var->bufferSize) < 0) { cxoError_raiseAndReturnNull(); Py_DECREF(var); return NULL; } return var; } //----------------------------------------------------------------------------- // cxoVar_free() // Free an existing variable. //----------------------------------------------------------------------------- static void cxoVar_free(cxoVar *var) { if (var->handle) { Py_BEGIN_ALLOW_THREADS dpiVar_release(var->handle); Py_END_ALLOW_THREADS var->handle = NULL; } if (var->encodingErrors) PyMem_Free((void*) var->encodingErrors); Py_CLEAR(var->connection); Py_CLEAR(var->inConverter); Py_CLEAR(var->outConverter); Py_CLEAR(var->objectType); Py_CLEAR(var->dbType); Py_TYPE(var)->tp_free((PyObject*) var); } //----------------------------------------------------------------------------- // cxoVar_check() // Returns a boolean indicating if the object is a variable. //----------------------------------------------------------------------------- int cxoVar_check(PyObject *object) { return (Py_TYPE(object) == &cxoPyTypeVar); } //----------------------------------------------------------------------------- // cxoVar_newByValue() // Allocate a new variable by looking at the type of the data. //----------------------------------------------------------------------------- cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value, Py_ssize_t numElements) { PyObject *result, *inputTypeHandler = NULL; cxoObjectType *objType = NULL; cxoTransformNum transformNum; Py_ssize_t size; cxoObject *obj; int isArray; // determine if an input type handler should be used; an input type handler // defined on the cursor takes precedence over one defined on the // connection to which the cursor belongs; the input type handler should // return a variable or None; the value None implies that the default // processing should take place just as if no input type handler was // defined if (cursor->inputTypeHandler && cursor->inputTypeHandler != Py_None) inputTypeHandler = cursor->inputTypeHandler; else if (cursor->connection->inputTypeHandler && cursor->connection->inputTypeHandler != Py_None) inputTypeHandler = cursor->connection->inputTypeHandler; if (inputTypeHandler) { result = PyObject_CallFunction(inputTypeHandler, "OOn", cursor, value, numElements); if (!result) return NULL; if (result != Py_None) { if (!cxoVar_check(result)) { Py_DECREF(result); PyErr_SetString(PyExc_TypeError, "expecting variable from input type handler"); return NULL; } return (cxoVar*) result; } Py_DECREF(result); } // default processing if (cxoTransform_getNumFromValue(value, &isArray, &size, &numElements, cursor->stmtInfo.isPLSQL, &transformNum) < 0) return NULL; if (transformNum == CXO_TRANSFORM_OBJECT) { obj = (cxoObject*) value; objType = obj->objectType; } return cxoVar_new(cursor, numElements, transformNum, size, isArray, objType); } //----------------------------------------------------------------------------- // cxoVar_newArrayByType() // Allocate a new PL/SQL array by looking at the Python data type. //----------------------------------------------------------------------------- static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor, PyObject *value) { PyObject *typeObj, *numElementsObj; cxoTransformNum transformNum; cxoObjectType *objType; uint32_t numElements; int ok; // validate parameters ok = (PyList_GET_SIZE(value) == 2); if (ok) { typeObj = PyList_GET_ITEM(value, 0); numElementsObj = PyList_GET_ITEM(value, 1); ok = PyLong_Check(numElementsObj); } if (!ok) { cxoError_raiseFromString(cxoProgrammingErrorException, "expecting an array of two elements [type, numelems]"); return NULL; } // create variable if (cxoTransform_getNumFromType(typeObj, &transformNum, &objType) < 0) return NULL; numElements = PyLong_AsLong(numElementsObj); if (PyErr_Occurred()) return NULL; return cxoVar_new(cursor, numElements, transformNum, 0, 1, objType); } //----------------------------------------------------------------------------- // cxoVar_newByType() // Allocate a new variable by looking at the Python data type. //----------------------------------------------------------------------------- cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value, uint32_t numElements) { cxoTransformNum transformNum; cxoObjectType *objType; long size; // passing an integer is assumed to be a string if (PyLong_Check(value)) { size = PyLong_AsLong(value); if (PyErr_Occurred()) return NULL; return cxoVar_new(cursor, numElements, CXO_TRANSFORM_STRING, size, 0, NULL); } // passing an array of two elements to define an array if (PyList_Check(value)) return cxoVar_newArrayByType(cursor, value); // handle directly bound variables if (cxoVar_check(value)) { Py_INCREF(value); return (cxoVar*) value; } // everything else ought to be a Python type, database type constant or // object type if (cxoTransform_getNumFromType(value, &transformNum, &objType) < 0) return NULL; return cxoVar_new(cursor, numElements, transformNum, 0, 0, objType); } //----------------------------------------------------------------------------- // cxoVar_bind() // Allocate a variable and bind it to the given statement. //----------------------------------------------------------------------------- int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos) { cxoBuffer nameBuffer; int status; // perform the bind if (name) { if (cxoBuffer_fromObject(&nameBuffer, name, cursor->connection->encodingInfo.encoding) < 0) return -1; status = dpiStmt_bindByName(cursor->handle, (char*) nameBuffer.ptr, nameBuffer.size, var->handle); cxoBuffer_clear(&nameBuffer); } else { status = dpiStmt_bindByPos(cursor->handle, pos, var->handle); } if (status < 0) return cxoError_raiseAndReturnInt(); // set flag if bound to a DML returning statement and no data set if (cursor->stmtInfo.isReturning && !var->isValueSet) var->getReturnedData = 1; return 0; } //----------------------------------------------------------------------------- // cxoVar_getArrayValue() // Return the value of the variable as an array. //----------------------------------------------------------------------------- static PyObject *cxoVar_getArrayValue(cxoVar *var, uint32_t numElements, dpiData *data) { PyObject *value, *singleValue; uint32_t i; value = PyList_New(numElements); if (!value) return NULL; for (i = 0; i < numElements; i++) { singleValue = cxoVar_getSingleValue(var, data, i); if (!singleValue) { Py_DECREF(value); return NULL; } PyList_SET_ITEM(value, i, singleValue); } return value; } //----------------------------------------------------------------------------- // cxoVar_getSingleValue() // Return the value of the variable at the given position. //----------------------------------------------------------------------------- PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos) { PyObject *value, *result; uint32_t numReturnedRows; dpiData *returnedData; // handle DML returning if (!data && var->getReturnedData) { if (dpiVar_getReturnedData(var->handle, arrayPos, &numReturnedRows, &returnedData) < 0) return cxoError_raiseAndReturnNull(); return cxoVar_getArrayValue(var, numReturnedRows, returnedData); } // in all other cases, just get the value stored at specified position if (data) data = &data[arrayPos]; else data = &var->data[arrayPos]; if (data->isNull) Py_RETURN_NONE; value = cxoTransform_toPython(var->transformNum, var->connection, var->objectType, &data->value, var->encodingErrors); if (value) { switch (var->transformNum) { case CXO_TRANSFORM_BFILE: case CXO_TRANSFORM_BLOB: case CXO_TRANSFORM_CLOB: case CXO_TRANSFORM_NCLOB: dpiLob_addRef(data->value.asLOB); break; case CXO_TRANSFORM_OBJECT: dpiObject_addRef(data->value.asObject); break; default: break; } if (var->outConverter && var->outConverter != Py_None) { result = PyObject_CallFunctionObjArgs(var->outConverter, value, NULL); Py_DECREF(value); return result; } } return value; } //----------------------------------------------------------------------------- // cxoVar_getValue() // Return the value of the variable. //----------------------------------------------------------------------------- PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos) { uint32_t numElements; if (var->isArray) { if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0) return cxoError_raiseAndReturnNull(); return cxoVar_getArrayValue(var, numElements, var->data); } if (arrayPos >= var->allocatedElements && !var->getReturnedData) { PyErr_SetString(PyExc_IndexError, "cxoVar_getSingleValue: array size exceeded"); return NULL; } return cxoVar_getSingleValue(var, NULL, arrayPos); } //----------------------------------------------------------------------------- // cxoVar_setValueBytes() // Set a value in the variable from a byte string of some sort. //----------------------------------------------------------------------------- static int cxoVar_setValueBytes(cxoVar *var, uint32_t pos, dpiData *data, cxoBuffer *buffer) { dpiData *tempVarData, *sourceData; dpiOracleTypeNum oracleTypeNum; dpiNativeTypeNum nativeTypeNum; uint32_t i, numElements; dpiVar *tempVarHandle; int status; if (buffer->size > var->bufferSize) { cxoTransform_getTypeInfo(var->transformNum, &oracleTypeNum, &nativeTypeNum); if (dpiConn_newVar(var->connection->handle, oracleTypeNum, nativeTypeNum, var->allocatedElements, buffer->size, 0, var->isArray, NULL, &tempVarHandle, &tempVarData) < 0) return cxoError_raiseAndReturnInt(); if (var->isArray) { if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0) { cxoError_raiseAndReturnInt(); dpiVar_release(tempVarHandle); return -1; } if (dpiVar_setNumElementsInArray(tempVarHandle, numElements) < 0) { cxoError_raiseAndReturnInt(); dpiVar_release(tempVarHandle); return -1; } } for (i = 0; i < var->allocatedElements; i++) { sourceData = &var->data[i]; if (i == pos || sourceData->isNull) continue; if (dpiVar_setFromBytes(tempVarHandle, i, sourceData->value.asBytes.ptr, sourceData->value.asBytes.length) < 0) { cxoError_raiseAndReturnInt(); dpiVar_release(tempVarHandle); return -1; } } dpiVar_release(var->handle); var->handle = tempVarHandle; var->data = tempVarData; var->size = buffer->numCharacters; var->bufferSize = buffer->size; } status = dpiVar_setFromBytes(var->handle, pos, buffer->ptr, buffer->size); if (status < 0) return cxoError_raiseAndReturnInt(); return 0; } //----------------------------------------------------------------------------- // cxoVar_setValueCursor() // Set the value of the variable (which is assumed to be a cursor). //----------------------------------------------------------------------------- static int cxoVar_setValueCursor(cxoVar *var, uint32_t pos, dpiData *data, PyObject *value) { cxoCursor *cursor; dpiStmtInfo info; if (!PyObject_IsInstance(value, (PyObject*) &cxoPyTypeCursor)) { PyErr_SetString(PyExc_TypeError, "expecting cursor"); return -1; } // if the cursor already has a handle, use it directly cursor = (cxoCursor *) value; if (cursor->handle) { if (dpiVar_setFromStmt(var->handle, pos, cursor->handle) < 0) return cxoError_raiseAndReturnInt(); // otherwise, make use of the statement handle allocated by the variable // BUT, make sure the statement handle is still valid as it may have been // closed by some other code; the call to dpiStmt_getInfo() will ensure the // statement is still open; if an error occurs, this bind will be discarded // and a second attempt will be made with a new cursor } else { if (dpiStmt_getInfo(data->value.asStmt, &info) < 0) return cxoError_raiseAndReturnInt(); cursor->handle = data->value.asStmt; dpiStmt_addRef(cursor->handle); } if (dpiStmt_setPrefetchRows(cursor->handle, cursor->prefetchRows) < 0) return cxoError_raiseAndReturnInt(); cursor->fixupRefCursor = 1; return 0; } //----------------------------------------------------------------------------- // cxoVar_setSingleValue() // Set a single value in the variable. //----------------------------------------------------------------------------- static int cxoVar_setSingleValue(cxoVar *var, uint32_t arrayPos, PyObject *value) { dpiDataBuffer tempDbValue, *dbValue; PyObject *convertedValue = NULL; dpiNativeTypeNum nativeTypeNum; cxoBuffer buffer; int result = 0; dpiData *data; // ensure we do not exceed the number of allocated elements if (arrayPos >= var->allocatedElements) { PyErr_SetString(PyExc_IndexError, "cxoVar_setSingleValue: array size exceeded"); return -1; } // convert value, if necessary if (var->inConverter && var->inConverter != Py_None) { convertedValue = PyObject_CallFunctionObjArgs(var->inConverter, value, NULL); if (!convertedValue) return -1; value = convertedValue; } // transform value from Python to value expected by ODPI-C data = &var->data[arrayPos]; data->isNull = (value == Py_None); if (!data->isNull) { if (var->transformNum == CXO_TRANSFORM_CURSOR) result = cxoVar_setValueCursor(var, arrayPos, data, value); else { cxoBuffer_init(&buffer); if (var->nativeTypeNum == DPI_NATIVE_TYPE_BYTES) dbValue = &tempDbValue; else dbValue = &data->value; result = cxoTransform_fromPython(var->transformNum, &nativeTypeNum, value, dbValue, &buffer, var->connection->encodingInfo.encoding, var->connection->encodingInfo.nencoding, var, arrayPos); if (result == 0 && var->nativeTypeNum == DPI_NATIVE_TYPE_BYTES) result = cxoVar_setValueBytes(var, arrayPos, data, &buffer); cxoBuffer_clear(&buffer); } } Py_CLEAR(convertedValue); return result; } //----------------------------------------------------------------------------- // cxoVar_setArrayValue() // Set all of the array values for the variable. //----------------------------------------------------------------------------- static int cxoVar_setArrayValue(cxoVar *var, PyObject *value) { Py_ssize_t numElements, i; // ensure we have an array to set if (!PyList_Check(value)) { PyErr_SetString(PyExc_TypeError, "expecting array data"); return -1; } // set the number of actual elements numElements = PyList_GET_SIZE(value); if (dpiVar_setNumElementsInArray(var->handle, (uint32_t) numElements) < 0) return cxoError_raiseAndReturnInt(); // set all of the values for (i = 0; i < numElements; i++) { if (cxoVar_setSingleValue(var, i, PyList_GET_ITEM(value, i)) < 0) return -1; } return 0; } //----------------------------------------------------------------------------- // cxoVar_setValue() // Set the value of the variable. //----------------------------------------------------------------------------- int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value) { var->isValueSet = 1; if (var->isArray) { if (arrayPos > 0) { cxoError_raiseFromString(cxoNotSupportedErrorException, "arrays of arrays are not supported by the OCI"); return -1; } return cxoVar_setArrayValue(var, value); } return cxoVar_setSingleValue(var, arrayPos, value); } //----------------------------------------------------------------------------- // cxoVar_externalCopy() // Copy the contents of the source variable to the destination variable. //----------------------------------------------------------------------------- static PyObject *cxoVar_externalCopy(cxoVar *targetVar, PyObject *args) { uint32_t sourcePos, targetPos; cxoVar *sourceVar; if (!PyArg_ParseTuple(args, "Oii", &sourceVar, &sourcePos, &targetPos)) return NULL; if (Py_TYPE(targetVar) != Py_TYPE(sourceVar)) return cxoError_raiseFromString(cxoProgrammingErrorException, "source and target variable type must match"); if (dpiVar_copyData(targetVar->handle, targetPos, sourceVar->handle, sourcePos) < 0) return cxoError_raiseAndReturnNull(); Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoVar_externalSetValue() // Set the value of the variable at the given position. //----------------------------------------------------------------------------- static PyObject *cxoVar_externalSetValue(cxoVar *var, PyObject *args) { PyObject *value; uint32_t pos; if (!PyArg_ParseTuple(args, "iO", &pos, &value)) return NULL; if (cxoVar_setValue(var, pos, value) < 0) return NULL; Py_RETURN_NONE; } //----------------------------------------------------------------------------- // cxoVar_externalGetValue() // Return the value of the variable at the given position. //----------------------------------------------------------------------------- static PyObject *cxoVar_externalGetValue(cxoVar *var, PyObject *args, PyObject *keywordArgs) { static char *keywordList[] = { "pos", NULL }; uint32_t pos = 0; if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList, &pos)) return NULL; return cxoVar_getValue(var, pos); } //----------------------------------------------------------------------------- // cxoVar_externalGetActualElements() // Return the values of the variable at all positions as a list. //----------------------------------------------------------------------------- static PyObject *cxoVar_externalGetActualElements(cxoVar *var, void *unused) { uint32_t numElements = var->allocatedElements; if (var->isArray && dpiVar_getNumElementsInArray(var->handle, &numElements) < 0) return cxoError_raiseAndReturnNull(); return PyLong_FromLong(numElements); } //----------------------------------------------------------------------------- // cxoVar_externalGetValues() // Return the values of the variable at all positions as a list. //----------------------------------------------------------------------------- static PyObject *cxoVar_externalGetValues(cxoVar *var, void *unused) { uint32_t numElements = var->allocatedElements; if (var->isArray && dpiVar_getNumElementsInArray(var->handle, &numElements) < 0) return cxoError_raiseAndReturnNull(); return cxoVar_getArrayValue(var, numElements, NULL); } //----------------------------------------------------------------------------- // cxoVar_getType() // Return the type associated with the variable. This is either an object // type or one of the database type constants. //----------------------------------------------------------------------------- static PyObject *cxoVar_getType(cxoVar *var, void *unused) { if (var->objectType) { Py_INCREF(var->objectType); return (PyObject*) var->objectType; } Py_INCREF(var->dbType); return (PyObject*) var->dbType; } //----------------------------------------------------------------------------- // cxoVar_repr() // Return a string representation of the variable. //----------------------------------------------------------------------------- static PyObject *cxoVar_repr(cxoVar *var) { PyObject *value, *module, *name, *result, *typeName; uint32_t numElements; if (var->isArray) { if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0) return cxoError_raiseAndReturnNull(); value = cxoVar_getArrayValue(var, numElements, var->data); } else if (var->allocatedElements == 1) value = cxoVar_getSingleValue(var, NULL, 0); else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL); if (!value) return NULL; typeName = PyUnicode_DecodeASCII(var->dbType->name, strlen(var->dbType->name), NULL); if (!typeName) { Py_DECREF(value); return NULL; } if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) { Py_DECREF(typeName); Py_DECREF(value); return NULL; } result = cxoUtils_formatString("<%s.%s of type %s with value %r>", PyTuple_Pack(4, module, name, typeName, value)); Py_DECREF(module); Py_DECREF(name); Py_DECREF(value); Py_DECREF(typeName); return result; } //----------------------------------------------------------------------------- // declaration of members //----------------------------------------------------------------------------- static PyMemberDef cxoMembers[] = { { "buffer_size", T_INT, offsetof(cxoVar, bufferSize), READONLY }, { "bufferSize", T_INT, offsetof(cxoVar, bufferSize), READONLY }, { "inconverter", T_OBJECT, offsetof(cxoVar, inConverter), 0 }, { "numElements", T_INT, offsetof(cxoVar, allocatedElements), READONLY }, { "num_elements", T_INT, offsetof(cxoVar, allocatedElements), READONLY }, { "outconverter", T_OBJECT, offsetof(cxoVar, outConverter), 0 }, { "size", T_INT, offsetof(cxoVar, size), READONLY }, { NULL } }; //----------------------------------------------------------------------------- // declaration of calculated members //----------------------------------------------------------------------------- static PyGetSetDef cxoCalcMembers[] = { { "actual_elements", (getter) cxoVar_externalGetActualElements, 0, 0, 0 }, { "actualElements", (getter) cxoVar_externalGetActualElements, 0, 0, 0 }, { "type", (getter) cxoVar_getType, 0, 0, 0 }, { "values", (getter) cxoVar_externalGetValues, 0, 0, 0 }, { NULL } }; //----------------------------------------------------------------------------- // declaration of methods //----------------------------------------------------------------------------- static PyMethodDef cxoVarMethods[] = { { "copy", (PyCFunction) cxoVar_externalCopy, METH_VARARGS }, { "setvalue", (PyCFunction) cxoVar_externalSetValue, METH_VARARGS }, { "getvalue", (PyCFunction) cxoVar_externalGetValue, METH_VARARGS | METH_KEYWORDS }, { NULL } }; //----------------------------------------------------------------------------- // declaration of Python type //----------------------------------------------------------------------------- PyTypeObject cxoPyTypeVar = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "cx_Oracle.Var", .tp_basicsize = sizeof(cxoVar), .tp_dealloc = (destructor) cxoVar_free, .tp_repr = (reprfunc) cxoVar_repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = cxoVarMethods, .tp_members = cxoMembers, .tp_getset = cxoCalcMembers }; python-cx_Oracle-8.3.0/test/000077500000000000000000000000001414105416400157175ustar00rootroot00000000000000python-cx_Oracle-8.3.0/test/README.md000066400000000000000000000046631414105416400172070ustar00rootroot00000000000000This directory contains the test suite for cx_Oracle. 1. The schemas and SQL objects that are referenced in the test suite can be created by running the Python script [setup_test.py][1]. The script requires administrative privileges and will prompt for these credentials as well as the names of the schemas that will be created, unless a number of environment variables are set, as documented in the Python script [test_env.py][2]. Run the script using the following command: python setup_test.py Alternatively, the [SQL script][3] can be run directly via SQL\*Plus, which will always prompt for the names of the schemas that will be created. Run the script using the following command: sqlplus system/systempassword@hostname/servicename @sql/setup_test.sql 2. Run the test suite by issuing the following command in the top-level directory of your cx_Oracle installation: tox This will build the module in an independent environment and run the test suite using the module that was just built in that environment. Alternatively, you can use the currently installed build of cx_Oracle and run the following command instead: python -m unittest discover -v -s test You may also run each of the test scripts independently, as in: python test_1000_module.py 3. After running the test suite, the schemas can be dropped by running the Python script [drop_test.py][4]. The script requires administrative privileges and will prompt for these credentials as well as the names of the schemas that will be dropped, unless a number of environment variables are set, as documented in the Python script [test_env.py][2]. Run the script using the following command: python drop_test.py Alternatively, the [SQL script][5] can be run directly via SQL\*Plus, which will always prompt for the names of the schemas that will be dropped. Run the script using the following command: sqlplus system/systempassword@hostname/servicename @sql/drop_test.sql [1]: https://github.com/oracle/python-cx_Oracle/blob/main/test/setup_test.py [2]: https://github.com/oracle/python-cx_Oracle/blob/main/test/test_env.py [3]: https://github.com/oracle/python-cx_Oracle/blob/main/test/sql/setup_test.sql [4]: https://github.com/oracle/python-cx_Oracle/blob/main/test/drop_test.py [5]: https://github.com/oracle/python-cx_Oracle/blob/main/test/sql/drop_test.sql python-cx_Oracle-8.3.0/test/drop_test.py000066400000000000000000000015671414105416400203050ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # drop_test.py # # Drops the database objects used by the test suite. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import test_env def drop_tests(conn): print("Dropping test schemas...") test_env.run_sql_script(conn, "drop_test", main_user=test_env.get_main_user(), proxy_user=test_env.get_proxy_user()) if __name__ == "__main__": conn = oracledb.connect(test_env.get_admin_connect_string()) drop_tests(conn) print("Done.") python-cx_Oracle-8.3.0/test/setup_test.py000066400000000000000000000022031414105416400204650ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # setup_test.py # # Creates users and populates their schemas with the tables and packages # necessary for the test suite. #------------------------------------------------------------------------------ import cx_Oracle as oracledb import drop_test import test_env # connect as administrative user (usually SYSTEM or ADMIN) conn = oracledb.connect(test_env.get_admin_connect_string()) # drop existing users and editions, if applicable drop_test.drop_tests(conn) # create test schemas print("Creating test schemas...") test_env.run_sql_script(conn, "setup_test", main_user=test_env.get_main_user(), main_password=test_env.get_main_password(), proxy_user=test_env.get_proxy_user(), proxy_password=test_env.get_proxy_password()) print("Done.") python-cx_Oracle-8.3.0/test/sql/000077500000000000000000000000001414105416400165165ustar00rootroot00000000000000python-cx_Oracle-8.3.0/test/sql/drop_test.sql000066400000000000000000000016231414105416400212440ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright 2019, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * drop_test.sql * Drops database objects used for cx_Oracle tests. * * Run this like: * sqlplus sys/syspassword@hostname/servicename as sysdba @drop_test *---------------------------------------------------------------------------*/ whenever sqlerror exit failure -- get parameters set echo off termout on feedback off verify off accept main_user char default pythontest - prompt "Name of main schema [pythontest]: " accept proxy_user char default pythontestproxy - prompt "Name of proxy schema [pythontestproxy]: " set feedback on -- perform work @@drop_test_exec.sql exit python-cx_Oracle-8.3.0/test/sql/drop_test_exec.sql000066400000000000000000000016701414105416400222520ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * drop_test_exec.sql * This script performs the actual work of dropping the database schemas used * by the cx_Oracle test suite. It is called by the drop_test.sql and * setup_test.sql scripts after acquiring the necessary parameters and also by * the Python script drop_test.py. *---------------------------------------------------------------------------*/ begin for r in ( select username from dba_users where username in (upper('&main_user'), upper('&proxy_user')) ) loop execute immediate 'drop user ' || r.username || ' cascade'; end loop; end; / python-cx_Oracle-8.3.0/test/sql/setup_test.sql000066400000000000000000000021371414105416400214410ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright 2019, Oracle and/or its affiliates. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * setup_test.sql * Creates and populates schemas with the database objects used by the * cx_Oracle test suite. * * Run this like: * sqlplus sys/syspassword@hostname/servicename as sysdba @setup_test *---------------------------------------------------------------------------*/ whenever sqlerror exit failure -- get parameters set echo off termout on feedback off verify off accept main_user char default pythontest - prompt "Name of main schema [pythontest]: " accept main_password char prompt "Password for &main_user: " HIDE accept proxy_user char default pythontestproxy - prompt "Name of edition schema [pythontestproxy]: " accept proxy_password char prompt "Password for &proxy_user: " HIDE set feedback on -- perform work @@drop_test_exec.sql @@setup_test_exec.sql exit python-cx_Oracle-8.3.0/test/sql/setup_test_exec.sql000066400000000000000000001057421414105416400224530ustar00rootroot00000000000000/*----------------------------------------------------------------------------- * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * * Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. * * Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, * Canada. All rights reserved. *---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * setup_test_exec.sql * This script performs the actual work of creating and populating the * schemas with the database objects used by the cx_Oracle test suite. It is * called by the setup_test.sql file after acquiring the necessary parameters * and also by the Python script setup_test.py. *---------------------------------------------------------------------------*/ alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS' / alter session set nls_numeric_characters='.,' / create user &main_user identified by &main_password / create user &proxy_user identified by &proxy_password / alter user &proxy_user grant connect through &main_user / grant create session to &proxy_user / grant create session, create table, create procedure, create type, select any dictionary, change notification, unlimited tablespace to &main_user / grant aq_administrator_role to &main_user / begin for r in ( select role from dba_roles where role in ('SODA_APP') ) loop execute immediate 'grant ' || r.role || ' to &main_user'; end loop; end; / -- create types create type &main_user..udt_SubObject as object ( SubNumberValue number, SubStringValue varchar2(60) ); / create type &main_user..udt_ObjectArray as varray(10) of &main_user..udt_SubObject; / create type &main_user..udt_Object as object ( NumberValue number, StringValue varchar2(60), FixedCharValue char(10), NStringValue nvarchar2(60), NFixedCharValue nchar(10), RawValue raw(16), IntValue integer, SmallIntValue smallint, RealValue real, DoublePrecisionValue double precision, FloatValue float, BinaryFloatValue binary_float, BinaryDoubleValue binary_double, DateValue date, TimestampValue timestamp, TimestampTZValue timestamp with time zone, TimestampLTZValue timestamp with local time zone, CLOBValue clob, NCLOBValue nclob, BLOBValue blob, SubObjectValue &main_user..udt_SubObject, SubObjectArray &main_user..udt_ObjectArray ); / create type &main_user..udt_Array as varray(10) of number; / create or replace type &main_user..udt_Building as object ( BuildingId number(9), NumFloors number(3), Description varchar2(60), DateBuilt date ); / create or replace type &main_user..udt_Book as object ( Title varchar2(100), Authors varchar2(100), Price number(5,2) ); / -- create tables create table &main_user..TestNumbers ( IntCol number(9) not null, LongIntCol number(16) not null, NumberCol number(9, 2) not null, FloatCol float not null, UnconstrainedCol number not null, NullableCol number(38) ) / create table &main_user..TestStrings ( IntCol number(9) not null, StringCol varchar2(20) not null, RawCol raw(30) not null, FixedCharCol char(40) not null, NullableCol varchar2(50) ) / create table &main_user..TestUnicodes ( IntCol number(9) not null, UnicodeCol nvarchar2(20) not null, FixedUnicodeCol nchar(40) not null, NullableCol nvarchar2(50) ) / create table &main_user..TestDates ( IntCol number(9) not null, DateCol date not null, NullableCol date ) / create table &main_user..TestCLOBs ( IntCol number(9) not null, CLOBCol clob not null ) / create table &main_user..TestNCLOBs ( IntCol number(9) not null, NCLOBCol nclob not null ) / create table &main_user..TestBLOBs ( IntCol number(9) not null, BLOBCol blob not null ) / create table &main_user..TestXML ( IntCol number(9) not null, XMLCol xmltype not null ) / create table &main_user..TestTempXML ( IntCol number(9) not null, XMLCol xmltype not null ) / create table &main_user..TestLongs ( IntCol number(9) not null, LongCol long ) nocompress / create table &main_user..TestLongRaws ( IntCol number(9) not null, LongRawCol long raw ) nocompress / create table &main_user..TestTempTable ( IntCol number(9) not null, StringCol varchar2(400), NumberCol number(25,2), constraint TestTempTable_pk primary key (IntCol) ) / create table &main_user..TestArrayDML ( IntCol number(9) not null, StringCol varchar2(100), IntCol2 number(3), constraint TestArrayDML_pk primary key (IntCol) ) / create table &main_user..TestObjects ( IntCol number(9) not null, ObjectCol &main_user..udt_Object, ArrayCol &main_user..udt_Array ) / create table &main_user..TestTimestamps ( IntCol number(9) not null, TimestampCol timestamp not null, NullableCol timestamp ) / create table &main_user..TestIntervals ( IntCol number(9) not null, IntervalCol interval day to second not null, NullableCol interval day to second ) / create table &main_user..TestUniversalRowids ( IntCol number(9) not null, StringCol varchar2(250) not null, DateCol date not null, constraint TestUniversalRowids_pk primary key (IntCol, StringCol, DateCol) ) organization index / create table &main_user..TestBuildings ( BuildingId number(9) not null, BuildingObj &main_user..udt_Building not null ) / create table &main_user..TestRowids ( IntCol number(9) not null, RowidCol rowid, URowidCol urowid ) / create table &main_user..PlsqlSessionCallbacks ( RequestedTag varchar2(250), ActualTag varchar2(250), FixupTimestamp timestamp ) / declare t_Version number; begin select to_number(substr(version, 1, instr(version, '.') - 1)) into t_Version from product_component_version where product like 'Oracle Database%'; if t_Version >= 21 then execute immediate 'create table &main_user..TestJson (' || ' IntCol number(9) not null,' || ' JsonCol json not null' || ')'; end if; end; / -- create queue table and queues for testing advanced queuing begin dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE_TAB', '&main_user..UDT_BOOK'); dbms_aqadm.create_queue('&main_user..TEST_BOOK_QUEUE', '&main_user..BOOK_QUEUE_TAB'); dbms_aqadm.start_queue('&main_user..TEST_BOOK_QUEUE'); dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_TAB', 'RAW'); dbms_aqadm.create_queue('&main_user..TEST_RAW_QUEUE', '&main_user..RAW_QUEUE_TAB'); dbms_aqadm.start_queue('&main_user..TEST_RAW_QUEUE'); end; / -- create transformations begin dbms_transform.create_transformation('&main_user', 'transform1', '&main_user', 'UDT_BOOK', '&main_user', 'UDT_BOOK', '&main_user..UDT_BOOK(source.user_data.TITLE, ' || 'source.user_data.AUTHORS, source.user_data.PRICE + 5)'); dbms_transform.create_transformation('&main_user', 'transform2', '&main_user', 'UDT_BOOK', '&main_user', 'UDT_BOOK', '&main_user..UDT_BOOK(source.user_data.TITLE, ' || 'source.user_data.AUTHORS, source.user_data.PRICE + 10)'); end; / -- populate tables begin for i in 1..10 loop insert into &main_user..TestNumbers values (i, power(38, i), i + i * 0.25, i + i * .75, i * i * i + i *.5, decode(mod(i, 2), 0, null, power(143, i))); end loop; end; / declare t_RawValue raw(30); function ConvertHexDigit(a_Value number) return varchar2 is begin if a_Value between 0 and 9 then return to_char(a_Value); end if; return chr(ascii('A') + a_Value - 10); end; function ConvertToHex(a_Value varchar2) return varchar2 is t_HexValue varchar2(60); t_Digit number; begin for i in 1..length(a_Value) loop t_Digit := ascii(substr(a_Value, i, 1)); t_HexValue := t_HexValue || ConvertHexDigit(trunc(t_Digit / 16)) || ConvertHexDigit(mod(t_Digit, 16)); end loop; return t_HexValue; end; begin for i in 1..10 loop t_RawValue := hextoraw(ConvertToHex('Raw ' || to_char(i))); insert into &main_user..TestStrings values (i, 'String ' || to_char(i), t_RawValue, 'Fixed Char ' || to_char(i), decode(mod(i, 2), 0, null, 'Nullable ' || to_char(i))); end loop; end; / begin for i in 1..10 loop insert into &main_user..TestUnicodes values (i, 'Unicode ' || unistr('\3042') || ' ' || to_char(i), 'Fixed Unicode ' || to_char(i), decode(mod(i, 2), 0, null, unistr('Nullable ') || to_char(i))); end loop; end; / begin for i in 1..10 loop insert into &main_user..TestDates values (i, to_date(20021209, 'YYYYMMDD') + i + i * .1, decode(mod(i, 2), 0, null, to_date(20021209, 'YYYYMMDD') + i + i + i * .15)); end loop; end; / begin for i in 1..100 loop insert into &main_user..TestXML values (i, '' || dbms_random.string('x', 1024) || ''); end loop; end; / begin for i in 1..10 loop insert into &main_user..TestTimestamps values (i, to_timestamp('20021209', 'YYYYMMDD') + to_dsinterval(to_char(i) || ' 00:00:' || to_char(i * 2) || '.' || to_char(i * 50)), decode(mod(i, 2), 0, to_timestamp(null, 'YYYYMMDD'), to_timestamp('20021209', 'YYYYMMDD') + to_dsinterval(to_char(i + 1) || ' 00:00:' || to_char(i * 3) || '.' || to_char(i * 125)))); end loop; end; / begin for i in 1..10 loop insert into &main_user..TestIntervals values (i, to_dsinterval(to_char(i) || ' ' || to_char(i) || ':' || to_char(i * 2) || ':' || to_char(i * 3)), decode(mod(i, 2), 0, to_dsinterval(null), to_dsinterval(to_char(i + 5) || ' ' || to_char(i + 2) || ':' || to_char(i * 2 + 5) || ':' || to_char(i * 3 + 5)))); end loop; end; / insert into &main_user..TestObjects values (1, &main_user..udt_Object(1, 'First row', 'First', 'N First Row', 'N First', '52617720446174612031', 2, 5, 12.125, 0.5, 12.5, 25.25, 50.125, to_date(20070306, 'YYYYMMDD'), to_timestamp('20080912 16:40:00', 'YYYYMMDD HH24:MI:SS'), to_timestamp_tz('20091013 17:50:00 00:00', 'YYYYMMDD HH24:MI:SS TZH:TZM'), to_timestamp_tz('20101114 18:55:00 00:00', 'YYYYMMDD HH24:MI:SS TZH:TZM'), 'Short CLOB value', 'Short NCLOB Value', utl_raw.cast_to_raw('Short BLOB value'), &main_user..udt_SubObject(11, 'Sub object 1'), &main_user..udt_ObjectArray( &main_user..udt_SubObject(5, 'first element'), &main_user..udt_SubObject(6, 'second element'))), &main_user..udt_Array(5, 10, null, 20)) / insert into &main_user..TestObjects values (2, null, &main_user..udt_Array(3, null, 9, 12, 15)) / insert into &main_user..TestObjects values (3, &main_user..udt_Object(3, 'Third row', 'Third', 'N Third Row', 'N Third', '52617720446174612033', 4, 10, 6.5, 0.75, 43.25, 86.5, 192.125, to_date(20070621, 'YYYYMMDD'), to_timestamp('20071213 07:30:45', 'YYYYMMDD HH24:MI:SS'), to_timestamp_tz('20170621 23:18:45 00:00', 'YYYYMMDD HH24:MI:SS TZH:TZM'), to_timestamp_tz('20170721 08:27:13 00:00', 'YYYYMMDD HH24:MI:SS TZH:TZM'), 'Another short CLOB value', 'Another short NCLOB Value', utl_raw.cast_to_raw('Yet another short BLOB value'), &main_user..udt_SubObject(13, 'Sub object 3'), &main_user..udt_ObjectArray( &main_user..udt_SubObject(10, 'element #1'), &main_user..udt_SubObject(20, 'element #2'), &main_user..udt_SubObject(30, 'element #3'), &main_user..udt_SubObject(40, 'element #4'))), null) / commit / -- create procedures for testing callproc() create procedure &main_user..proc_Test ( a_InValue varchar2, a_InOutValue in out number, a_OutValue out number ) as begin a_InOutValue := a_InOutValue * length(a_InValue); a_OutValue := length(a_InValue); end; / create procedure &main_user..proc_TestNoArgs as begin null; end; / -- create procedure for testing refcursor create procedure &main_user..myrefcursorproc ( a_RefCursor out sys_refcursor ) as begin open a_RefCursor for select * from TestTempTable; end; / -- create functions for testing callfunc() create function &main_user..func_Test ( a_String varchar2, a_ExtraAmount number ) return number as begin return length(a_String) + a_ExtraAmount; end; / create function &main_user..func_TestNoArgs return number as begin return 712; end; / -- create packages create or replace package &main_user..pkg_TestStringArrays as type udt_StringList is table of varchar2(100) index by binary_integer; function TestInArrays ( a_StartingLength number, a_Array udt_StringList ) return number; procedure TestInOutArrays ( a_NumElems number, a_Array in out nocopy udt_StringList ); procedure TestOutArrays ( a_NumElems number, a_Array out nocopy udt_StringList ); procedure TestIndexBy ( a_Array out nocopy udt_StringList ); end; / create or replace package body &main_user..pkg_TestStringArrays as function TestInArrays ( a_StartingLength number, a_Array udt_StringList ) return number is t_Length number; begin t_Length := a_StartingLength; for i in 1..a_Array.count loop t_Length := t_Length + length(a_Array(i)); end loop; return t_Length; end; procedure TestInOutArrays ( a_NumElems number, a_Array in out udt_StringList ) is begin for i in 1..a_NumElems loop a_Array(i) := 'Converted element # ' || to_char(i) || ' originally had length ' || to_char(length(a_Array(i))); end loop; end; procedure TestOutArrays ( a_NumElems number, a_Array out udt_StringList ) is begin for i in 1..a_NumElems loop a_Array(i) := 'Test out element # ' || to_char(i); end loop; end; procedure TestIndexBy ( a_Array out nocopy udt_StringList ) is begin a_Array(-1048576) := 'First element'; a_Array(-576) := 'Second element'; a_Array(284) := 'Third element'; a_Array(8388608) := 'Fourth element'; end; end; / create or replace package &main_user..pkg_TestUnicodeArrays as type udt_UnicodeList is table of nvarchar2(100) index by binary_integer; function TestInArrays ( a_StartingLength number, a_Array udt_UnicodeList ) return number; procedure TestInOutArrays ( a_NumElems number, a_Array in out nocopy udt_UnicodeList ); procedure TestOutArrays ( a_NumElems number, a_Array out nocopy udt_UnicodeList ); end; / create or replace package body &main_user..pkg_TestUnicodeArrays as function TestInArrays ( a_StartingLength number, a_Array udt_UnicodeList ) return number is t_Length number; begin t_Length := a_StartingLength; for i in 1..a_Array.count loop t_Length := t_Length + length(a_Array(i)); end loop; return t_Length; end; procedure TestInOutArrays ( a_NumElems number, a_Array in out udt_UnicodeList ) is begin for i in 1..a_NumElems loop a_Array(i) := unistr('Converted element ' || unistr('\3042') || ' # ') || to_char(i) || ' originally had length ' || to_char(length(a_Array(i))); end loop; end; procedure TestOutArrays ( a_NumElems number, a_Array out udt_UnicodeList ) is begin for i in 1..a_NumElems loop a_Array(i) := unistr('Test out element ') || unistr('\3042') || ' # ' || to_char(i); end loop; end; end; / create or replace package &main_user..pkg_TestNumberArrays as type udt_NumberList is table of number index by binary_integer; function TestInArrays ( a_StartingValue number, a_Array udt_NumberList ) return number; procedure TestInOutArrays ( a_NumElems number, a_Array in out nocopy udt_NumberList ); procedure TestOutArrays ( a_NumElems number, a_Array out nocopy udt_NumberList ); end; / create or replace package body &main_user..pkg_TestNumberArrays as function TestInArrays ( a_StartingValue number, a_Array udt_NumberList ) return number is t_Value number; begin t_Value := a_StartingValue; for i in 1..a_Array.count loop t_Value := t_Value + a_Array(i); end loop; return t_Value; end; procedure TestInOutArrays ( a_NumElems number, a_Array in out udt_NumberList ) is begin for i in 1..a_NumElems loop a_Array(i) := a_Array(i) * 10; end loop; end; procedure TestOutArrays ( a_NumElems number, a_Array out udt_NumberList ) is begin for i in 1..a_NumElems loop a_Array(i) := i * 100; end loop; end; end; / create or replace package &main_user..pkg_TestDateArrays as type udt_DateList is table of date index by binary_integer; function TestInArrays ( a_StartingValue number, a_BaseDate date, a_Array udt_DateList ) return number; procedure TestInOutArrays ( a_NumElems number, a_Array in out nocopy udt_DateList ); procedure TestOutArrays ( a_NumElems number, a_Array out nocopy udt_DateList ); end; / create or replace package body &main_user..pkg_TestDateArrays as function TestInArrays ( a_StartingValue number, a_BaseDate date, a_Array udt_DateList ) return number is t_Value number; begin t_Value := a_StartingValue; for i in 1..a_Array.count loop t_Value := t_Value + a_Array(i) - a_BaseDate; end loop; return t_Value; end; procedure TestInOutArrays ( a_NumElems number, a_Array in out udt_DateList ) is begin for i in 1..a_NumElems loop a_Array(i) := a_Array(i) + 7; end loop; end; procedure TestOutArrays ( a_NumElems number, a_Array out udt_DateList ) is begin for i in 1..a_NumElems loop a_Array(i) := to_date(20021212, 'YYYYMMDD') + i * 1.2; end loop; end; end; / create or replace package &main_user..pkg_TestRefCursors as procedure TestOutCursor ( a_MaxIntValue number, a_Cursor out sys_refcursor ); function TestInCursor ( a_Cursor sys_refcursor ) return varchar2; end; / create or replace package body &main_user..pkg_TestRefCursors as procedure TestOutCursor ( a_MaxIntValue number, a_Cursor out sys_refcursor ) is begin open a_Cursor for select IntCol, StringCol from TestStrings where IntCol <= a_MaxIntValue order by IntCol; end; function TestInCursor ( a_Cursor sys_refcursor ) return varchar2 is t_String varchar2(100); begin fetch a_Cursor into t_String; return t_String || ' (Modified)'; end; end; / create or replace package &main_user..pkg_TestBooleans as type udt_BooleanList is table of boolean index by binary_integer; function GetStringRep ( a_Value boolean ) return varchar2; function IsLessThan10 ( a_Value number ) return boolean; function TestInArrays ( a_Value udt_BooleanList ) return number; procedure TestOutArrays ( a_NumElements number, a_Value out nocopy udt_BooleanList ); end; / create or replace package body &main_user..pkg_TestBooleans as function GetStringRep ( a_Value boolean ) return varchar2 is begin if a_Value is null then return 'NULL'; elsif a_Value then return 'TRUE'; end if; return 'FALSE'; end; function IsLessThan10 ( a_Value number ) return boolean is begin return a_Value < 10; end; function TestInArrays ( a_Value udt_BooleanList ) return number is t_Result pls_integer; begin t_Result := 0; for i in 1..a_Value.count loop if a_Value(i) then t_Result := t_Result + 1; end if; end loop; return t_Result; end; procedure TestOutArrays ( a_NumElements number, a_Value out nocopy udt_BooleanList ) is begin for i in 1..a_NumElements loop a_Value(i) := (mod(i, 2) = 1); end loop; end; end; / create or replace package &main_user..pkg_TestBindObject as function GetStringRep ( a_Object udt_Object ) return varchar2; procedure BindObjectOut ( a_NumberValue number, a_StringValue varchar2, a_Object out nocopy udt_Object ); end; / create or replace package body &main_user..pkg_TestBindObject as function GetStringRep ( a_Object udt_SubObject ) return varchar2 is begin if a_Object is null then return 'null'; end if; return 'udt_SubObject(' || nvl(to_char(a_Object.SubNumberValue), 'null') || ', ' || case when a_Object.SubStringValue is null then 'null' else '''' || a_Object.SubStringValue || '''' end || ')'; end; function GetStringRep ( a_Array udt_ObjectArray ) return varchar2 is t_StringRep varchar2(4000); begin if a_Array is null then return 'null'; end if; t_StringRep := 'udt_ObjectArray('; for i in 1..a_Array.count loop if i > 1 then t_StringRep := t_StringRep || ', '; end if; t_StringRep := t_StringRep || GetStringRep(a_Array(i)); end loop; return t_StringRep || ')'; end; function GetStringRep ( a_Object udt_Object ) return varchar2 is begin if a_Object is null then return 'null'; end if; return 'udt_Object(' || nvl(to_char(a_Object.NumberValue), 'null') || ', ' || case when a_Object.StringValue is null then 'null' else '''' || a_Object.StringValue || '''' end || ', ' || case when a_Object.FixedCharValue is null then 'null' else '''' || a_Object.FixedCharValue || '''' end || ', ' || case when a_Object.DateValue is null then 'null' else 'to_date(''' || to_char(a_Object.DateValue, 'YYYY-MM-DD') || ''', ''YYYY-MM-DD'')' end || ', ' || case when a_Object.TimestampValue is null then 'null' else 'to_timestamp(''' || to_char(a_Object.TimestampValue, 'YYYY-MM-DD HH24:MI:SS') || ''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' || GetStringRep(a_Object.SubObjectValue) || ', ' || GetStringRep(a_Object.SubObjectArray) || ')'; end; procedure BindObjectOut ( a_NumberValue number, a_StringValue varchar2, a_Object out nocopy udt_Object ) is begin a_Object := udt_Object(a_NumberValue, a_StringValue, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); end; end; / create or replace package &main_user..pkg_TestRecords as type udt_Record is record ( NumberValue number, StringValue varchar2(30), DateValue date, TimestampValue timestamp, BooleanValue boolean, PlsIntegerValue pls_integer, BinaryIntegerValue binary_integer ); type udt_RecordArray is table of udt_Record index by binary_integer; function GetStringRep ( a_Value udt_Record ) return varchar2; procedure TestOut ( a_Value out nocopy udt_Record ); function TestInArrays ( a_Value udt_RecordArray ) return varchar2; end; / create or replace package body &main_user..pkg_TestRecords as function GetStringRep ( a_Value udt_Record ) return varchar2 is begin return 'udt_Record(' || nvl(to_char(a_Value.NumberValue), 'null') || ', ' || case when a_Value.StringValue is null then 'null' else '''' || a_Value.StringValue || '''' end || ', ' || case when a_Value.DateValue is null then 'null' else 'to_date(''' || to_char(a_Value.DateValue, 'YYYY-MM-DD') || ''', ''YYYY-MM-DD'')' end || ', ' || case when a_Value.TimestampValue is null then 'null' else 'to_timestamp(''' || to_char(a_Value.TimestampValue, 'YYYY-MM-DD HH24:MI:SS') || ''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' || case when a_Value.BooleanValue is null then 'null' when a_Value.BooleanValue then 'true' else 'false' end || ', ' || nvl(to_char(a_Value.PlsIntegerValue), 'null') || ', ' || nvl(to_char(a_Value.BinaryIntegerValue), 'null') || ')'; end; procedure TestOut ( a_Value out nocopy udt_Record ) is begin a_Value.NumberValue := 25; a_Value.StringValue := 'String in record'; a_Value.DateValue := to_date(20160216, 'YYYYMMDD'); a_Value.TimestampValue := to_timestamp('20160216 18:23:55', 'YYYYMMDD HH24:MI:SS'); a_Value.BooleanValue := true; a_Value.PlsIntegerValue := 45; a_Value.BinaryIntegerValue := 10; end; function TestInArrays ( a_Value udt_RecordArray ) return varchar2 is t_Result varchar2(4000); begin for i in 0..a_Value.count - 1 loop if t_Result is not null then t_Result := t_Result || '; '; end if; t_Result := t_Result || GetStringRep(a_Value(i)); end loop; return t_Result; end; end; / create or replace package &main_user..pkg_SessionCallback as procedure TheCallback ( a_RequestedTag varchar2, a_ActualTag varchar2 ); end; / create or replace package body &main_user..pkg_SessionCallback as type udt_Properties is table of varchar2(64) index by varchar2(64); procedure LogCall ( a_RequestedTag varchar2, a_ActualTag varchar2 ) is pragma autonomous_transaction; begin insert into PlsqlSessionCallbacks values (a_RequestedTag, a_ActualTag, systimestamp); commit; end; procedure ParseProperty ( a_Property varchar2, a_Name out nocopy varchar2, a_Value out nocopy varchar2 ) is t_Pos number; begin t_Pos := instr(a_Property, '='); if t_Pos = 0 then raise_application_error(-20000, 'Tag must contain key=value pairs'); end if; a_Name := substr(a_Property, 1, t_Pos - 1); a_Value := substr(a_Property, t_Pos + 1); end; procedure SetProperty ( a_Name varchar2, a_Value varchar2 ) is t_ValidValues udt_Properties; begin if a_Name = 'TIME_ZONE' then t_ValidValues('UTC') := 'UTC'; t_ValidValues('MST') := '-07:00'; elsif a_Name = 'NLS_DATE_FORMAT' then t_ValidValues('SIMPLE') := 'YYYY-MM-DD HH24:MI'; t_ValidValues('FULL') := 'YYYY-MM-DD HH24:MI:SS'; else raise_application_error(-20000, 'Unsupported session setting'); end if; if not t_ValidValues.exists(a_Value) then raise_application_error(-20000, 'Unsupported session setting'); end if; execute immediate 'ALTER SESSION SET ' || a_Name || '=''' || t_ValidValues(a_Value) || ''''; end; procedure ParseTag ( a_Tag varchar2, a_Properties out nocopy udt_Properties ) is t_PropertyName varchar2(64); t_PropertyValue varchar2(64); t_StartPos number; t_EndPos number; begin t_StartPos := 1; while t_StartPos < length(a_Tag) loop t_EndPos := instr(a_Tag, ';', t_StartPos); if t_EndPos = 0 then t_EndPos := length(a_Tag) + 1; end if; ParseProperty(substr(a_Tag, t_StartPos, t_EndPos - t_StartPos), t_PropertyName, t_PropertyValue); a_Properties(t_PropertyName) := t_PropertyValue; t_StartPos := t_EndPos + 1; end loop; end; procedure TheCallback ( a_RequestedTag varchar2, a_ActualTag varchar2 ) is t_RequestedProps udt_Properties; t_ActualProps udt_Properties; t_PropertyName varchar2(64); begin LogCall(a_RequestedTag, a_ActualTag); ParseTag(a_RequestedTag, t_RequestedProps); ParseTag(a_ActualTag, t_ActualProps); t_PropertyName := t_RequestedProps.first; while t_PropertyName is not null loop if not t_ActualProps.exists(t_PropertyName) or t_ActualProps(t_PropertyName) != t_RequestedProps(t_PropertyName) then SetProperty(t_PropertyName, t_RequestedProps(t_PropertyName)); end if; t_PropertyName := t_RequestedProps.next(t_PropertyName); end loop; end; end; / python-cx_Oracle-8.3.0/test/test_1000_module.py000066400000000000000000000031311414105416400212530ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 1000 - Module for testing top-level module methods """ import datetime import time import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): requires_connection = False def test_1000_date_from_ticks(self): "1000 - test DateFromTicks()" today = datetime.datetime.today() timestamp = time.mktime(today.timetuple()) date = oracledb.DateFromTicks(timestamp) self.assertEqual(date, today.date()) def test_1001_future_obj(self): "1001 - test management of __future__ object" self.assertEqual(oracledb.__future__.dummy, None) oracledb.__future__.dummy = "Unimportant" self.assertEqual(oracledb.__future__.dummy, None) def test_1002_timestamp_from_ticks(self): "1002 - test TimestampFromTicks()" timestamp = time.mktime(datetime.datetime.today().timetuple()) today = datetime.datetime.fromtimestamp(timestamp) date = oracledb.TimestampFromTicks(timestamp) self.assertEqual(date, today) def test_1003_unsupported_functions(self): "1003 - test unsupported time functions" self.assertRaises(oracledb.NotSupportedError, oracledb.Time, 12, 0, 0) self.assertRaises(oracledb.NotSupportedError, oracledb.TimeFromTicks, 100) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1100_connection.py000066400000000000000000000642721414105416400221430ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 1100 - Module for testing connections """ import random import string import threading import time import unittest import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): requires_connection = False def __connect_and_drop(self): """Connect to the database, perform a query and drop the connection.""" connection = test_env.get_connection(threaded=True) cursor = connection.cursor() cursor.execute("select count(*) from TestNumbers") count, = cursor.fetchone() self.assertEqual(count, 10) def __verify_fetched_data(self, connection): expected_data = [f"String {i + 1}" for i in range(10)] sql = "select StringCol from TestStrings order by IntCol" for i in range(5): with connection.cursor() as cursor: fetched_data = [s for s, in cursor.execute(sql)] self.assertEqual(fetched_data, expected_data) def __verify_args(self, connection): self.assertEqual(connection.username, test_env.get_main_user(), "user name differs") self.assertEqual(connection.tnsentry, test_env.get_connect_string(), "tnsentry differs") self.assertEqual(connection.dsn, test_env.get_connect_string(), "dsn differs") def __verify_attributes(self, connection, attrName, value, sql): setattr(connection, attrName, value) cursor = connection.cursor() cursor.execute(sql) result, = cursor.fetchone() self.assertEqual(result, value, "%s value mismatch" % attrName) def test_1100_all_args(self): "1100 - connection to database with user, password, TNS separate" connection = test_env.get_connection() self.__verify_args(connection) def test_1101_app_context(self): "1101 - test use of application context" namespace = "CLIENTCONTEXT" app_context_entries = [ ( namespace, "ATTR1", "VALUE1" ), ( namespace, "ATTR2", "VALUE2" ), ( namespace, "ATTR3", "VALUE3" ) ] connection = test_env.get_connection(appcontext=app_context_entries) cursor = connection.cursor() for namespace, name, value in app_context_entries: cursor.execute("select sys_context(:1, :2) from dual", (namespace, name)) actual_value, = cursor.fetchone() self.assertEqual(actual_value, value) def test_1102_app_context_negative(self): "1102 - test invalid use of application context" self.assertRaises(TypeError, oracledb.connect, user=test_env.get_main_user(), password=test_env.get_main_password(), dsn=test_env.get_connect_string(), appcontext=[('userenv', 'action')]) def test_1103_attributes(self): "1103 - test connection end-to-end tracing attributes" connection = test_env.get_connection() if test_env.get_client_version() >= (12, 1) \ and not self.is_on_oracle_cloud(connection): sql = "select dbop_name from v$sql_monitor " \ "where sid = sys_context('userenv', 'sid')" \ "and status = 'EXECUTING'" self.__verify_attributes(connection, "dbop", "oracledb_dbop", sql) sql = "select sys_context('userenv', 'action') from dual" self.__verify_attributes(connection, "action", "oracledb_Action", sql) sql = "select sys_context('userenv', 'module') from dual" self.__verify_attributes(connection, "module", "oracledb_Module", sql) sql = "select sys_context('userenv', 'client_info') from dual" self.__verify_attributes(connection, "clientinfo", "oracledb_cinfo", sql) sql = "select sys_context('userenv', 'client_identifier') from dual" self.__verify_attributes(connection, "client_identifier", "oracledb_cid", sql) def test_1104_autocommit(self): "1104 - test use of autocommit" connection = test_env.get_connection() cursor = connection.cursor() other_connection = test_env.get_connection() other_cursor = other_connection.cursor() cursor.execute("truncate table TestTempTable") cursor.execute("insert into TestTempTable (IntCol) values (1)") other_cursor.execute("select IntCol from TestTempTable") rows = other_cursor.fetchall() self.assertEqual(rows, []) connection.autocommit = True cursor.execute("insert into TestTempTable (IntCol) values (2)") other_cursor.execute("select IntCol from TestTempTable order by IntCol") rows = other_cursor.fetchall() self.assertEqual(rows, [(1,), (2,)]) def test_1105_bad_connect_string(self): "1105 - connection to database with bad connect string" self.assertRaises(oracledb.DatabaseError, oracledb.connect, test_env.get_main_user()) self.assertRaises(oracledb.DatabaseError, oracledb.connect, test_env.get_main_user() + "@" + \ test_env.get_connect_string()) self.assertRaises(oracledb.DatabaseError, oracledb.connect, test_env.get_main_user() + "@" + \ test_env.get_connect_string() + "/" + \ test_env.get_main_password()) def test_1106_bad_password(self): "1106 - connection to database with bad password" self.assertRaises(oracledb.DatabaseError, oracledb.connect, user=test_env.get_main_user(), password=test_env.get_main_password() + "X", dsn=test_env.get_connect_string()) def test_1107_change_password(self): "1107 - test changing password" connection = test_env.get_connection() if self.is_on_oracle_cloud(connection): self.skipTest("passwords on Oracle Cloud are strictly controlled") sys_random = random.SystemRandom() new_password = "".join(sys_random.choice(string.ascii_letters) \ for i in range(20)) connection.changepassword(test_env.get_main_password(), new_password) connection = oracledb.connect(dsn=test_env.get_connect_string(), user=test_env.get_main_user(), password=new_password) connection.changepassword(new_password, test_env.get_main_password()) def test_1108_change_password_negative(self): "1108 - test changing password to an invalid value" connection = test_env.get_connection() if self.is_on_oracle_cloud(connection): self.skipTest("passwords on Oracle Cloud are strictly controlled") new_password = "1" * 150 self.assertRaises(oracledb.DatabaseError, connection.changepassword, test_env.get_main_password(), new_password) def test_1109_parse_password(self): "1109 - test connecting with password containing / and @ symbols" connection = test_env.get_connection() if self.is_on_oracle_cloud(connection): self.skipTest("passwords on Oracle Cloud are strictly controlled") sys_random = random.SystemRandom() chars = list(sys_random.choice(string.ascii_letters) for i in range(20)) chars[4] = "/" chars[8] = "@" new_password = "".join(chars) connection.changepassword(test_env.get_main_password(), new_password) try: arg = "%s/%s@%s" % (test_env.get_main_user(), new_password, test_env.get_connect_string()) oracledb.connect(arg) finally: connection.changepassword(new_password, test_env.get_main_password()) def test_1110_encodings(self): "1110 - connection with only encoding/nencoding specified should work" connection = oracledb.connect(test_env.get_main_user(), test_env.get_main_password(), test_env.get_connect_string()) encoding = connection.encoding nencoding = connection.nencoding alt_encoding = "ISO-8859-1" connection = oracledb.connect(test_env.get_main_user(), test_env.get_main_password(), test_env.get_connect_string(), encoding=alt_encoding) self.assertEqual(connection.encoding, alt_encoding) self.assertEqual(connection.nencoding, nencoding) connection = oracledb.connect(test_env.get_main_user(), test_env.get_main_password(), test_env.get_connect_string(), nencoding=alt_encoding) self.assertEqual(connection.encoding, encoding) self.assertEqual(connection.nencoding, alt_encoding) def test_1111_different_encodings(self): "1111 - different encodings can be specified for encoding/nencoding" connection = oracledb.connect(test_env.get_main_user(), test_env.get_main_password(), test_env.get_connect_string(), encoding="UTF-8", nencoding="UTF-16") value = "\u03b4\u4e2a" cursor = connection.cursor() nchar_var = cursor.var(oracledb.DB_TYPE_NVARCHAR, 100) nchar_var.setvalue(0, value) cursor.execute("select :value from dual", value=nchar_var) result, = cursor.fetchone() self.assertEqual(result, value) def test_1112_exception_on_close(self): "1112 - confirm an exception is raised after closing a connection" connection = test_env.get_connection() connection.close() self.assertRaises(oracledb.InterfaceError, connection.rollback) def test_1113_connect_with_handle(self): "1113 - test creating a connection using a handle" connection = test_env.get_connection() cursor = connection.cursor() cursor.execute("truncate table TestTempTable") int_value = random.randint(1, 32768) cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:val, null)""", val=int_value) connection2 = oracledb.connect(handle=connection.handle) cursor = connection2.cursor() cursor.execute("select IntCol from TestTempTable") fetched_int_value, = cursor.fetchone() self.assertEqual(fetched_int_value, int_value) cursor.close() self.assertRaises(oracledb.DatabaseError, connection2.close) connection.close() def test_1114_make_dsn(self): "1114 - test making a data source name from host, port and sid" format_string = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" + \ "(HOST=%s)(PORT=%d))(CONNECT_DATA=(SID=%s)))" args = ("hostname", 1521, "TEST") result = oracledb.makedsn(*args) self.assertEqual(result, format_string % args) def test_1115_single_arg(self): "1115 - connection to database with user, password, DSN together" arg = "%s/%s@%s" % (test_env.get_main_user(), test_env.get_main_password(), test_env.get_connect_string()) connection = oracledb.connect(arg) self.__verify_args(connection) def test_1116_version(self): "1116 - connection version is a string" connection = test_env.get_connection() self.assertTrue(isinstance(connection.version, str)) def test_1117_rollback_on_close(self): "1117 - connection rolls back before close" connection = test_env.get_connection() cursor = connection.cursor() cursor.execute("truncate table TestTempTable") other_connection = test_env.get_connection() other_cursor = other_connection.cursor() other_cursor.execute("insert into TestTempTable (IntCol) values (1)") other_cursor.close() other_connection.close() cursor.execute("select count(*) from TestTempTable") count, = cursor.fetchone() self.assertEqual(count, 0) def test_1118_rollback_on_del(self): "1118 - connection rolls back before destruction" connection = test_env.get_connection() cursor = connection.cursor() cursor.execute("truncate table TestTempTable") other_connection = test_env.get_connection() other_cursor = other_connection.cursor() other_cursor.execute("insert into TestTempTable (IntCol) values (1)") del other_cursor del other_connection cursor.execute("select count(*) from TestTempTable") count, = cursor.fetchone() self.assertEqual(count, 0) def test_1119_threading(self): "1119 - multiple connections to database with multiple threads" threads = [] for i in range(20): thread = threading.Thread(None, self.__connect_and_drop) threads.append(thread) thread.start() for thread in threads: thread.join() def test_1120_string_format(self): "1120 - test string format of connection" connection = test_env.get_connection() expected_value = "" % \ (test_env.get_main_user(), test_env.get_connect_string()) self.assertEqual(str(connection), expected_value) def test_1121_ctx_mgr_close(self): "1121 - test context manager - close" connection = test_env.get_connection() with connection: cursor = connection.cursor() cursor.execute("truncate table TestTempTable") cursor.execute("insert into TestTempTable (IntCol) values (1)") connection.commit() cursor.execute("insert into TestTempTable (IntCol) values (2)") self.assertRaises(oracledb.InterfaceError, connection.ping) connection = test_env.get_connection() cursor = connection.cursor() cursor.execute("select count(*) from TestTempTable") count, = cursor.fetchone() self.assertEqual(count, 1) def test_1122_connection_attributes(self): "1122 - test connection attribute values" connection = oracledb.connect(test_env.get_main_user(), test_env.get_main_password(), test_env.get_connect_string(), encoding="ASCII") self.assertEqual(connection.maxBytesPerCharacter, 1) connection = oracledb.connect(test_env.get_main_user(), test_env.get_main_password(), test_env.get_connect_string(), encoding="UTF-8") self.assertEqual(connection.maxBytesPerCharacter, 4) if test_env.get_client_version() >= (12, 1): self.assertEqual(connection.ltxid, b'') self.assertEqual(connection.current_schema, None) connection.current_schema = "test_schema" self.assertEqual(connection.current_schema, "test_schema") self.assertEqual(connection.edition, None) connection.external_name = "test_external" self.assertEqual(connection.external_name, "test_external") connection.internal_name = "test_internal" self.assertEqual(connection.internal_name, "test_internal") connection.stmtcachesize = 30 self.assertEqual(connection.stmtcachesize, 30) self.assertRaises(TypeError, connection.stmtcachesize, 20.5) self.assertRaises(TypeError, connection.stmtcachesize, "value") def test_1123_closed_connection_attributes(self): "1123 - test closed connection attribute values" connection = test_env.get_connection() connection.close() attr_names = ["current_schema", "edition", "external_name", "internal_name", "stmtcachesize"] if test_env.get_client_version() >= (12, 1): attr_names.append("ltxid") for name in attr_names: self.assertRaises(oracledb.InterfaceError, getattr, connection, name) def test_1124_ping(self): "1124 - test connection ping" connection = test_env.get_connection() connection.ping() def test_1125_transaction_begin(self): "1125 - test begin, prepare, cancel transaction" connection = test_env.get_connection() cursor = connection.cursor() cursor.execute("truncate table TestTempTable") connection.begin(10, 'trxnId', 'branchId') self.assertEqual(connection.prepare(), False) connection.begin(10, 'trxnId', 'branchId') cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (1, 'tesName')""") self.assertEqual(connection.prepare(), True) connection.cancel() connection.rollback() cursor.execute("select count(*) from TestTempTable") count, = cursor.fetchone() self.assertEqual(count, 0) def test_1126_multiple_transactions(self): "1126 - test multiple transactions on the same connection" connection = test_env.get_connection() with connection.cursor() as cursor: cursor.execute("truncate table TestTempTable") id_ = random.randint(0, 2 ** 128) xid = (0x1234, "%032x" % id_, "%032x" % 9) connection.begin(*xid) with connection.cursor() as cursor: cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (1, 'tesName')""") self.assertEqual(connection.prepare(), True) connection.commit() for begin_trans in (True, False): val = 3 if begin_trans: connection.begin() val = 2 with connection.cursor() as cursor: cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:int_val, 'tesName')""", int_val=val) connection.commit() expected_rows = [(1, "tesName"), (2, "tesName"), (3, "tesName")] with connection.cursor() as cursor: cursor.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor.fetchall(), expected_rows) def test_1127_multiple_global_transactions(self): "1127 - test multiple global transactions on the same connection" connection = test_env.get_connection() with connection.cursor() as cursor: cursor.execute("truncate table TestTempTable") id_ = random.randint(0, 2 ** 128) xid = (0x1234, "%032x" % id_, "%032x" % 9) connection.begin(*xid) with connection.cursor() as cursor: cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (1, 'tesName')""") self.assertEqual(connection.prepare(), True) connection.commit() for begin_trans in (True, False): val = 3 if begin_trans: connection.begin() val = 2 with connection.cursor() as cursor: cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:int_val, 'tesName')""", int_val=val) connection.commit() id2_ = random.randint(0, 2 ** 128) xid2 = (0x1234, "%032x" % id2_, "%032x" % 9) connection.begin(*xid2) with connection.cursor() as cursor: cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (4, 'tesName')""") self.assertEqual(connection.prepare(), True) connection.commit() expected_rows = [(1, "tesName"), (2, "tesName"), (3, "tesName"), (4, "tesName")] with connection.cursor() as cursor: cursor.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor.fetchall(), expected_rows) def test_1128_exception_creating_global_txn_after_local_txn(self): "1128 - test creating global txn after a local txn" connection = test_env.get_connection() with connection.cursor() as cursor: cursor.execute("truncate table TestTempTable") val = 2 with connection.cursor() as cursor: cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:int_val, 'tesName')""", int_val=val) id_ = random.randint(0, 2 ** 128) xid = (0x1234, "%032x" % id_, "%032x" % 9) self.assertRaises(oracledb.DatabaseError, connection.begin, *xid) def test_1129_threading_single_connection(self): "1129 - single connection to database with multiple threads" with test_env.get_connection(threaded=True) as connection: threads = [threading.Thread(target=self.__verify_fetched_data, args=(connection,)) for i in range(3)] for t in threads: t.start() for t in threads: t.join() def test_1130_cancel(self): "1130 - test connection cancel" conn = test_env.get_connection() def perform_cancel(): time.sleep(0.1) conn.cancel() thread = threading.Thread(target=perform_cancel) thread.start() try: with conn.cursor() as cursor: self.assertRaises(oracledb.OperationalError, cursor.callproc, test_env.get_sleep_proc_name(), [2]) finally: thread.join() with conn.cursor() as cursor: cursor.execute("select user from dual") user, = cursor.fetchone() self.assertEqual(user, test_env.get_main_user().upper()) def test_1131_change_password_during_connect(self): "1131 - test changing password during connect" connection = test_env.get_connection() if self.is_on_oracle_cloud(connection): self.skipTest("passwords on Oracle Cloud are strictly controlled") sys_random = random.SystemRandom() new_password = "".join(sys_random.choice(string.ascii_letters) \ for i in range(20)) connection = oracledb.connect(dsn=test_env.get_connect_string(), user=test_env.get_main_user(), password=test_env.get_main_password(), newpassword=new_password) connection = oracledb.connect(dsn=test_env.get_connect_string(), user=test_env.get_main_user(), password=new_password) connection.changepassword(new_password, test_env.get_main_password()) def test_1132_autocommit_during_reexecute(self): "1132 - test use of autocommit during reexecute" sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" data_to_insert = [ (1, "Test String #1"), (2, "Test String #2") ] connection = test_env.get_connection() cursor = connection.cursor() other_connection = test_env.get_connection() other_cursor = other_connection.cursor() cursor.execute("truncate table TestTempTable") cursor.execute(sql, data_to_insert[0]) other_cursor.execute("select IntCol, StringCol from TestTempTable") rows = other_cursor.fetchall() self.assertEqual(rows, []) connection.autocommit = True cursor.execute(sql, data_to_insert[1]) other_cursor.execute("select IntCol, StringCol from TestTempTable") rows = other_cursor.fetchall() self.assertEqual(rows, data_to_insert) def test_1133_current_schema(self): "1133 - test current_schame is set properly" conn = test_env.get_connection() self.assertEqual(conn.current_schema, None) user = test_env.get_main_user().upper() proxy_user = test_env.get_proxy_user().upper() cursor = conn.cursor() cursor.execute(f'alter session set current_schema={proxy_user}') self.assertEqual(conn.current_schema, proxy_user) conn.current_schema = user self.assertEqual(conn.current_schema, user) cursor.execute(""" select sys_context('userenv', 'current_schema') from dual""") result, = cursor.fetchone() self.assertEqual(result, user) def test_1134_dbms_output(self): "1134 - test dbms_output package" conn = test_env.get_connection() cursor = conn.cursor() test_string = "Testing DBMS_OUTPUT package" cursor.callproc("dbms_output.enable") cursor.execute(""" begin dbms_output.put_line(:val); end; """, val=test_string) string_var = cursor.var(str) number_var = cursor.var(int) cursor.callproc("dbms_output.get_line", (string_var, number_var)) self.assertEqual(string_var.getvalue(), test_string) @unittest.skipIf(test_env.get_client_version() < (18, 1), "unsupported client") def test_1135_calltimeout(self): "1135 - test connection call_timeout" conn = test_env.get_connection() conn.call_timeout = 500 # milliseconds self.assertEqual(conn.call_timeout, 500) self.assertRaises(oracledb.DatabaseError, conn.cursor().callproc, test_env.get_sleep_proc_name(), [2]) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1200_cursor.py000066400000000000000000001315631414105416400213200ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 1200 - Module for testing cursors """ import decimal import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def test_1200_create_scrollable_cursor(self): "1200 - test creating a scrollable cursor" cursor = self.connection.cursor() self.assertEqual(cursor.scrollable, False) cursor = self.connection.cursor(True) self.assertEqual(cursor.scrollable, True) cursor = self.connection.cursor(scrollable=True) self.assertEqual(cursor.scrollable, True) cursor.scrollable = False self.assertEqual(cursor.scrollable, False) def test_1201_execute_no_args(self): "1201 - test executing a statement without any arguments" result = self.cursor.execute("begin null; end;") self.assertEqual(result, None) def test_1202_execute_no_statement_with_args(self): "1202 - test executing a None statement with bind variables" self.assertRaises(oracledb.ProgrammingError, self.cursor.execute, None, x=5) def test_1203_execute_empty_keyword_args(self): "1203 - test executing a statement with args and empty keyword args" simple_var = self.cursor.var(oracledb.NUMBER) args = [simple_var] kwargs = {} result = self.cursor.execute("begin :1 := 25; end;", args, **kwargs) self.assertEqual(result, None) self.assertEqual(simple_var.getvalue(), 25) def test_1204_execute_keyword_args(self): "1204 - test executing a statement with keyword arguments" simple_var = self.cursor.var(oracledb.NUMBER) result = self.cursor.execute("begin :value := 5; end;", value=simple_var) self.assertEqual(result, None) self.assertEqual(simple_var.getvalue(), 5) def test_1205_execute_dictionary_arg(self): "1205 - test executing a statement with a dictionary argument" simple_var = self.cursor.var(oracledb.NUMBER) dict_arg = dict(value=simple_var) result = self.cursor.execute("begin :value := 10; end;", dict_arg) self.assertEqual(result, None) self.assertEqual(simple_var.getvalue(), 10) def test_1206_execute_multiple_arg_types(self): "1206 - test executing a statement with both a dict and keyword args" simple_var = self.cursor.var(oracledb.NUMBER) dict_arg = dict(value=simple_var) self.assertRaises(oracledb.InterfaceError, self.cursor.execute, "begin :value := 15; end;", dict_arg, value=simple_var) def test_1207_execute_and_modify_array_size(self): "1207 - test executing a statement and then changing the array size" self.cursor.execute("select IntCol from TestNumbers") self.cursor.arraysize = 20 self.assertEqual(len(self.cursor.fetchall()), 10) def test_1208_callproc(self): "1208 - test executing a stored procedure" var = self.cursor.var(oracledb.NUMBER) results = self.cursor.callproc("proc_Test", ("hi", 5, var)) self.assertEqual(results, ["hi", 10, 2.0]) def test_1209_callproc_all_keywords(self): "1209 - test executing a stored procedure with all args keyword args" inout_value = self.cursor.var(oracledb.NUMBER) inout_value.setvalue(0, 5) out_value = self.cursor.var(oracledb.NUMBER) kwargs = dict(a_InOutValue=inout_value, a_InValue="hi", a_OutValue=out_value) results = self.cursor.callproc("proc_Test", [], kwargs) self.assertEqual(results, []) self.assertEqual(inout_value.getvalue(), 10) self.assertEqual(out_value.getvalue(), 2.0) def test_1210_callproc_only_last_keyword(self): "1210 - test executing a stored procedure with last arg as keyword arg" out_value = self.cursor.var(oracledb.NUMBER) kwargs = dict(a_OutValue=out_value) results = self.cursor.callproc("proc_Test", ("hi", 5), kwargs) self.assertEqual(results, ["hi", 10]) self.assertEqual(out_value.getvalue(), 2.0) def test_1211_callproc_repeated_keyword_parameters(self): "1211 - test executing a stored procedure, repeated keyword arg" kwargs = dict(a_InValue="hi", a_OutValue=self.cursor.var(oracledb.NUMBER)) self.assertRaises(oracledb.DatabaseError, self.cursor.callproc, "proc_Test", ("hi", 5), kwargs) def test_1212_callproc_no_args(self): "1212 - test executing a stored procedure without any arguments" results = self.cursor.callproc("proc_TestNoArgs") self.assertEqual(results, []) def test_1213_callfunc(self): "1213 - test executing a stored function" results = self.cursor.callfunc("func_Test", oracledb.NUMBER, ("hi", 5)) self.assertEqual(results, 7) def test_1214_callfunc_no_args(self): "1214 - test executing a stored function without any arguments" results = self.cursor.callfunc("func_TestNoArgs", oracledb.NUMBER) self.assertEqual(results, 712) def test_1215_callfunc_negative(self): "1215 - test executing a stored function with wrong parameters" func_name = "func_Test" self.assertRaises(TypeError, self.cursor.callfunc, oracledb.NUMBER, func_name, ("hi", 5)) self.assertRaises(oracledb.DatabaseError, self.cursor.callfunc, func_name, oracledb.NUMBER, ("hi", 5, 7)) self.assertRaises(TypeError, self.cursor.callfunc, func_name, oracledb.NUMBER, "hi", 7) self.assertRaises(oracledb.DatabaseError, self.cursor.callfunc, func_name, oracledb.NUMBER, [5, "hi"]) self.assertRaises(oracledb.DatabaseError, self.cursor.callfunc, func_name, oracledb.NUMBER) self.assertRaises(TypeError, self.cursor.callfunc, func_name, oracledb.NUMBER, 5) def test_1216_executemany_by_name(self): "1216 - test executing a statement multiple times (named args)" self.cursor.execute("truncate table TestTempTable") rows = [{"value": n} for n in range(250)] self.cursor.arraysize = 100 statement = "insert into TestTempTable (IntCol) values (:value)" self.cursor.executemany(statement, rows) self.connection.commit() self.cursor.execute("select count(*) from TestTempTable") count, = self.cursor.fetchone() self.assertEqual(count, len(rows)) def test_1217_executemany_by_position(self): "1217 - test executing a statement multiple times (positional args)" self.cursor.execute("truncate table TestTempTable") rows = [[n] for n in range(230)] self.cursor.arraysize = 100 statement = "insert into TestTempTable (IntCol) values (:1)" self.cursor.executemany(statement, rows) self.connection.commit() self.cursor.execute("select count(*) from TestTempTable") count, = self.cursor.fetchone() self.assertEqual(count, len(rows)) def test_1218_executemany_with_prepare(self): "1218 - test executing a statement multiple times (with prepare)" self.cursor.execute("truncate table TestTempTable") rows = [[n] for n in range(225)] self.cursor.arraysize = 100 statement = "insert into TestTempTable (IntCol) values (:1)" self.cursor.prepare(statement) self.cursor.executemany(None, rows) self.connection.commit() self.cursor.execute("select count(*) from TestTempTable") count, = self.cursor.fetchone() self.assertEqual(count, len(rows)) def test_1219_executemany_with_rebind(self): "1219 - test executing a statement multiple times (with rebind)" self.cursor.execute("truncate table TestTempTable") rows = [[n] for n in range(235)] self.cursor.arraysize = 100 statement = "insert into TestTempTable (IntCol) values (:1)" self.cursor.executemany(statement, rows[:50]) self.cursor.executemany(statement, rows[50:]) self.connection.commit() self.cursor.execute("select count(*) from TestTempTable") count, = self.cursor.fetchone() self.assertEqual(count, len(rows)) def test_1220_executemany_with_input_sizes_wrong(self): "1220 - test executing multiple times (with input sizes wrong)" cursor = self.connection.cursor() cursor.setinputsizes(oracledb.NUMBER) data = [[decimal.Decimal("25.8")], [decimal.Decimal("30.0")]] cursor.executemany("declare t number; begin t := :1; end;", data) def test_1221_executemany_with_multiple_batches(self): "1221 - test executing multiple times (with multiple batches)" self.cursor.execute("truncate table TestTempTable") sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" self.cursor.executemany(sql, [(1, None), (2, None)]) self.cursor.executemany(sql, [(3, None), (4, "Testing")]) def test_1222_executemany_numeric(self): "1222 - test executemany() with various numeric types" self.cursor.execute("truncate table TestTempTable") data = [ (1, 5), (2, 7.0), (3, 6.5), (4, 2 ** 65), (5, decimal.Decimal("24.5")) ] sql = "insert into TestTempTable (IntCol, NumberCol) values (:1, :2)" self.cursor.executemany(sql, data) self.cursor.execute(""" select IntCol, NumberCol from TestTempTable order by IntCol""") self.assertEqual(self.cursor.fetchall(), data) def test_1223_executemany_with_resize(self): "1223 - test executing a statement multiple times (with resize)" self.cursor.execute("truncate table TestTempTable") rows = [ (1, "First"), (2, "Second"), (3, "Third"), (4, "Fourth"), (5, "Fifth"), (6, "Sixth"), (7, "Seventh and the longest one") ] sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" self.cursor.executemany(sql, rows) self.cursor.execute(""" select IntCol, StringCol from TestTempTable order by IntCol""") fetched_rows = self.cursor.fetchall() self.assertEqual(fetched_rows, rows) def test_1224_executemany_with_exception(self): "1224 - test executing a statement multiple times (with exception)" self.cursor.execute("truncate table TestTempTable") rows = [{"value": n} for n in (1, 2, 3, 2, 5)] statement = "insert into TestTempTable (IntCol) values (:value)" self.assertRaises(oracledb.DatabaseError, self.cursor.executemany, statement, rows) self.assertEqual(self.cursor.rowcount, 3) def test_1225_executemany_with_invalid_parameters(self): "1225 - test calling executemany() with invalid parameters" sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" self.assertRaises(TypeError, self.cursor.executemany, sql, "These are not valid parameters") def test_1226_executemany_no_parameters(self): "1226 - test calling executemany() without any bind parameters" num_rows = 5 self.cursor.execute("truncate table TestTempTable") self.cursor.executemany(""" declare t_Id number; begin select nvl(count(*), 0) + 1 into t_Id from TestTempTable; insert into TestTempTable (IntCol, StringCol) values (t_Id, 'Test String ' || t_Id); end;""", num_rows) self.assertEqual(self.cursor.rowcount, num_rows) self.cursor.execute("select count(*) from TestTempTable") count, = self.cursor.fetchone() self.assertEqual(count, num_rows) def test_1227_executemany_bound_earlier(self): "1227 - test calling executemany() with binds performed earlier" num_rows = 9 self.cursor.execute("truncate table TestTempTable") var = self.cursor.var(int, arraysize=num_rows) self.cursor.setinputsizes(var) self.cursor.executemany(""" declare t_Id number; begin select nvl(count(*), 0) + 1 into t_Id from TestTempTable; insert into TestTempTable (IntCol, StringCol) values (t_Id, 'Test String ' || t_Id); select sum(IntCol) into :1 from TestTempTable; end;""", num_rows) self.assertEqual(self.cursor.rowcount, num_rows) expected_data = [1, 3, 6, 10, 15, 21, 28, 36, 45] self.assertEqual(var.values, expected_data) def test_1228_prepare(self): "1228 - test preparing a statement and executing it multiple times" self.assertEqual(self.cursor.statement, None) statement = "begin :value := :value + 5; end;" self.cursor.prepare(statement) var = self.cursor.var(oracledb.NUMBER) self.assertEqual(self.cursor.statement, statement) var.setvalue(0, 2) self.cursor.execute(None, value = var) self.assertEqual(var.getvalue(), 7) self.cursor.execute(None, value = var) self.assertEqual(var.getvalue(), 12) self.cursor.execute("begin :value2 := 3; end;", value2 = var) self.assertEqual(var.getvalue(), 3) def test_1229_exception_on_close(self): "1229 - confirm an exception is raised after closing a cursor" self.cursor.close() self.assertRaises(oracledb.InterfaceError, self.cursor.execute, "select 1 from dual") def test_1230_iterators(self): "1230 - test iterators" self.cursor.execute(""" select IntCol from TestNumbers where IntCol between 1 and 3 order by IntCol""") rows = [v for v, in self.cursor] self.assertEqual(rows, [1, 2, 3]) def test_1231_iterators_interrupted(self): "1231 - test iterators (with intermediate execute)" self.cursor.execute("truncate table TestTempTable") self.cursor.execute(""" select IntCol from TestNumbers where IntCol between 1 and 3 order by IntCol""") test_iter = iter(self.cursor) value, = next(test_iter) self.cursor.execute("insert into TestTempTable (IntCol) values (1)") self.assertRaises(oracledb.InterfaceError, next, test_iter) def test_1232_bind_names(self): "1232 - test that bindnames() works correctly." self.assertRaises(oracledb.ProgrammingError, self.cursor.bindnames) self.cursor.prepare("begin null; end;") self.assertEqual(self.cursor.bindnames(), []) self.cursor.prepare("begin :retval := :inval + 5; end;") self.assertEqual(self.cursor.bindnames(), ["RETVAL", "INVAL"]) self.cursor.prepare("begin :retval := :a * :a + :b * :b; end;") self.assertEqual(self.cursor.bindnames(), ["RETVAL", "A", "B"]) self.cursor.prepare("begin :a := :b + :c + :d + :e + :f + :g + " + \ ":h + :i + :j + :k + :l; end;") names = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"] self.assertEqual(self.cursor.bindnames(), names) self.cursor.prepare("select :a * :a + :b * :b from dual") self.assertEqual(self.cursor.bindnames(), ["A", "B"]) def test_1233_bad_execute(self): "1233 - test that subsequent executes succeed after bad execute" self.assertRaises(oracledb.DatabaseError, self.cursor.execute, "begin raise_application_error(-20000, 'this); end;") self.cursor.execute("begin null; end;") def test_1234_fetch_after_bad_execute(self): "1234 - test that subsequent fetches fail after bad execute" self.assertRaises(oracledb.DatabaseError, self.cursor.execute, "select y from dual") self.assertRaises(oracledb.InterfaceError, self.cursor.fetchall) def test_1235_scroll_absolute_exception_after(self): "1235 - test scrolling absolute yields an exception (after result set)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") self.assertRaises(oracledb.DatabaseError, cursor.scroll, 12, "absolute") def test_1236_scroll_absolute_in_buffer(self): "1236 - test scrolling absolute (when in buffers)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") cursor.fetchmany() self.assertTrue(cursor.arraysize > 1, "array size must exceed 1 for this test to work correctly") cursor.scroll(1, mode="absolute") row = cursor.fetchone() self.assertEqual(row[0], 1.25) self.assertEqual(cursor.rowcount, 1) def test_1237_scroll_absolute_not_in_buffer(self): "1237 - test scrolling absolute (when not in buffers)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") cursor.scroll(6, mode="absolute") row = cursor.fetchone() self.assertEqual(row[0], 7.5) self.assertEqual(cursor.rowcount, 6) def test_1238_scroll_first_in_buffer(self): "1238 - test scrolling to first row in result set (in buffers)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") cursor.fetchmany() cursor.scroll(mode="first") row = cursor.fetchone() self.assertEqual(row[0], 1.25) self.assertEqual(cursor.rowcount, 1) def test_1239_scroll_first_not_in_buffer(self): "1239 - test scrolling to first row in result set (not in buffers)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") cursor.fetchmany() cursor.fetchmany() cursor.scroll(mode="first") row = cursor.fetchone() self.assertEqual(row[0], 1.25) self.assertEqual(cursor.rowcount, 1) def test_1240_scroll_last(self): "1240 - test scrolling to last row in result set" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") cursor.scroll(mode="last") row = cursor.fetchone() self.assertEqual(row[0], 12.5) self.assertEqual(cursor.rowcount, 10) def test_1241_scroll_relative_exception_after(self): "1241 - test scrolling relative yields an exception (after result set)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") self.assertRaises(oracledb.DatabaseError, cursor.scroll, 15) def test_1242_scroll_relative_exception_before(self): "1242 - test scrolling relative yields exception (before result set)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") self.assertRaises(oracledb.DatabaseError, cursor.scroll, -5) def test_1243_scroll_relative_in_buffer(self): "1243 - test scrolling relative (when in buffers)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") cursor.fetchmany() message = "array size must exceed 1 for this test to work correctly" self.assertTrue(cursor.arraysize > 1, message) cursor.scroll(2 - cursor.rowcount) row = cursor.fetchone() self.assertEqual(row[0], 2.5) self.assertEqual(cursor.rowcount, 2) def test_1244_scroll_relative_not_in_buffer(self): "1244 - test scrolling relative (when not in buffers)" cursor = self.connection.cursor(scrollable=True) cursor.arraysize = self.cursor.arraysize cursor.execute(""" select NumberCol from TestNumbers order by IntCol""") cursor.fetchmany() cursor.fetchmany() message = "array size must exceed 1 for this test to work correctly" self.assertTrue(cursor.arraysize > 1, message) cursor.scroll(3 - cursor.rowcount) row = cursor.fetchone() self.assertEqual(row[0], 3.75) self.assertEqual(cursor.rowcount, 3) def test_1245_scroll_no_rows(self): "1245 - test scrolling when there are no rows" self.cursor.execute("truncate table TestTempTable") cursor = self.connection.cursor(scrollable=True) cursor.execute("select * from TestTempTable") cursor.scroll(mode="last") self.assertEqual(cursor.fetchall(), []) cursor.scroll(mode="first") self.assertEqual(cursor.fetchall(), []) self.assertRaises(oracledb.DatabaseError, cursor.scroll, 1, mode="absolute") def test_1246_scroll_differing_array_and_fetch_sizes(self): "1246 - test scrolling with differing array and fetch array sizes" self.cursor.execute("truncate table TestTempTable") for i in range(30): self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:1, null)""", (i + 1,)) for arraysize in range(1, 6): cursor = self.connection.cursor(scrollable = True) cursor.arraysize = arraysize cursor.execute("select IntCol from TestTempTable order by IntCol") for num_rows in range(1, arraysize + 1): cursor.scroll(15, "absolute") rows = cursor.fetchmany(num_rows) self.assertEqual(rows[0][0], 15) self.assertEqual(cursor.rowcount, 15 + num_rows - 1) cursor.scroll(9) rows = cursor.fetchmany(num_rows) num_rows_fetched = len(rows) self.assertEqual(rows[0][0], 15 + num_rows + 8) self.assertEqual(cursor.rowcount, 15 + num_rows + num_rows_fetched + 7) cursor.scroll(-12) rows = cursor.fetchmany(num_rows) count = 15 + num_rows + num_rows_fetched - 5 self.assertEqual(rows[0][0], count) count = 15 + num_rows + num_rows_fetched + num_rows - 6 self.assertEqual(cursor.rowcount, count) def test_1247_set_input_sizes_negative(self): "1247 - test cursor.setinputsizes() with invalid parameters" val = decimal.Decimal(5) self.assertRaises(oracledb.InterfaceError, self.cursor.setinputsizes, val, x=val) self.assertRaises(TypeError, self.cursor.setinputsizes, val) def test_1248_set_input_sizes_no_parameters(self): "1248 - test setting input sizes without any parameters" self.cursor.setinputsizes() self.cursor.execute("select :val from dual", val="Test Value") self.assertEqual(self.cursor.fetchall(), [("Test Value",)]) def test_1249_set_input_sizes_empty_dict(self): "1249 - test setting input sizes with an empty dictionary" empty_dict = {} self.cursor.prepare("select 236 from dual") self.cursor.setinputsizes(**empty_dict) self.cursor.execute(None, empty_dict) self.assertEqual(self.cursor.fetchall(), [(236,)]) def test_1250_set_input_sizes_empty_list(self): "1250 - test setting input sizes with an empty list" empty_list = {} self.cursor.prepare("select 239 from dual") self.cursor.setinputsizes(*empty_list) self.cursor.execute(None, empty_list) self.assertEqual(self.cursor.fetchall(), [(239,)]) def test_1251_set_input_sizes_by_position(self): "1251 - test setting input sizes with positional args" var = self.cursor.var(oracledb.STRING, 100) self.cursor.setinputsizes(None, 5, None, 10, None, oracledb.NUMBER) self.cursor.execute(""" begin :1 := :2 || to_char(:3) || :4 || to_char(:5) || to_char(:6); end;""", [var, 'test_', 5, '_second_', 3, 7]) self.assertEqual(var.getvalue(), "test_5_second_37") def test_1252_string_format(self): "1252 - test string format of cursor" format_string = ">" expected_value = format_string % \ (test_env.get_main_user(), test_env.get_connect_string()) self.assertEqual(str(self.cursor), expected_value) def test_1253_cursor_fetch_raw(self): "1253 - test cursor.fetchraw()" cursor = self.connection.cursor() cursor.arraysize = 25 cursor.execute("select LongIntCol from TestNumbers order by IntCol") self.assertEqual(cursor.fetchraw(), 10) self.assertEqual(cursor.fetchvars[0].getvalue(), 38) def test_1254_parse_query(self): "1254 - test parsing query statements" sql = "select LongIntCol from TestNumbers where IntCol = :val" self.cursor.parse(sql) self.assertEqual(self.cursor.statement, sql) self.assertEqual(self.cursor.description, [('LONGINTCOL', oracledb.DB_TYPE_NUMBER, 17, None, 16, 0, 0)]) def test_1255_set_output_size(self): "1255 - test cursor.setoutputsize() does not fail (but does nothing)" self.cursor.setoutputsize(100, 2) def test_1256_var_negative(self): "1256 - test cursor.var() with invalid parameters" self.assertRaises(TypeError, self.cursor.var, 5) def test_1257_arrayvar_negative(self): "1257 - test cursor.arrayvar() with invalid parameters" self.assertRaises(TypeError, self.cursor.arrayvar, 5, 1) def test_1258_boolean_without_plsql(self): "1258 - test binding boolean data without the use of PL/SQL" self.cursor.execute("truncate table TestTempTable") sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" self.cursor.execute(sql, (False, "Value should be 0")) self.cursor.execute(sql, (True, "Value should be 1")) self.cursor.execute(""" select IntCol, StringCol from TestTempTable order by IntCol""") expected_value = [(0, "Value should be 0"), (1, "Value should be 1")] self.assertEqual(self.cursor.fetchall(), expected_value) def test_1259_as_context_manager(self): "1259 - test using a cursor as a context manager" with self.cursor as cursor: cursor.execute("truncate table TestTempTable") cursor.execute("select count(*) from TestTempTable") count, = cursor.fetchone() self.assertEqual(count, 0) self.assertRaises(oracledb.InterfaceError, self.cursor.close) def test_1260_query_row_count(self): "1260 - test that rowcount attribute is reset to zero on query execute" sql = "select * from dual where 1 = :s" self.cursor.execute(sql, [0]) self.cursor.fetchone() self.assertEqual(self.cursor.rowcount, 0) self.cursor.execute(sql, [1]) self.cursor.fetchone() self.assertEqual(self.cursor.rowcount, 1) self.cursor.execute(sql, [1]) self.cursor.fetchone() self.assertEqual(self.cursor.rowcount, 1) self.cursor.execute(sql, [0]) self.cursor.fetchone() self.assertEqual(self.cursor.rowcount, 0) def test_1261_var_type_name_none(self): "1261 - test that the typename attribute can be passed a value of None" value_to_set = 5 var = self.cursor.var(int, typename=None) var.setvalue(0, value_to_set) self.assertEqual(var.getvalue(), value_to_set) def test_1262_var_type_with_object_type(self): "1262 - test that an object type can be used as type in cursor.var()" obj_type = self.connection.gettype("UDT_OBJECT") var = self.cursor.var(obj_type) self.cursor.callproc("pkg_TestBindObject.BindObjectOut", (28, "Bind obj out", var)) obj = var.getvalue() result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str, (obj,)) exp = "udt_Object(28, 'Bind obj out', null, null, null, null, null)" self.assertEqual(result, exp) def test_1263_fetch_xmltype(self): "1263 - test that fetching an XMLType returns a string" int_val = 5 label = "IntCol" expected_result = "<%s>%s" % (label, int_val, label) self.cursor.execute(""" select XMLElement("%s", IntCol) from TestStrings where IntCol = :int_val""" % label, int_val=int_val) result, = self.cursor.fetchone() self.assertEqual(result, expected_result) def test_1264_lastrowid(self): "1264 - test last rowid" # no statement executed: no rowid self.assertEqual(None, self.cursor.lastrowid) # DDL statement executed: no rowid self.cursor.execute("truncate table TestTempTable") self.assertEqual(None, self.cursor.lastrowid) # statement prepared: no rowid self.cursor.prepare("insert into TestTempTable (IntCol) values (:1)") self.assertEqual(None, self.cursor.lastrowid) # multiple rows inserted: rowid of last row inserted rows = [(n,) for n in range(225)] self.cursor.executemany(None, rows) rowid = self.cursor.lastrowid self.cursor.execute(""" select rowid from TestTempTable where IntCol = :1""", rows[-1]) self.assertEqual(rowid, self.cursor.fetchone()[0]) # statement executed but no rows updated: no rowid self.cursor.execute("delete from TestTempTable where 1 = 0") self.assertEqual(None, self.cursor.lastrowid) # stetement executed with one row updated: rowid of updated row self.cursor.execute(""" update TestTempTable set StringCol = 'Modified' where IntCol = :1""", rows[-2]) rowid = self.cursor.lastrowid self.cursor.execute(""" select rowid from TestTempTable where IntCol = :1""", rows[-2]) self.assertEqual(rowid, self.cursor.fetchone()[0]) # statement executed with many rows updated: rowid of last updated row self.cursor.execute(""" update TestTempTable set StringCol = 'Row ' || to_char(IntCol) where IntCol = :1""", rows[-3]) rowid = self.cursor.lastrowid self.cursor.execute(""" select StringCol from TestTempTable where rowid = :1""", [rowid]) self.assertEqual("Row %s" % rows[-3], self.cursor.fetchone()[0]) def test_1265_prefetchrows(self): "1265 - test prefetch rows" self.setup_round_trip_checker() # perform simple query and verify only one round trip is needed with self.connection.cursor() as cursor: cursor.execute("select sysdate from dual").fetchall() self.assertRoundTrips(1) # set prefetchrows to 1 and verify that two round trips are now needed with self.connection.cursor() as cursor: cursor.prefetchrows = 1 cursor.execute("select sysdate from dual").fetchall() self.assertRoundTrips(2) # simple DDL only requires a single round trip with self.connection.cursor() as cursor: cursor.execute("truncate table TestTempTable") self.assertRoundTrips(1) # array execution only requires a single round trip num_rows = 590 with self.connection.cursor() as cursor: sql = "insert into TestTempTable (IntCol) values (:1)" data = [(n + 1,) for n in range(num_rows)] cursor.executemany(sql, data) self.assertRoundTrips(1) # setting prefetch and array size to 1 requires a round-trip for each # row with self.connection.cursor() as cursor: cursor.prefetchrows = 1 cursor.arraysize = 1 cursor.execute("select IntCol from TestTempTable").fetchall() self.assertRoundTrips(num_rows + 1) # setting prefetch and array size to 300 requires 2 round-trips with self.connection.cursor() as cursor: cursor.prefetchrows = 300 cursor.arraysize = 300 cursor.execute("select IntCol from TestTempTable").fetchall() self.assertRoundTrips(2) def test_1266_refcursor_prefetchrows(self): "1266 - test prefetch rows and arraysize using a refcursor" self.setup_round_trip_checker() # simple DDL only requires a single round trip with self.connection.cursor() as cursor: cursor.execute("truncate table TestTempTable") self.assertRoundTrips(1) # array execution only requires a single round trip num_rows = 590 with self.connection.cursor() as cursor: sql = "insert into TestTempTable (IntCol) values (:1)" data = [(n + 1,) for n in range(num_rows)] cursor.executemany(sql, data) self.assertRoundTrips(1) # create refcursor and execute stored procedure with self.connection.cursor() as cursor: refcursor = self.connection.cursor() refcursor.prefetchrows = 150 refcursor.arraysize = 50 cursor.callproc("myrefcursorproc", [refcursor]) refcursor.fetchall() self.assertRoundTrips(4) def test_1267_existing_cursor_prefetchrows(self): "1267 - test prefetch rows using existing cursor" self.setup_round_trip_checker() # Set prefetch rows on an existing cursor num_rows = 590 with self.connection.cursor() as cursor: cursor.execute("truncate table TestTempTable") sql = "insert into TestTempTable (IntCol) values (:1)" data = [(n + 1,) for n in range(num_rows)] cursor.executemany(sql, data) cursor.prefetchrows = 300 cursor.arraysize = 300 cursor.execute("select IntCol from TestTempTable").fetchall() self.assertRoundTrips(4) def test_1268_bind_names_with_single_line_comments(self): "1268 - test bindnames() with single line comments" self.cursor.prepare("""--begin :value2 := :a + :b + :c +:a +3; end; begin :value2 := :a + :c +3; end; """) self.assertEqual(self.cursor.bindnames(), ["VALUE2", "A", "C"]) def test_1269_bind_names_with_multi_line_comments(self): "1269 - test bindnames() with multi line comments" self.cursor.prepare("""/*--select * from :a where :a = 1 select * from table_names where :a = 1*/ select * from :table_name where :value = 1 """) self.assertEqual(self.cursor.bindnames(), ["TABLE_NAME", "VALUE"]) def test_1270_execute_bind_names_with_incorrect_bind(self): "1270 - test executing a statement with an incorrect named bind" statement = "select * from TestStrings where IntCol = :value" self.assertRaises(oracledb.DatabaseError, self.cursor.execute, statement, value2=3) def test_1271_execute_with_named_binds(self): "1271 - test executing a statement with named binds" statement = "select * from TestNumbers where IntCol = :value1 " + \ "and LongIntCol = :value2" result = self.cursor.execute(statement, value1=1, value2=38) self.assertEqual(len(result.fetchall()), 1) def test_1272_execute_bind_position_with_incorrect_bind(self): "1272 - test executing a statement with an incorrect positional bind" statement = "select * from TestNumbers where IntCol = :value " + \ "and LongIntCol = :value2" self.assertRaises(oracledb.DatabaseError, self.cursor.execute, statement, [3]) def test_1273_execute_with_positional_binds(self): "1273 - test executing a statement with positional binds" statement = "select * from TestNumbers where IntCol = :value " + \ "and LongIntCol = :value2" result = self.cursor.execute(statement, [1,38]) self.assertEqual(len(result.fetchall()), 1) def test_1274_execute_with_rebinding_bind_name(self): "1274 - test executing a statement after rebinding a named bind" statement = "begin :value := :value2 + 5; end;" simple_var = self.cursor.var(oracledb.NUMBER) simple_var2 = self.cursor.var(oracledb.NUMBER) simple_var2.setvalue(0, 5) result = self.cursor.execute(statement, value=simple_var, value2=simple_var2) self.assertEqual(result, None) self.assertEqual(simple_var.getvalue(), 10) simple_var = self.cursor.var(oracledb.NATIVE_FLOAT) simple_var2 = self.cursor.var(oracledb.NATIVE_FLOAT) simple_var2.setvalue(0, 10) result = self.cursor.execute(statement, value=simple_var, value2=simple_var2) self.assertEqual(result, None) self.assertEqual(simple_var.getvalue(), 15) def test_1275_bind_names_with_strings(self): "1275 - test bindnames() with strings in the statement" statement = """ begin :value := to_date('20021231 12:31:00', 'YYYYMMDD HH24:MI:SS'); end;""" self.cursor.prepare(statement) self.assertEqual(self.cursor.bindnames(), ["VALUE"]) def test_1276_bind_by_name_with_duplicates(self): "1276 - test executing a PL/SQL statement with duplicate binds" statement = "begin :value := :value + 5; end;" simple_var = self.cursor.var(oracledb.NUMBER) simple_var.setvalue(0, 5) result = self.cursor.execute(statement, value=simple_var) self.assertEqual(result, None) self.assertEqual(simple_var.getvalue(), 10) def test_1277_positional_bind_with_duplicates(self): "1277 - test executing a PL/SQL statement with duplicate binds" statement = "begin :value := :value + 5; end;" simple_var = self.cursor.var(oracledb.NUMBER) simple_var.setvalue(0, 5) self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), 10) def test_1278_execute_with_incorrect_bind_values(self): "1278 - test executing a statement with an incorrect number of binds" statement = "begin :value := :value2 + 5; end;" var = self.cursor.var(oracledb.NUMBER) var.setvalue(0, 5) self.assertRaises(oracledb.DatabaseError, self.cursor.execute, statement) self.assertRaises(oracledb.DatabaseError, self.cursor.execute, statement, value=var) self.assertRaises(oracledb.DatabaseError, self.cursor.execute, statement, value=var, value2=var, value3=var) def test_1279_change_in_size_on_successive_bind(self): "1279 - change in size on subsequent binds does not use optimised path" self.cursor.execute("truncate table TestTempTable") data = [ (1, "Test String #1"), (2, "ABC" * 100) ] sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" for row in data: self.cursor.execute(sql, row) self.connection.commit() self.cursor.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(self.cursor.fetchall(), data) def test_1280_change_in_type_on_successive_bind(self): "1280 - change in type on subsequent binds cannot use optimised path" sql = "select :1 from dual" self.cursor.execute(sql, ('W',)) row, = self.cursor.fetchone() self.assertEqual(row, 'W') self.cursor.execute(sql, ('S',)) row, = self.cursor.fetchone() self.assertEqual(row, 'S') self.cursor.execute(sql, (7,)) row, = self.cursor.fetchone() self.assertEqual(row, '7') def test_1281_dml_can_use_optimised_path(self): "1281 - test that dml can use optimised path" data_to_insert = [ (1, "Test String #1"), (2, "Test String #2"), (3, "Test String #3") ] self.cursor.execute("truncate table TestTempTable") sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" for row in data_to_insert: with self.connection.cursor() as cursor: cursor.execute(sql, row) self.connection.commit() self.cursor.execute(""" select IntCol, StringCol from TestTempTable order by IntCol""") self.assertEqual(self.cursor.fetchall(), data_to_insert) def test_1282_parse_plsql(self): "1282 - test parsing plsql statements" sql = "begin :value := 5; end;" self.cursor.parse(sql) self.assertEqual(self.cursor.statement, sql) self.assertEqual(self.cursor.description, None) def test_1283_parse_ddl(self): "1283 - test parsing ddl statements" sql = "truncate table TestTempTable" self.cursor.parse(sql) self.assertEqual(self.cursor.statement, sql) self.assertEqual(self.cursor.description, None) def test_1284_parse_dml(self): "1284 - test parsing dml statements" sql = "insert into TestTempTable (IntCol) values (1)" self.cursor.parse(sql) self.assertEqual(self.cursor.statement, sql) self.assertEqual(self.cursor.description, None) def test_1285_executemany_with_plsql_binds(self): "1285 - test executing plsql statements multiple times (with binds)" var = self.cursor.var(int, arraysize=5) self.cursor.setinputsizes(var) data = [[25], [30], [None], [35], [None]] exepected_data = [25, 30, None, 35, None] self.cursor.executemany("declare t number; begin t := :1; end;", data) self.assertEqual(var.values, exepected_data) def test_1286_encodingErrors_deprecation(self): "1286 - test to verify encodingErrors is deprecated" errors = 'strict' self.assertRaises(oracledb.ProgrammingError, self.cursor.var, oracledb.NUMBER, encoding_errors=errors, encodingErrors=errors) def test_1287_keywordParameters_deprecation(self): "1287 - test to verify keywordParameters is deprecated" out_value = self.cursor.var(oracledb.NUMBER) kwargs = dict(a_OutValue=out_value) self.assertRaises(oracledb.ProgrammingError, self.cursor.callproc, "proc_Test", ("hi", 5), kwargs, keywordParameters=kwargs) extra_amount = self.cursor.var(oracledb.NUMBER) extra_amount.setvalue(0, 5) kwargs = dict(a_ExtraAmount=extra_amount, a_String="hi") self.assertRaises(oracledb.ProgrammingError, self.cursor.callfunc, "func_Test", oracledb.NUMBER, [], kwargs, keywordParameters=kwargs) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1300_cursor_var.py000066400000000000000000000115201414105416400221570ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 1300 - Module for testing cursor variables """ import sys import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def test_1300_bind_cursor(self): "1300 - test binding in a cursor" cursor = self.connection.cursor() self.assertEqual(cursor.description, None) self.cursor.execute(""" begin open :cursor for select 'X' StringValue from dual; end;""", cursor=cursor) varchar_ratio, nvarchar_ratio = test_env.get_charset_ratios() expected_value = [ ('STRINGVALUE', oracledb.DB_TYPE_CHAR, 1, varchar_ratio, None, None, True) ] self.assertEqual(cursor.description, expected_value) self.assertEqual(cursor.fetchall(), [('X',)]) def test_1301_bind_cursor_in_package(self): "1301 - test binding in a cursor from a package" cursor = self.connection.cursor() self.assertEqual(cursor.description, None) self.cursor.callproc("pkg_TestRefCursors.TestOutCursor", (2, cursor)) varchar_ratio, nvarchar_ratio = test_env.get_charset_ratios() expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('STRINGCOL', oracledb.DB_TYPE_VARCHAR, 20, 20 * varchar_ratio, None, None, False) ] self.assertEqual(cursor.description, expected_value) self.assertEqual(cursor.fetchall(), [(1, 'String 1'), (2, 'String 2')]) def test_1302_bind_self(self): "1302 - test that binding the cursor itself is not supported" cursor = self.connection.cursor() sql = """ begin open :pcursor for select 1 from dual; end;""" self.assertRaises(oracledb.DatabaseError, cursor.execute, sql, pcursor=cursor) def test_1303_execute_after_close(self): "1303 - test returning a ref cursor after closing it" out_cursor = self.connection.cursor() sql = """ begin open :pcursor for select IntCol from TestNumbers order by IntCol; end;""" self.cursor.execute(sql, pcursor=out_cursor) rows = out_cursor.fetchall() out_cursor.close() out_cursor = self.connection.cursor() self.cursor.execute(sql, pcursor=out_cursor) rows2 = out_cursor.fetchall() self.assertEqual(rows, rows2) def test_1304_fetch_cursor(self): "1304 - test fetching a cursor" self.cursor.execute(""" select IntCol, cursor(select IntCol + 1 from dual) CursorValue from TestNumbers order by IntCol""") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('CURSORVALUE', oracledb.DB_TYPE_CURSOR, None, None, None, None, True) ] self.assertEqual(self.cursor.description, expected_value) for i in range(1, 11): number, cursor = self.cursor.fetchone() self.assertEqual(number, i) self.assertEqual(cursor.fetchall(), [(i + 1,)]) def test_1305_ref_cursor_binds(self): "1305 - test that ref cursor binds cannot use optimised path" ref_cursor = self.connection.cursor() sql = """ begin open :rcursor for select IntCol, StringCol from TestStrings where IntCol between :start_value and :end_value; end;""" self.cursor.execute(sql, rcursor=ref_cursor, start_value=2, end_value=4) expected_value = [ (2, 'String 2'), (3, 'String 3'), (4, 'String 4') ] rows = ref_cursor.fetchall() ref_cursor.close() self.assertEqual(rows, expected_value) ref_cursor = self.connection.cursor() self.cursor.execute(sql, rcursor=ref_cursor, start_value=5, end_value=6) expected_value = [ (5, 'String 5'), (6, 'String 6') ] rows = ref_cursor.fetchall() self.assertEqual(rows, expected_value) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1400_datetime_var.py000066400000000000000000000251021414105416400224400ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 1400 - Module for testing date/time variables """ import datetime import time import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def setUp(self): super().setUp() self.raw_data = [] self.data_by_key = {} for i in range(1, 11): time_tuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1) time_in_ticks = time.mktime(time_tuple) + i * 86400 + i * 8640 date_col = oracledb.TimestampFromTicks(int(time_in_ticks)) if i % 2: time_in_ticks = time.mktime(time_tuple) + i * 86400 * 2 + \ i * 12960 nullable_col = oracledb.TimestampFromTicks(int(time_in_ticks)) else: nullable_col = None tuple = (i, date_col, nullable_col) self.raw_data.append(tuple) self.data_by_key[i] = tuple def test_1400_bind_date(self): "1400 - test binding in a date" self.cursor.execute(""" select * from TestDates where DateCol = :value""", value = oracledb.Timestamp(2002, 12, 13, 9, 36, 0)) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[4]]) def test_1401_bind_datetime(self): "1401 - test binding in a datetime.datetime value" self.cursor.execute(""" select * from TestDates where DateCol = :value""", value=datetime.datetime(2002, 12, 13, 9, 36, 0)) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[4]]) def test_1402_bind_date_in_datetime_var(self): "1402 - test binding date in a datetime variable" var = self.cursor.var(oracledb.DATETIME) date_val = datetime.date.today() var.setvalue(0, date_val) self.cursor.execute("select :1 from dual", [var]) result, = self.cursor.fetchone() self.assertEqual(result.date(), date_val) def test_1403_bind_date_after_string(self): "1403 - test binding in a date after setting input sizes to a string" self.cursor.setinputsizes(value=15) self.cursor.execute(""" select * from TestDates where DateCol = :value""", value = oracledb.Timestamp(2002, 12, 14, 12, 0, 0)) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]]) def test_1404_bind_null(self): "1404 - test binding in a null" self.cursor.setinputsizes(value=oracledb.DATETIME) self.cursor.execute(""" select * from TestDates where DateCol = :value""", value = None) self.assertEqual(self.cursor.fetchall(), []) def test_1405_bind_date_array_direct(self): "1405 - test binding in a date array" return_value = self.cursor.var(oracledb.NUMBER) array = [r[1] for r in self.raw_data] statement = """ begin :return_value := pkg_TestDateArrays.TestInArrays( :start_value, :base_date, :array); end;""" self.cursor.execute(statement, return_value=return_value, start_value=5, base_date=oracledb.Date(2002, 12, 12), array=array) self.assertEqual(return_value.getvalue(), 35.5) array = array + array[:5] self.cursor.execute(statement, start_value=7, base_date=oracledb.Date(2002, 12, 13), array=array) self.assertEqual(return_value.getvalue(), 24.0) def test_1406_bind_date_array_by_sizes(self): "1406 - test binding in a date array (with setinputsizes)" return_value = self.cursor.var(oracledb.NUMBER) self.cursor.setinputsizes(array=[oracledb.DATETIME, 10]) array = [r[1] for r in self.raw_data] self.cursor.execute(""" begin :return_value := pkg_TestDateArrays.TestInArrays( :start_value, :base_date, :array); end;""", return_value=return_value, start_value=6, base_date=oracledb.Date(2002, 12, 13), array=array) self.assertEqual(return_value.getvalue(), 26.5) def test_1407_bind_date_array_by_var(self): "1407 - test binding in a date array (with arrayvar)" return_value = self.cursor.var(oracledb.NUMBER) array = self.cursor.arrayvar(oracledb.DATETIME, 10, 20) array.setvalue(0, [r[1] for r in self.raw_data]) self.cursor.execute(""" begin :return_value := pkg_TestDateArrays.TestInArrays( :start_value, :base_date, :array); end;""", return_value=return_value, start_value=7, base_date=oracledb.Date(2002, 12, 14), array=array) self.assertEqual(return_value.getvalue(), 17.5) def test_1408_bind_in_out_date_array_by_var(self): "1408 - test binding in/out a date array (with arrayvar)" array = self.cursor.arrayvar(oracledb.DATETIME, 10, 100) original_data = [r[1] for r in self.raw_data] array.setvalue(0, original_data) self.cursor.execute(""" begin pkg_TestDateArrays.TestInOutArrays(:num_elems, :array); end;""", num_elems=5, array=array) self.assertEqual(array.getvalue(), [ oracledb.Timestamp(2002, 12, 17, 2, 24, 0), oracledb.Timestamp(2002, 12, 18, 4, 48, 0), oracledb.Timestamp(2002, 12, 19, 7, 12, 0), oracledb.Timestamp(2002, 12, 20, 9, 36, 0), oracledb.Timestamp(2002, 12, 21, 12, 0, 0) ] + \ original_data[5:]) def test_1409_bind_out_date_array_by_var(self): "1409 - test binding out a date array (with arrayvar)" array = self.cursor.arrayvar(oracledb.DATETIME, 6, 100) self.cursor.execute(""" begin pkg_TestDateArrays.TestOutArrays(:num_elems, :array); end;""", num_elems=6, array=array) self.assertEqual(array.getvalue(), [ oracledb.Timestamp(2002, 12, 13, 4, 48, 0), oracledb.Timestamp(2002, 12, 14, 9, 36, 0), oracledb.Timestamp(2002, 12, 15, 14, 24, 0), oracledb.Timestamp(2002, 12, 16, 19, 12, 0), oracledb.Timestamp(2002, 12, 18, 0, 0, 0), oracledb.Timestamp(2002, 12, 19, 4, 48, 0) ]) def test_1410_bind_out_set_input_sizes(self): "1410 - test binding out with set input sizes defined" bind_vars = self.cursor.setinputsizes(value=oracledb.DATETIME) self.cursor.execute(""" begin :value := to_date(20021209, 'YYYYMMDD'); end;""") self.assertEqual(bind_vars["value"].getvalue(), oracledb.Timestamp(2002, 12, 9)) def test_1411_bind_in_out_set_input_sizes(self): "1411 - test binding in/out with set input sizes defined" bind_vars = self.cursor.setinputsizes(value=oracledb.DATETIME) self.cursor.execute(""" begin :value := :value + 5.25; end;""", value=oracledb.Timestamp(2002, 12, 12, 10, 0, 0)) self.assertEqual(bind_vars["value"].getvalue(), oracledb.Timestamp(2002, 12, 17, 16, 0, 0)) def test_1412_bind_out_var(self): "1412 - test binding out with cursor.var() method" var = self.cursor.var(oracledb.DATETIME) self.cursor.execute(""" begin :value := to_date('20021231 12:31:00', 'YYYYMMDD HH24:MI:SS'); end;""", value=var) self.assertEqual(var.getvalue(), oracledb.Timestamp(2002, 12, 31, 12, 31, 0)) def test_1413_bind_in_out_var_direct_set(self): "1413 - test binding in/out with cursor.var() method" var = self.cursor.var(oracledb.DATETIME) var.setvalue(0, oracledb.Timestamp(2002, 12, 9, 6, 0, 0)) self.cursor.execute(""" begin :value := :value + 5.25; end;""", value=var) self.assertEqual(var.getvalue(), oracledb.Timestamp(2002, 12, 14, 12, 0, 0)) def test_1414_cursor_description(self): "1414 - test cursor description is accurate" self.cursor.execute("select * from TestDates") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('DATECOL', oracledb.DB_TYPE_DATE, 23, None, None, None, False), ('NULLABLECOL', oracledb.DB_TYPE_DATE, 23, None, None, None, True) ] self.assertEqual(self.cursor.description, expected_value) def test_1415_fetchall(self): "1415 - test that fetching all of the data returns the correct results" self.cursor.execute("select * From TestDates order by IntCol") self.assertEqual(self.cursor.fetchall(), self.raw_data) self.assertEqual(self.cursor.fetchall(), []) def test_1416_fetchmany(self): "1416 - test that fetching data in chunks returns the correct results" self.cursor.execute("select * From TestDates order by IntCol") self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3]) self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5]) self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9]) self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:]) self.assertEqual(self.cursor.fetchmany(3), []) def test_1417_fetchone(self): "1417 - test that fetching a single row returns the correct results" self.cursor.execute(""" select * from TestDates where IntCol in (3, 4) order by IntCol""") self.assertEqual(self.cursor.fetchone(), self.data_by_key[3]) self.assertEqual(self.cursor.fetchone(), self.data_by_key[4]) self.assertEqual(self.cursor.fetchone(), None) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1500_types.py000066400000000000000000000174601414105416400211510ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 1500 - Module for testing comparisons with database types and API types, including the synonyms retained for backwards compatibility. This module also tests for pickling/unpickling of database types and API types. """ import pickle import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): requires_connection = False def __test_compare(self, db_type, api_type): self.assertEqual(db_type, db_type) self.assertEqual(db_type, api_type) self.assertEqual(api_type, db_type) self.assertNotEqual(db_type, 5) self.assertNotEqual(db_type, oracledb.DB_TYPE_OBJECT) def __test_pickle(self, typ): self.assertIs(typ, pickle.loads(pickle.dumps(typ))) def test_1500_DB_TYPE_BFILE(self): "1500 - test oracledb.DB_TYPE_BFILE comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_BFILE, oracledb.BFILE) self.__test_pickle(oracledb.DB_TYPE_BFILE) def test_1501_DB_TYPE_BINARY_DOUBLE(self): "1501 - test oracledb.DB_TYPE_BINARY_DOUBLE comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_BINARY_DOUBLE, oracledb.NUMBER) self.assertEqual(oracledb.DB_TYPE_BINARY_DOUBLE, oracledb.NATIVE_FLOAT) self.__test_pickle(oracledb.DB_TYPE_BINARY_DOUBLE) def test_1502_DB_TYPE_BINARY_FLOAT(self): "1502 - test oracledb.DB_TYPE_BINARY_FLOAT comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_BINARY_FLOAT, oracledb.NUMBER) self.__test_pickle(oracledb.DB_TYPE_BINARY_FLOAT) def test_1503_DB_TYPE_BINARY_INTEGER(self): "1503 - test oracledb.DB_TYPE_BINARY_INTEGER comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_BINARY_INTEGER, oracledb.NUMBER) self.assertEqual(oracledb.DB_TYPE_BINARY_INTEGER, oracledb.NATIVE_INT) self.__test_pickle(oracledb.DB_TYPE_BINARY_INTEGER) def test_1504_DB_TYPE_BLOB(self): "1504 - test oracledb.DB_TYPE_BLOB comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_BLOB, oracledb.BLOB) self.__test_pickle(oracledb.DB_TYPE_BLOB) def test_1505_DB_TYPE_BOOLEAN(self): "1505 - test oracledb.DB_TYPE_BOOLEAN comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_BOOLEAN, oracledb.BOOLEAN) self.__test_pickle(oracledb.DB_TYPE_BOOLEAN) def test_1506_DB_TYPE_CHAR(self): "1506 - test oracledb.DB_TYPE_CHAR comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_CHAR, oracledb.STRING) self.assertEqual(oracledb.DB_TYPE_CHAR, oracledb.FIXED_CHAR) self.__test_pickle(oracledb.DB_TYPE_CHAR) def test_1507_DB_TYPE_CLOB(self): "1507 - test oracledb.DB_TYPE_CLOB comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_CLOB, oracledb.CLOB) self.__test_pickle(oracledb.DB_TYPE_CLOB) def test_1508_DB_TYPE_CURSOR(self): "1508 - test oracledb.DB_TYPE_CURSOR comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_CURSOR, oracledb.CURSOR) self.__test_pickle(oracledb.DB_TYPE_CURSOR) def test_1509_DB_TYPE_DATE(self): "1509 - test oracledb.DB_TYPE_DATE comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_DATE, oracledb.DATETIME) self.__test_pickle(oracledb.DB_TYPE_DATE) def test_1510_DB_TYPE_INTERVAL_DS(self): "1510 - test oracledb.DB_TYPE_INTERVAL_DS comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_INTERVAL_DS, oracledb.INTERVAL) self.__test_pickle(oracledb.DB_TYPE_INTERVAL_DS) def test_1511_DB_TYPE_LONG(self): "1511 - test oracledb.DB_TYPE_LONG comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_LONG, oracledb.STRING) self.assertEqual(oracledb.DB_TYPE_LONG, oracledb.LONG_STRING) self.__test_pickle(oracledb.DB_TYPE_LONG) def test_1512_DB_TYPE_LONG_RAW(self): "1512 - test oracledb.DB_TYPE_LONG_RAW comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_LONG_RAW, oracledb.BINARY) self.assertEqual(oracledb.DB_TYPE_LONG_RAW, oracledb.LONG_BINARY) self.__test_pickle(oracledb.DB_TYPE_LONG_RAW) def test_1513_DB_TYPE_NCHAR(self): "1513 - test oracledb.DB_TYPE_NCHAR comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_NCHAR, oracledb.STRING) self.assertEqual(oracledb.DB_TYPE_NCHAR, oracledb.FIXED_NCHAR) self.__test_pickle(oracledb.DB_TYPE_NCHAR) def test_1514_DB_TYPE_NCLOB(self): "1514 - test oracledb.DB_TYPE_NCLOB comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_NCLOB, oracledb.NCLOB) self.__test_pickle(oracledb.DB_TYPE_NCLOB) def test_1515_DB_TYPE_NUMBER(self): "1515 - test oracledb.DB_TYPE_NUMBER comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_NUMBER, oracledb.NUMBER) self.__test_pickle(oracledb.DB_TYPE_NUMBER) def test_1516_DB_TYPE_NVARCHAR(self): "1516 - test oracledb.DB_TYPE_NVARCHAR comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_NVARCHAR, oracledb.STRING) self.assertEqual(oracledb.DB_TYPE_NVARCHAR, oracledb.NCHAR) self.__test_pickle(oracledb.DB_TYPE_NVARCHAR) def test_1517_DB_TYPE_OBJECT(self): "1517 - test oracledb.DB_TYPE_OBJECT comparisons and pickling" self.assertEqual(oracledb.DB_TYPE_OBJECT, oracledb.OBJECT) self.__test_pickle(oracledb.DB_TYPE_OBJECT) def test_1518_DB_TYPE_RAW(self): "1518 - test oracledb.DB_TYPE_RAW comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_RAW, oracledb.BINARY) self.__test_pickle(oracledb.DB_TYPE_RAW) def test_1519_DB_TYPE_ROWID(self): "1519 - test oracledb.DB_TYPE_ROWID comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_ROWID, oracledb.ROWID) self.__test_pickle(oracledb.DB_TYPE_ROWID) def test_1520_DB_TYPE_TIMESTAMP(self): "1520 - test oracledb.DB_TYPE_TIMESTAMP comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_TIMESTAMP, oracledb.DATETIME) self.assertEqual(oracledb.DB_TYPE_TIMESTAMP, oracledb.TIMESTAMP) self.__test_pickle(oracledb.DB_TYPE_TIMESTAMP) def test_1521_DB_TYPE_TIMESTAMP_LTZ(self): "1521 - test oracledb.DB_TYPE_TIMESTAMP_LTZ comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_TIMESTAMP_LTZ, oracledb.DATETIME) self.__test_pickle(oracledb.DB_TYPE_TIMESTAMP_LTZ) def test_1522_DB_TYPE_TIMESTAMP_TZ(self): "1522 - test oracledb.DB_TYPE_TIMESTAMP_TZ comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_TIMESTAMP_TZ, oracledb.DATETIME) self.__test_pickle(oracledb.DB_TYPE_TIMESTAMP_TZ) def test_1523_DB_TYPE_VARCHAR(self): "1523 - test oracledb.DB_TYPE_VARCHAR comparisons and pickling" self.__test_compare(oracledb.DB_TYPE_VARCHAR, oracledb.STRING) self.__test_pickle(oracledb.DB_TYPE_VARCHAR) def test_1524_NUMBER(self): "1524 - test oracledb.NUMBER pickling" self.__test_pickle(oracledb.NUMBER) def test_1525_STRING(self): "1525 - test oracledb.STRING pickling" self.__test_pickle(oracledb.STRING) def test_1526_DATETIME(self): "1526 - test oracledb.DATETIME pickling" self.__test_pickle(oracledb.DATETIME) def test_1527_BINARY(self): "1527 - test oracledb.BINARY pickling" self.__test_pickle(oracledb.BINARY) def test_1528_ROWID(self): "1528 - test oracledb.ROWID pickling" self.__test_pickle(oracledb.ROWID) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1600_dml_returning.py000066400000000000000000000270351414105416400226560ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 1600 - Module for testing DML returning clauses """ import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def test_1600_insert(self): "1600 - test insert (single row) with DML returning" self.cursor.execute("truncate table TestTempTable") int_val = 5 str_val = "A test string" int_var = self.cursor.var(oracledb.NUMBER) str_var = self.cursor.var(str) self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:int_val, :str_val) returning IntCol, StringCol into :int_var, :str_var""", int_val=int_val, str_val=str_val, int_var=int_var, str_var=str_var) self.assertEqual(int_var.values, [[int_val]]) self.assertEqual(str_var.values, [[str_val]]) def test_1601_insert_many(self): "1601 - test insert (multiple rows) with DML returning" self.cursor.execute("truncate table TestTempTable") int_values = [5, 8, 17, 24, 6] str_values = ["Test 5", "Test 8", "Test 17", "Test 24", "Test 6"] int_var = self.cursor.var(oracledb.NUMBER, arraysize=len(int_values)) str_var = self.cursor.var(str, arraysize=len(int_values)) self.cursor.setinputsizes(None, None, int_var, str_var) data = list(zip(int_values, str_values)) self.cursor.executemany(""" insert into TestTempTable (IntCol, StringCol) values (:int_val, :str_val) returning IntCol, StringCol into :int_var, :str_var""", data) self.assertEqual(int_var.values, [[v] for v in int_values]) self.assertEqual(str_var.values, [[v] for v in str_values]) def test_1602_insert_with_small_size(self): "1602 - test insert with DML returning into too small a variable" self.cursor.execute("truncate table TestTempTable") int_val = 6 str_val = "A different test string" int_var = self.cursor.var(oracledb.NUMBER) str_var = self.cursor.var(str, 2) parameters = dict(int_val=int_val, str_val=str_val, int_var=int_var, str_var=str_var) self.assertRaises(oracledb.DatabaseError, self.cursor.execute, """ insert into TestTempTable (IntCol, StringCol) values (:int_val, :str_val) returning IntCol, StringCol into :int_var, :str_var""", parameters) def test_1603_update_single_row(self): "1603 - test update single row with DML returning" int_val = 7 str_val = "The updated value of the string" self.cursor.execute("truncate table TestTempTable") self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:1, :2)""", (int_val, "The initial value of the string")) int_var = self.cursor.var(oracledb.NUMBER) str_var = self.cursor.var(str) self.cursor.execute(""" update TestTempTable set StringCol = :str_val where IntCol = :int_val returning IntCol, StringCol into :int_var, :str_var""", int_val=int_val, str_val=str_val, int_var=int_var, str_var=str_var) self.assertEqual(int_var.values, [[int_val]]) self.assertEqual(str_var.values, [[str_val]]) def test_1604_update_no_rows(self): "1604 - test update no rows with DML returning" int_val = 8 str_val = "The updated value of the string" self.cursor.execute("truncate table TestTempTable") self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:1, :2)""", (int_val, "The initial value of the string")) int_var = self.cursor.var(oracledb.NUMBER) str_var = self.cursor.var(str) self.cursor.execute(""" update TestTempTable set StringCol = :str_val where IntCol = :int_val returning IntCol, StringCol into :int_var, :str_var""", int_val=int_val + 1, str_val=str_val, int_var=int_var, str_var=str_var) self.assertEqual(int_var.values, [[]]) self.assertEqual(str_var.values, [[]]) self.assertEqual(int_var.getvalue(), []) self.assertEqual(str_var.getvalue(), []) def test_1605_update_multiple_rows(self): "1605 - test update multiple rows with DML returning" self.cursor.execute("truncate table TestTempTable") for i in (8, 9, 10): self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:1, :2)""", (i, "The initial value of string %d" % i)) int_var = self.cursor.var(oracledb.NUMBER) str_var = self.cursor.var(str) self.cursor.execute(""" update TestTempTable set IntCol = IntCol + 15, StringCol = 'The final value of string ' || to_char(IntCol) returning IntCol, StringCol into :int_var, :str_var""", int_var=int_var, str_var=str_var) self.assertEqual(self.cursor.rowcount, 3) self.assertEqual(int_var.values, [[23, 24, 25]]) expected_values = [[ "The final value of string 8", "The final value of string 9", "The final value of string 10" ]] self.assertEqual(str_var.values, expected_values) def test_1606_update_multiple_rows_executemany(self): "1606 - test update multiple rows with DML returning (executeMany)" data = [(i, "The initial value of string %d" % i) \ for i in range(1, 11)] self.cursor.execute("truncate table TestTempTable") self.cursor.executemany(""" insert into TestTempTable (IntCol, StringCol) values (:1, :2)""", data) int_var = self.cursor.var(oracledb.NUMBER, arraysize=3) str_var = self.cursor.var(str, arraysize=3) self.cursor.setinputsizes(None, int_var, str_var) self.cursor.executemany(""" update TestTempTable set IntCol = IntCol + 25, StringCol = 'Updated value of string ' || to_char(IntCol) where IntCol < :inVal returning IntCol, StringCol into :int_var, :str_var""", [[3], [8], [11]]) expected_values = [ [26, 27], [28, 29, 30, 31, 32], [33, 34, 35] ] self.assertEqual(int_var.values, expected_values) expected_values = [ [ "Updated value of string 1", "Updated value of string 2" ], [ "Updated value of string 3", "Updated value of string 4", "Updated value of string 5", "Updated value of string 6", "Updated value of string 7" ], [ "Updated value of string 8", "Updated value of string 9", "Updated value of string 10" ] ] self.assertEqual(str_var.values, expected_values) def test_1607_insert_and_return_object(self): "1607 - test inserting an object with DML returning" type_obj = self.connection.gettype("UDT_OBJECT") string_value = "The string that will be verified" obj = type_obj.newobject() obj.STRINGVALUE = string_value out_var = self.cursor.var(oracledb.DB_TYPE_OBJECT, typename="UDT_OBJECT") self.cursor.execute(""" insert into TestObjects (IntCol, ObjectCol) values (4, :obj) returning ObjectCol into :outObj""", obj=obj, outObj=out_var) result, = out_var.getvalue() self.assertEqual(result.STRINGVALUE, string_value) self.connection.rollback() def test_1608_insert_and_return_rowid(self): "1608 - test inserting a row and returning a rowid" self.cursor.execute("truncate table TestTempTable") var = self.cursor.var(oracledb.ROWID) self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (278, 'String 278') returning rowid into :1""", (var,)) rowid, = var.getvalue() self.cursor.execute(""" select IntCol, StringCol from TestTempTable where rowid = :1""", (rowid,)) self.assertEqual(self.cursor.fetchall(), [(278, 'String 278')]) def test_1609_insert_with_ref_cursor(self): "1609 - test inserting with a REF cursor and returning a rowid" self.cursor.execute("truncate table TestTempTable") var = self.cursor.var(oracledb.ROWID) in_cursor = self.connection.cursor() in_cursor.execute(""" select StringCol from TestStrings where IntCol >= 5 order by IntCol""") self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (187, pkg_TestRefCursors.TestInCursor(:1)) returning rowid into :2""", (in_cursor, var)) rowid, = var.getvalue() self.cursor.execute(""" select IntCol, StringCol from TestTempTable where rowid = :1""", (rowid,)) self.assertEqual(self.cursor.fetchall(), [(187, 'String 7 (Modified)')]) def test_1610_delete_returning_decreasing_rows_returned(self): "1610 - test delete returning decreasing number of rows" data = [(i, "Test String %d" % i) for i in range(1, 11)] self.cursor.execute("truncate table TestTempTable") self.cursor.executemany(""" insert into TestTempTable (IntCol, StringCol) values (:1, :2)""", data) results = [] int_var = self.cursor.var(int) self.cursor.setinputsizes(None, int_var) for int_val in (5, 8, 10): self.cursor.execute(""" delete from TestTempTable where IntCol < :1 returning IntCol into :2""", [int_val]) results.append(int_var.getvalue()) self.assertEqual(results, [[1, 2, 3, 4], [5, 6, 7], [8, 9]]) def test_1611_delete_returning_no_rows_after_many_rows(self): "1611 - test delete returning no rows after returning many rows" data = [(i, "Test String %d" % i) for i in range(1, 11)] self.cursor.execute("truncate table TestTempTable") self.cursor.executemany(""" insert into TestTempTable (IntCol, StringCol) values (:1, :2)""", data) int_var = self.cursor.var(int) self.cursor.execute(""" delete from TestTempTable where IntCol < :1 returning IntCol into :2""", [5, int_var]) self.assertEqual(int_var.getvalue(), [1, 2, 3, 4]) self.cursor.execute(None, [4, int_var]) self.assertEqual(int_var.getvalue(), []) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1700_error.py000066400000000000000000000037421414105416400211360ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 1700 - Module for testing error objects """ import pickle import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def test_1700_parse_error(self): "1700 - test parse error returns offset correctly" with self.assertRaises(oracledb.Error) as cm: self.cursor.execute("begin t_Missing := 5; end;") error_obj, = cm.exception.args self.assertEqual(error_obj.offset, 6) def test_1701_pickle_error(self): "1701 - test picking/unpickling an error object" with self.assertRaises(oracledb.Error) as cm: self.cursor.execute(""" begin raise_application_error(-20101, 'Test!'); end;""") error_obj, = cm.exception.args self.assertEqual(type(error_obj), oracledb._Error) self.assertTrue("Test!" in error_obj.message) self.assertEqual(error_obj.code, 20101) self.assertEqual(error_obj.offset, 0) self.assertTrue(isinstance(error_obj.isrecoverable, bool)) new_error_obj = pickle.loads(pickle.dumps(error_obj)) self.assertEqual(type(new_error_obj), oracledb._Error) self.assertTrue(new_error_obj.message == error_obj.message) self.assertTrue(new_error_obj.code == error_obj.code) self.assertTrue(new_error_obj.offset == error_obj.offset) self.assertTrue(new_error_obj.context == error_obj.context) self.assertTrue(new_error_obj.isrecoverable == error_obj.isrecoverable) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1800_interval_var.py000066400000000000000000000160751414105416400225050ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 1800 - Module for testing interval variables """ import datetime import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def setUp(self): super().setUp() self.raw_data = [] self.data_by_key = {} for i in range(1, 11): delta = datetime.timedelta(days=i, hours=i, minutes=i * 2, seconds=i * 3) if i % 2 == 0: nullable_delta = None else: nullable_delta = datetime.timedelta(days=i + 5, hours=i + 2, minutes=i * 2 + 5, seconds=i * 3 + 5) data_tuple = (i, delta, nullable_delta) self.raw_data.append(data_tuple) self.data_by_key[i] = data_tuple def test_1800_bind_interval(self): "1800 - test binding in an interval" self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS) value = datetime.timedelta(days=5, hours=5, minutes=10, seconds=15) self.cursor.execute(""" select * from TestIntervals where IntervalCol = :value""", value=value) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]]) def test_1801_bind_null(self): "1801 - test binding in a null" self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS) self.cursor.execute(""" select * from TestIntervals where IntervalCol = :value""", value=None) self.assertEqual(self.cursor.fetchall(), []) def test_1802_bind_out_set_input_sizes(self): "1802 - test binding out with set input sizes defined" bind_vars = \ self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS) self.cursor.execute(""" begin :value := to_dsinterval('8 09:24:18.123789'); end;""") expected_value = datetime.timedelta(days=8, hours=9, minutes=24, seconds=18, microseconds=123789) self.assertEqual(bind_vars["value"].getvalue(), expected_value) def test_1803_bind_in_out_set_input_sizes(self): "1803 - test binding in/out with set input sizes defined" bind_vars = \ self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS) self.cursor.execute(""" begin :value := :value + to_dsinterval('5 08:30:00'); end;""", value=datetime.timedelta(days=5, hours=2, minutes=15)) expected_value = datetime.timedelta(days=10, hours=10, minutes=45) self.assertEqual(bind_vars["value"].getvalue(), expected_value) def test_1804_bind_in_out_fractional_second(self): "1804 - test binding in/out with set input sizes defined" bind_vars = \ self.cursor.setinputsizes(value=oracledb.DB_TYPE_INTERVAL_DS) self.cursor.execute(""" begin :value := :value + to_dsinterval('5 08:30:00'); end;""", value=datetime.timedelta(days=5, seconds=12.123789)) expected_value = datetime.timedelta(days=10, hours=8, minutes=30, seconds=12, microseconds=123789) self.assertEqual(bind_vars["value"].getvalue(), expected_value) def test_1805_bind_out_var(self): "1805 - test binding out with cursor.var() method" var = self.cursor.var(oracledb.DB_TYPE_INTERVAL_DS) self.cursor.execute(""" begin :value := to_dsinterval('15 18:35:45.586'); end;""", value=var) expected_value = datetime.timedelta(days=15, hours=18, minutes=35, seconds=45, milliseconds=586) self.assertEqual(var.getvalue(), expected_value) def test_1806_bind_in_out_var_direct_set(self): "1806 - test binding in/out with cursor.var() method" var = self.cursor.var(oracledb.DB_TYPE_INTERVAL_DS) var.setvalue(0, datetime.timedelta(days=1, minutes=50)) self.cursor.execute(""" begin :value := :value + to_dsinterval('8 05:15:00'); end;""", value=var) expected_value = datetime.timedelta(days=9, hours=6, minutes=5) self.assertEqual(var.getvalue(), expected_value) def test_1807_cursor_description(self): "1807 - test cursor description is accurate" self.cursor.execute("select * from TestIntervals") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('INTERVALCOL', oracledb.DB_TYPE_INTERVAL_DS, None, None, 2, 6, False), ('NULLABLECOL', oracledb.DB_TYPE_INTERVAL_DS, None, None, 2, 6, True) ] self.assertEqual(self.cursor.description, expected_value) def test_1808_fetchall(self): "1808 - test that fetching all of the data returns the correct results" self.cursor.execute("select * From TestIntervals order by IntCol") self.assertEqual(self.cursor.fetchall(), self.raw_data) self.assertEqual(self.cursor.fetchall(), []) def test_1809_fetchmany(self): "1809 - test that fetching data in chunks returns the correct results" self.cursor.execute("select * From TestIntervals order by IntCol") self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3]) self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5]) self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9]) self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:]) self.assertEqual(self.cursor.fetchmany(3), []) def test_1810_fetchone(self): "1810 - test that fetching a single row returns the correct results" self.cursor.execute(""" select * from TestIntervals where IntCol in (3, 4) order by IntCol""") self.assertEqual(self.cursor.fetchone(), self.data_by_key[3]) self.assertEqual(self.cursor.fetchone(), self.data_by_key[4]) self.assertEqual(self.cursor.fetchone(), None) def test_1811_bind_and_fetch_negative_interval(self): "1811 - test binding and fetching a negative interval" value = datetime.timedelta(days=-1, seconds=86314, microseconds=431152) self.cursor.execute("select :1 from dual", [value]) result, = self.cursor.fetchone() self.assertEqual(result, value) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_1900_lob_var.py000066400000000000000000000263771414105416400214440ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 1900 - Module for testing LOB (CLOB and BLOB) variables """ import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def __get_temp_lobs(self, sid): cursor = self.connection.cursor() cursor.execute(""" select cache_lobs + nocache_lobs + abstract_lobs from v$temporary_lobs where sid = :sid""", sid = sid) row = cursor.fetchone() if row is None: return 0 return int(row[0]) def __perform_test(self, lob_type, input_type): long_string = "" db_type = getattr(oracledb, "DB_TYPE_" + lob_type) self.cursor.execute("truncate table Test%ss" % lob_type) for i in range(0, 11): if i > 0: char = chr(ord('A') + i - 1) long_string += char * 25000 elif input_type is not db_type: continue self.cursor.setinputsizes(long_string=input_type) if lob_type == "BLOB": bind_value = long_string.encode() else: bind_value = long_string self.cursor.execute(""" insert into Test%ss ( IntCol, %sCol ) values ( :integer_value, :long_string )""" % (lob_type, lob_type), integer_value=i, long_string=bind_value) self.connection.commit() self.cursor.execute(""" select * from Test%ss order by IntCol""" % lob_type) self.__validate_query(self.cursor, lob_type) def __test_lob_operations(self, lob_type): self.cursor.execute("truncate table Test%ss" % lob_type) self.cursor.setinputsizes(long_string=getattr(oracledb, lob_type)) long_string = "X" * 75000 write_value = "TEST" if lob_type == "BLOB": long_string = long_string.encode("ascii") write_value = write_value.encode("ascii") self.cursor.execute(""" insert into Test%ss ( IntCol, %sCol ) values ( :integer_value, :long_string )""" % (lob_type, lob_type), integer_value=1, long_string=long_string) self.cursor.execute(""" select %sCol from Test%ss where IntCol = 1""" % (lob_type, lob_type)) lob, = self.cursor.fetchone() self.assertEqual(lob.isopen(), False) lob.open() self.assertEqual(lob.isopen(), True) lob.close() self.assertEqual(lob.isopen(), False) self.assertEqual(lob.size(), 75000) lob.write(write_value, 75001) self.assertEqual(lob.size(), 75000 + len(write_value)) self.assertEqual(lob.read(), long_string + write_value) lob.write(write_value, 1) self.assertEqual(lob.read(), write_value + long_string[4:] + write_value) lob.trim(25000) self.assertEqual(lob.size(), 25000) lob.trim() self.assertEqual(lob.size(), 0) def __test_temporary_lob(self, lob_type): self.cursor.execute("truncate table Test%ss" % lob_type) value = "A test string value" if lob_type == "BLOB": value = value.encode("ascii") db_type = getattr(oracledb, "DB_TYPE_" + lob_type) lob = self.connection.createlob(db_type) lob.write(value) self.cursor.execute(""" insert into Test%ss (IntCol, %sCol) values (:int_val, :lob_val)""" % (lob_type, lob_type), int_val=1, lob_val=lob) self.cursor.execute("select %sCol from Test%ss" % (lob_type, lob_type)) lob, = self.cursor.fetchone() self.assertEqual(lob.read(), value) def __validate_query(self, rows, lob_type): long_string = "" db_type = getattr(oracledb, "DB_TYPE_" + lob_type) for row in rows: integer_value, lob = row self.assertEqual(lob.type, db_type) if integer_value == 0: self.assertEqual(lob.size(), 0) expected_value = "" if lob_type == "BLOB": expected_value = expected_value.encode() self.assertEqual(lob.read(), expected_value) else: char = chr(ord('A') + integer_value - 1) prev_char = chr(ord('A') + integer_value - 2) long_string += char * 25000 if lob_type == "BLOB": expected_value = long_string.encode("ascii") char = char.encode("ascii") prev_char = prev_char.encode("ascii") else: expected_value = long_string self.assertEqual(lob.size(), len(expected_value)) self.assertEqual(lob.read(), expected_value) if lob_type == "CLOB": self.assertEqual(str(lob), expected_value) self.assertEqual(lob.read(len(expected_value)), char) if integer_value > 1: offset = (integer_value - 1) * 25000 - 4 string = prev_char * 5 + char * 5 self.assertEqual(lob.read(offset, 10), string) def test_1900_bind_lob_value(self): "1900 - test binding a LOB value directly" self.cursor.execute("truncate table TestCLOBs") self.cursor.execute("insert into TestCLOBs values (1, 'Short value')") self.cursor.execute("select ClobCol from TestCLOBs") lob, = self.cursor.fetchone() self.cursor.execute("insert into TestCLOBs values (2, :value)", value=lob) def test_1901_blob_cursor_description(self): "1901 - test cursor description is accurate for BLOBs" self.cursor.execute("select * from TestBLOBs") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0), ('BLOBCOL', oracledb.DB_TYPE_BLOB, None, None, None, None, 0) ] self.assertEqual(self.cursor.description, expected_value) def test_1902_blob_direct(self): "1902 - test binding and fetching BLOB data (directly)" self.__perform_test("BLOB", oracledb.DB_TYPE_BLOB) def test_1903_blob_indirect(self): "1903 - test binding and fetching BLOB data (indirectly)" self.__perform_test("BLOB", oracledb.DB_TYPE_LONG_RAW) def test_1904_blob_operations(self): "1904 - test operations on BLOBs" self.__test_lob_operations("BLOB") def test_1905_clob_cursor_description(self): "1905 - test cursor description is accurate for CLOBs" self.cursor.execute("select * from TestCLOBs") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('CLOBCOL', oracledb.DB_TYPE_CLOB, None, None, None, None, False) ] self.assertEqual(self.cursor.description, expected_value) def test_1906_clob_direct(self): "1906 - test binding and fetching CLOB data (directly)" self.__perform_test("CLOB", oracledb.DB_TYPE_CLOB) def test_1907_clob_indirect(self): "1907 - test binding and fetching CLOB data (indirectly)" self.__perform_test("CLOB", oracledb.DB_TYPE_LONG) def test_1908_clob_operations(self): "1908 - test operations on CLOBs" self.__test_lob_operations("CLOB") def test_1909_create_temp_blob(self): "1909 - test creating a temporary BLOB" self.__test_temporary_lob("BLOB") def test_1910_create_temp_clob(self): "1910 - test creating a temporary CLOB" self.__test_temporary_lob("CLOB") def test_1911_create_temp_nclob(self): "1911 - test creating a temporary NCLOB" self.__test_temporary_lob("NCLOB") def test_1912_multiple_fetch(self): "1912 - test retrieving data from a CLOB after multiple fetches" self.cursor.arraysize = 1 self.cursor.execute("select * from TestCLOBS") rows = self.cursor.fetchall() self.__validate_query(rows, "CLOB") def test_1913_nclob_cursor_description(self): "1913 - test cursor description is accurate for NCLOBs" self.cursor.execute("select * from TestNCLOBs") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, 0), ('NCLOBCOL', oracledb.DB_TYPE_NCLOB, None, None, None, None, 0) ] self.assertEqual(self.cursor.description, expected_value) def test_1914_nclob_direct(self): "1914 - test binding and fetching NCLOB data (directly)" self.__perform_test("NCLOB", oracledb.DB_TYPE_NCLOB) def test_1915_nclob_non_ascii_chars(self): "1915 - test binding and fetching NCLOB data (with non-ASCII chars)" value = "\u03b4\u4e2a" self.cursor.execute("truncate table TestNCLOBs") self.cursor.setinputsizes(val=oracledb.DB_TYPE_NVARCHAR) self.cursor.execute("insert into TestNCLOBs values (1, :val)", val=value) self.cursor.execute("select NCLOBCol from TestNCLOBs") nclob, = self.cursor.fetchone() self.cursor.setinputsizes(val=oracledb.DB_TYPE_NVARCHAR) self.cursor.execute("update TestNCLOBs set NCLOBCol = :val", val=nclob.read() + value) self.cursor.execute("select NCLOBCol from TestNCLOBs") nclob, = self.cursor.fetchone() self.assertEqual(nclob.read(), value + value) def test_1916_nclob_indirect(self): "1916 - test binding and fetching NCLOB data (indirectly)" self.__perform_test("NCLOB", oracledb.DB_TYPE_LONG) def test_1917_nclob_operations(self): "1917 - test operations on NCLOBs" self.__test_lob_operations("NCLOB") def test_1918_temporary_lobs(self): "1918 - test temporary LOBs" cursor = self.connection.cursor() cursor.arraysize = self.cursor.arraysize cursor.execute(""" select sys_context('USERENV', 'SID') from dual""") sid, = cursor.fetchone() temp_lobs = self.__get_temp_lobs(sid) self.assertEqual(temp_lobs, 0) cursor.execute(""" select extract(xmlcol, '/').getclobval() from TestXML""") for lob, in cursor: value = lob.read() del lob cursor.close() temp_lobs = self.__get_temp_lobs(sid) self.assertEqual(temp_lobs, 0) def test_1919_assign_string_beyond_array_size(self): "1919 - test assign string to NCLOB beyond array size" nclobVar = self.cursor.var(oracledb.DB_TYPE_NCLOB) self.assertRaises(IndexError, nclobVar.setvalue, 1, "test char") if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2000_long_var.py000066400000000000000000000105601414105416400216020ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 2000 - Module for testing long and long raw variables """ import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def __perform_test(self, typ): name_part = "Long" if typ is oracledb.DB_TYPE_LONG else "LongRaw" self.cursor.execute(f"truncate table Test{name_part}s") self.cursor.setinputsizes(long_string=typ) long_string = "" for i in range(1, 11): char = chr(ord('A') + i - 1) long_string += char * 25000 if i % 3 == 1: bind_value = None else: if typ is oracledb.DB_TYPE_LONG_RAW: bind_value = long_string.encode() else: bind_value = long_string self.cursor.execute(f""" insert into Test{name_part}s ( IntCol, {name_part}Col ) values ( :integer_value, :long_string )""", integer_value=i, long_string=bind_value) self.connection.commit() self.cursor.execute(""" select * from Test%ss order by IntCol""" % name_part) long_string = "" for integer_value, fetched_value in self.cursor: char = chr(ord('A') + integer_value - 1) long_string += char * 25000 if integer_value % 3 == 1: expected_value = None else: if typ is oracledb.DB_TYPE_LONG_RAW: expected_value = long_string.encode() else: expected_value = long_string if fetched_value is not None: self.assertEqual(len(fetched_value), integer_value * 25000) self.assertEqual(fetched_value, expected_value) def test_2000_longs(self): "2000 - test binding and fetching long data" self.__perform_test(oracledb.DB_TYPE_LONG) def test_2001_long_with_execute_many(self): "2001 - test binding long data with executemany()" data = [] self.cursor.execute("truncate table TestLongs") for i in range(5): char = chr(ord('A') + i) long_str = char * (32768 * (i + 1)) data.append((i + 1, long_str)) self.cursor.executemany("insert into TestLongs values (:1, :2)", data) self.connection.commit() self.cursor.execute("select * from TestLongs order by IntCol") fetched_data = self.cursor.fetchall() self.assertEqual(fetched_data, data) def test_2002_long_raws(self): "2002 - test binding and fetching long raw data" self.__perform_test(oracledb.DB_TYPE_LONG_RAW) def test_2003_long_cursor_description(self): "2003 - test cursor description is accurate for longs" self.cursor.execute("select * from TestLongs") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('LONGCOL', oracledb.DB_TYPE_LONG, None, None, None, None, True) ] self.assertEqual(self.cursor.description, expected_value) def test_2004_long_raw_cursor_description(self): "2004 - test cursor description is accurate for long raws" self.cursor.execute("select * from TestLongRaws") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('LONGRAWCOL', oracledb.DB_TYPE_LONG_RAW, None, None, None, None, True) ] self.assertEqual(self.cursor.description, expected_value) def test_2005_array_size_too_large(self): "2005 - test array size too large generates an exception" self.cursor.arraysize = 268435456 self.assertRaises(oracledb.DatabaseError, self.cursor.execute, "select * from TestLongRaws") if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2100_nchar_var.py000066400000000000000000000246021414105416400217410ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 2100 - Module for testing NCHAR variables """ import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def setUp(self): super().setUp() self.raw_data = [] self.data_by_key = {} for i in range(1, 11): unicode_col = "Unicode \u3042 %d" % i fixed_char_col = ("Fixed Unicode %d" % i).ljust(40) if i % 2: nullable_col = "Nullable %d" % i else: nullable_col = None data_tuple = (i, unicode_col, fixed_char_col, nullable_col) self.raw_data.append(data_tuple) self.data_by_key[i] = data_tuple def test_2100_unicode_length(self): "2100 - test value length" return_value = self.cursor.var(int) self.cursor.execute(""" begin :retval := LENGTH(:value); end;""", value="InVal \u3042", retval=return_value) self.assertEqual(return_value.getvalue(), 7) def test_2101_bind_unicode(self): "2101 - test binding in a unicode" self.cursor.setinputsizes(value=oracledb.DB_TYPE_NVARCHAR) self.cursor.execute(""" select * from TestUnicodes where UnicodeCol = :value""", value="Unicode \u3042 5") self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]]) def test_2102_bind_different_var(self): "2102 - test binding a different variable on second execution" retval_1 = self.cursor.var(oracledb.DB_TYPE_NVARCHAR, 30) retval_2 = self.cursor.var(oracledb.DB_TYPE_NVARCHAR, 30) self.cursor.execute(r"begin :retval := unistr('Called \3042'); end;", retval=retval_1) self.assertEqual(retval_1.getvalue(), "Called \u3042") self.cursor.execute("begin :retval := 'Called'; end;", retval=retval_2) self.assertEqual(retval_2.getvalue(), "Called") def test_2103_bind_unicode_after_number(self): "2103 - test binding in a string after setting input sizes to a number" unicode_val = self.cursor.var(oracledb.DB_TYPE_NVARCHAR) unicode_val.setvalue(0, "Unicode \u3042 6") self.cursor.setinputsizes(value=oracledb.NUMBER) self.cursor.execute(""" select * from TestUnicodes where UnicodeCol = :value""", value=unicode_val) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[6]]) def test_2104_bind_unicode_array_direct(self): "2104 - test binding in a unicode array" return_value = self.cursor.var(oracledb.NUMBER) array = [r[1] for r in self.raw_data] array_var = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, array) statement = """ begin :retval := pkg_TestUnicodeArrays.TestInArrays( :integer_value, :array); end;""" self.cursor.execute(statement, retval=return_value, integer_value=5, array=array_var) self.assertEqual(return_value.getvalue(), 116) array = ["Unicode - \u3042 %d" % i for i in range(15)] array_var = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, array) self.cursor.execute(statement, integer_value=8, array=array_var) self.assertEqual(return_value.getvalue(), 208) def test_2105_bind_unicode_array_by_sizes(self): "2105 - test binding in a unicode array (with setinputsizes)" return_value = self.cursor.var(oracledb.NUMBER) self.cursor.setinputsizes(array = [oracledb.DB_TYPE_NVARCHAR, 10]) array = [r[1] for r in self.raw_data] self.cursor.execute(""" begin :retval := pkg_TestUnicodeArrays.TestInArrays(:integer_value, :array); end;""", retval=return_value, integer_value=6, array=array) self.assertEqual(return_value.getvalue(), 117) def test_2106_bind_unicode_array_by_var(self): "2106 - test binding in a unicode array (with arrayvar)" return_value = self.cursor.var(oracledb.NUMBER) array = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, 10, 20) array.setvalue(0, [r[1] for r in self.raw_data]) self.cursor.execute(""" begin :retval := pkg_TestUnicodeArrays.TestInArrays(:integer_value, :array); end;""", retval=return_value, integer_value=7, array=array) self.assertEqual(return_value.getvalue(), 118) def test_2107_bind_in_out_unicode_array_by_var(self): "2107 - test binding in/out a unicode array (with arrayvar)" array = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, 10, 100) original_data = [r[1] for r in self.raw_data] fmt = "Converted element \u3042 # %d originally had length %d" expected_data = [fmt % (i, len(original_data[i - 1])) \ for i in range(1, 6)] + original_data[5:] array.setvalue(0, original_data) self.cursor.execute(""" begin pkg_TestUnicodeArrays.TestInOutArrays(:numElems, :array); end;""", numElems = 5, array = array) self.assertEqual(array.getvalue(), expected_data) def test_2108_bind_out_unicode_array_by_var(self): "2108 - test binding out a unicode array (with arrayvar)" array = self.cursor.arrayvar(oracledb.DB_TYPE_NVARCHAR, 6, 100) fmt = "Test out element \u3042 # %d" expected_data = [fmt % i for i in range(1, 7)] self.cursor.execute(""" begin pkg_TestUnicodeArrays.TestOutArrays(:numElems, :array); end;""", numElems = 6, array = array) self.assertEqual(array.getvalue(), expected_data) def test_2109_bind_null(self): "2109 - test binding in a null" self.cursor.execute(""" select * from TestUnicodes where UnicodeCol = :value""", value = None) self.assertEqual(self.cursor.fetchall(), []) def test_2110_bind_out_set_input_sizes_by_type(self): "2110 - test binding out with set input sizes defined (by type)" bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_NVARCHAR) self.cursor.execute(r""" begin :value := unistr('TSI \3042'); end;""") self.assertEqual(bind_vars["value"].getvalue(), "TSI \u3042") def test_2111_bind_in_out_set_input_sizes_by_type(self): "2111 - test binding in/out with set input sizes defined (by type)" bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_NVARCHAR) self.cursor.execute(r""" begin :value := :value || unistr(' TSI \3042'); end;""", value = "InVal \u3041") self.assertEqual(bind_vars["value"].getvalue(), "InVal \u3041 TSI \u3042") def test_2112_bind_out_var(self): "2112 - test binding out with cursor.var() method" var = self.cursor.var(oracledb.DB_TYPE_NVARCHAR) self.cursor.execute(r""" begin :value := unistr('TSI (VAR) \3042'); end;""", value=var) self.assertEqual(var.getvalue(), "TSI (VAR) \u3042") def test_2113_bind_in_out_var_direct_set(self): "2113 - test binding in/out with cursor.var() method" var = self.cursor.var(oracledb.DB_TYPE_NVARCHAR) var.setvalue(0, "InVal \u3041") self.cursor.execute(r""" begin :value := :value || unistr(' TSI (VAR) \3042'); end;""", value = var) self.assertEqual(var.getvalue(), "InVal \u3041 TSI (VAR) \u3042") def test_2114_cursor_description(self): "2114 - test cursor description is accurate" self.cursor.execute("select * from TestUnicodes") varchar_ratio, nvarchar_ratio = test_env.get_charset_ratios() expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('UNICODECOL', oracledb.DB_TYPE_NVARCHAR, 20, 20 * nvarchar_ratio, None, None, False), ('FIXEDUNICODECOL', oracledb.DB_TYPE_NCHAR, 40, 40 * nvarchar_ratio, None, None, False), ('NULLABLECOL', oracledb.DB_TYPE_NVARCHAR, 50, 50 * nvarchar_ratio, None, None, True) ] self.assertEqual(self.cursor.description, expected_value) def test_2115_fetchall(self): "2115 - test that fetching all of the data returns the correct results" self.cursor.execute("select * From TestUnicodes order by IntCol") self.assertEqual(self.cursor.fetchall(), self.raw_data) self.assertEqual(self.cursor.fetchall(), []) def test_2116_fetchmany(self): "2116 - test that fetching data in chunks returns the correct results" self.cursor.execute("select * From TestUnicodes order by IntCol") self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3]) self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5]) self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9]) self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:]) self.assertEqual(self.cursor.fetchmany(3), []) def test_2117_fetchone(self): "2117 - test that fetching a single row returns the correct results" self.cursor.execute(""" select * from TestUnicodes where IntCol in (3, 4) order by IntCol""") self.assertEqual(self.cursor.fetchone(), self.data_by_key[3]) self.assertEqual(self.cursor.fetchone(), self.data_by_key[4]) self.assertEqual(self.cursor.fetchone(), None) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2200_number_var.py000066400000000000000000000464561414105416400221520ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 2200 - Module for testing number variables """ import decimal import sys import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def output_type_handler_binary_int(self, cursor, name, default_type, size, precision, scale): return cursor.var(oracledb.DB_TYPE_BINARY_INTEGER, arraysize=cursor.arraysize) def output_type_handler_decimal(self, cursor, name, default_type, size, precision, scale): if default_type == oracledb.NUMBER: return cursor.var(str, 255, outconverter=decimal.Decimal, arraysize=cursor.arraysize) def setUp(self): super().setUp() self.raw_data = [] self.data_by_key = {} for i in range(1, 11): number_col = i + i * 0.25 float_col = i + i * 0.75 unconstrained_col = i ** 3 + i * 0.5 if i % 2: nullable_col = 143 ** i else: nullable_col = None data_tuple = (i, 38 ** i, number_col, float_col, unconstrained_col, nullable_col) self.raw_data.append(data_tuple) self.data_by_key[i] = data_tuple def test_2200_bind_boolean(self): "2200 - test binding in a boolean" result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str, (True,)) self.assertEqual(result, "TRUE") def test_2201_bind_boolean_as_number(self): "2201 - test binding in a boolean as a number" var = self.cursor.var(oracledb.NUMBER) var.setvalue(0, True) self.cursor.execute("select :1 from dual", [var]) result, = self.cursor.fetchone() self.assertEqual(result, 1) var.setvalue(0, False) self.cursor.execute("select :1 from dual", [var]) result, = self.cursor.fetchone() self.assertEqual(result, 0) def test_2202_bind_decimal(self): "2202 - test binding in a decimal.Decimal" self.cursor.execute(""" select * from TestNumbers where NumberCol - :value1 - :value2 = trunc(NumberCol)""", value1=decimal.Decimal("0.20"), value2=decimal.Decimal("0.05")) expected_data = [self.data_by_key[1], self.data_by_key[5], self.data_by_key[9]] self.assertEqual(self.cursor.fetchall(), expected_data) def test_2203_bind_float(self): "2203 - test binding in a float" self.cursor.execute(""" select * from TestNumbers where NumberCol - :value = trunc(NumberCol)""", value=0.25) expected_data = [self.data_by_key[1], self.data_by_key[5], self.data_by_key[9]] self.assertEqual(self.cursor.fetchall(), expected_data) def test_2204_bind_integer(self): "2204 - test binding in an integer" self.cursor.execute(""" select * from TestNumbers where IntCol = :value""", value = 2) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[2]]) def test_2205_bind_large_long_as_oracle_number(self): "2205 - test binding in a large long integer as Oracle number" in_val = 6088343244 value_var = self.cursor.var(oracledb.NUMBER) value_var.setvalue(0, in_val) self.cursor.execute(""" begin :value := :value + 5; end;""", value=value_var) value = value_var.getvalue() self.assertEqual(value, in_val + 5) def test_2206_bind_large_long_as_integer(self): "2206 - test binding in a large long integer as Python integer" long_value = -9999999999999999999 self.cursor.execute("select :value from dual", value=long_value) result, = self.cursor.fetchone() self.assertEqual(result, long_value) def test_2207_bind_integer_after_string(self): "2207 - test binding in an integer after setting input sizes to string" self.cursor.setinputsizes(value=15) self.cursor.execute(""" select * from TestNumbers where IntCol = :value""", value=3) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[3]]) def test_2208_bind_decimal_after_number(self): "2208 - test binding in a decimal after setting input sizes to number" cursor = self.connection.cursor() value = decimal.Decimal("319438950232418390.273596") cursor.setinputsizes(value=oracledb.NUMBER) cursor.outputtypehandler = self.output_type_handler_decimal cursor.execute("select :value from dual", value=value) out_value, = cursor.fetchone() self.assertEqual(out_value, value) def test_2209_bind_null(self): "2209 - test binding in a null" self.cursor.execute(""" select * from TestNumbers where IntCol = :value""", value=None) self.assertEqual(self.cursor.fetchall(), []) def test_2210_bind_number_array_direct(self): "2210 - test binding in a number array" return_value = self.cursor.var(oracledb.NUMBER) array = [r[2] for r in self.raw_data] statement = """ begin :return_value := pkg_TestNumberArrays.TestInArrays( :start_value, :array); end;""" self.cursor.execute(statement, return_value=return_value, start_value=5, array=array) self.assertEqual(return_value.getvalue(), 73.75) array = list(range(15)) self.cursor.execute(statement, start_value=10, array=array) self.assertEqual(return_value.getvalue(), 115.0) def test_2211_bind_number_array_by_sizes(self): "2211 - test binding in a number array (with setinputsizes)" return_value = self.cursor.var(oracledb.NUMBER) self.cursor.setinputsizes(array = [oracledb.NUMBER, 10]) array = [r[2] for r in self.raw_data] self.cursor.execute(""" begin :return_value := pkg_TestNumberArrays.TestInArrays( :start_value, :array); end;""", return_value=return_value, start_value=6, array=array) self.assertEqual(return_value.getvalue(), 74.75) def test_2212_bind_number_array_by_var(self): "2212 - test binding in a number array (with arrayvar)" return_value = self.cursor.var(oracledb.NUMBER) array = self.cursor.arrayvar(oracledb.NUMBER, [r[2] for r in self.raw_data]) self.cursor.execute(""" begin :return_value := pkg_TestNumberArrays.TestInArrays( :integer_value, :array); end;""", return_value = return_value, integer_value = 7, array = array) self.assertEqual(return_value.getvalue(), 75.75) def test_2213_bind_zero_length_number_array_by_var(self): "2213 - test binding in a zero length number array (with arrayvar)" return_value = self.cursor.var(oracledb.NUMBER) array = self.cursor.arrayvar(oracledb.NUMBER, 0) self.cursor.execute(""" begin :return_value := pkg_TestNumberArrays.TestInArrays( :integer_value, :array); end;""", return_value=return_value, integer_value=8, array=array) self.assertEqual(return_value.getvalue(), 8.0) self.assertEqual(array.getvalue(), []) def test_2214_bind_in_out_number_array_by_var(self): "2214 - test binding in/out a number array (with arrayvar)" array = self.cursor.arrayvar(oracledb.NUMBER, 10) original_data = [r[2] for r in self.raw_data] expected_data = [original_data[i - 1] * 10 for i in range(1, 6)] + \ original_data[5:] array.setvalue(0, original_data) self.cursor.execute(""" begin pkg_TestNumberArrays.TestInOutArrays(:num_elems, :array); end;""", num_elems=5, array=array) self.assertEqual(array.getvalue(), expected_data) def test_2215_bind_out_number_array_by_var(self): "2215 - test binding out a Number array (with arrayvar)" array = self.cursor.arrayvar(oracledb.NUMBER, 6) expected_data = [i * 100 for i in range(1, 7)] self.cursor.execute(""" begin pkg_TestNumberArrays.TestOutArrays(:num_elems, :array); end;""", num_elems=6, array=array) self.assertEqual(array.getvalue(), expected_data) def test_2216_bind_out_set_input_sizes(self): "2216 - test binding out with set input sizes defined" bind_vars = self.cursor.setinputsizes(value = oracledb.NUMBER) self.cursor.execute(""" begin :value := 5; end;""") self.assertEqual(bind_vars["value"].getvalue(), 5) def test_2217_bind_in_out_set_input_sizes(self): "2217 - test binding in/out with set input sizes defined" bind_vars = self.cursor.setinputsizes(value = oracledb.NUMBER) self.cursor.execute(""" begin :value := :value + 5; end;""", value = 1.25) self.assertEqual(bind_vars["value"].getvalue(), 6.25) def test_2218_bind_out_var(self): "2218 - test binding out with cursor.var() method" var = self.cursor.var(oracledb.NUMBER) self.cursor.execute(""" begin :value := 5; end;""", value = var) self.assertEqual(var.getvalue(), 5) def test_2219_bind_in_out_var_direct_set(self): "2219 - test binding in/out with cursor.var() method" var = self.cursor.var(oracledb.NUMBER) var.setvalue(0, 2.25) self.cursor.execute(""" begin :value := :value + 5; end;""", value = var) self.assertEqual(var.getvalue(), 7.25) def test_2220_cursor_description(self): "2220 - test cursor description is accurate" self.cursor.execute("select * from TestNumbers") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('LONGINTCOL', oracledb.DB_TYPE_NUMBER, 17, None, 16, 0, False), ('NUMBERCOL', oracledb.DB_TYPE_NUMBER, 13, None, 9, 2, False), ('FLOATCOL', oracledb.DB_TYPE_NUMBER, 127, None, 126, -127, False), ('UNCONSTRAINEDCOL', oracledb.DB_TYPE_NUMBER, 127, None, 0, -127, False), ('NULLABLECOL', oracledb.DB_TYPE_NUMBER, 39, None, 38, 0, True) ] self.assertEqual(self.cursor.description, expected_value) def test_2221_fetchall(self): "2221 - test that fetching all of the data returns the correct results" self.cursor.execute("select * From TestNumbers order by IntCol") self.assertEqual(self.cursor.fetchall(), self.raw_data) self.assertEqual(self.cursor.fetchall(), []) def test_2222_fetchmany(self): "2222 - test that fetching data in chunks returns the correct results" self.cursor.execute("select * From TestNumbers order by IntCol") self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3]) self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5]) self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9]) self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:]) self.assertEqual(self.cursor.fetchmany(3), []) def test_2223_fetchone(self): "2223 - test that fetching a single row returns the correct results" self.cursor.execute(""" select * from TestNumbers where IntCol in (3, 4) order by IntCol""") self.assertEqual(self.cursor.fetchone(), self.data_by_key[3]) self.assertEqual(self.cursor.fetchone(), self.data_by_key[4]) self.assertEqual(self.cursor.fetchone(), None) def test_2224_return_as_long(self): "2224 - test that fetching a long integer returns such in Python" self.cursor.execute(""" select NullableCol from TestNumbers where IntCol = 9""") col, = self.cursor.fetchone() self.assertEqual(col, 25004854810776297743) def test_2225_return_constant_float(self): "2225 - test fetching a floating point number returns such in Python" self.cursor.execute("select 1.25 from dual") result, = self.cursor.fetchone() self.assertEqual(result, 1.25) def test_2226_return_constant_integer(self): "2226 - test that fetching an integer returns such in Python" self.cursor.execute("select 148 from dual") result, = self.cursor.fetchone() self.assertEqual(result, 148) self.assertTrue(isinstance(result, int), "integer not returned") def test_2227_acceptable_boundary_numbers(self): "2227 - test that acceptable boundary numbers are handled properly" in_values = [decimal.Decimal("9.99999999999999e+125"), decimal.Decimal("-9.99999999999999e+125"), 0.0, 1e-130, -1e-130] out_values = [int("9" * 15 + "0" * 111), -int("9" * 15 + "0" * 111), 0, 1e-130, -1e-130] for in_value, out_value in zip(in_values, out_values): self.cursor.execute("select :1 from dual", (in_value,)) result, = self.cursor.fetchone() self.assertEqual(result, out_value) def test_2228_unacceptable_boundary_numbers(self): "2228 - test that unacceptable boundary numbers are rejected" in_values = [1e126, -1e126, float("inf"), float("-inf"), float("NaN"), decimal.Decimal("1e126"), decimal.Decimal("-1e126"), decimal.Decimal("inf"), decimal.Decimal("-inf"), decimal.Decimal("NaN")] no_rep_err = "value cannot be represented as an Oracle number" invalid_err = "invalid number" expected_errors = [no_rep_err, no_rep_err, invalid_err, invalid_err, invalid_err, no_rep_err, no_rep_err, invalid_err, invalid_err, invalid_err] for in_value, error in zip(in_values, expected_errors): self.assertRaisesRegex(oracledb.DatabaseError, error, self.cursor.execute, "select :1 from dual", (in_value,)) def test_2229_return_float_from_division(self): "2229 - test that fetching the result of division returns a float" self.cursor.execute(""" select IntCol / 7 from TestNumbers where IntCol = 1""") result, = self.cursor.fetchone() self.assertEqual(result, 1.0 / 7.0) self.assertTrue(isinstance(result, float), "float not returned") def test_2230_string_format(self): "2230 - test that string format is returned properly" var = self.cursor.var(oracledb.NUMBER) self.assertEqual(str(var), "") var.setvalue(0, 4) self.assertEqual(str(var), "") def test_2231_bind_binary_double(self): "2231 - test that binding binary double is possible" statement = "select :1 from dual" self.cursor.setinputsizes(oracledb.DB_TYPE_BINARY_DOUBLE) self.cursor.execute(statement, (5,)) self.assertEqual(self.cursor.bindvars[0].type, oracledb.DB_TYPE_BINARY_DOUBLE) value, = self.cursor.fetchone() self.assertEqual(value, 5) self.cursor.execute(statement, (1.5,)) self.assertEqual(self.cursor.bindvars[0].type, oracledb.DB_TYPE_BINARY_DOUBLE) value, = self.cursor.fetchone() self.assertEqual(value, 1.5) self.cursor.execute(statement, (decimal.Decimal("NaN"),)) self.assertEqual(self.cursor.bindvars[0].type, oracledb.DB_TYPE_BINARY_DOUBLE) value, = self.cursor.fetchone() self.assertEqual(str(value), str(float("NaN"))) def test_2232_fetch_binary_int(self): "2232 - test fetching numbers as binary integers" self.cursor.outputtypehandler = self.output_type_handler_binary_int for value in (1, 2 ** 31, 2 ** 63 - 1, -1, -2 ** 31, -2 ** 63 + 1): self.cursor.execute("select :1 from dual", [str(value)]) fetched_value, = self.cursor.fetchone() self.assertEqual(value, fetched_value) def test_2233_out_bind_binary_int(self): "2233 - test binding native integer as an out bind" statement = "begin :value := 2.9; end;" simple_var = self.cursor.var(oracledb.DB_TYPE_BINARY_INTEGER) self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), 2) statement = "begin :value := 1.5; end;" simple_var = self.cursor.var(oracledb.DB_TYPE_BINARY_INTEGER) self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), 1) def test_2234_in_bind_binary_int(self): "2234 - test binding in a native integer" statement = "begin :value := :value + 2.5; end;" simple_var = self.cursor.var(oracledb.DB_TYPE_BINARY_INTEGER) simple_var.setvalue(0, 0) self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), 2) simple_var.setvalue(0, -5) self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), -2) def test_2235_setting_decimal_value_binary_int(self): "2235 - test setting decimal value for binary int" statement = "begin :value := :value + 2.5; end;" simple_var = self.cursor.var(oracledb.DB_TYPE_BINARY_INTEGER) simple_var.setvalue(0, 2.5) self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), 4) def test_2236_out_bind_binary_int_with_large_value(self): "2236 - bind a large value to binary int" statement = "begin :value := POWER(2, 31) - 1; end;" simple_var = self.cursor.var(oracledb.DB_TYPE_BINARY_INTEGER) self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), 2**31 - 1) statement = "begin :value := POWER(-2, 31) - 1; end;" self.cursor.execute(statement, [simple_var]) self.assertEqual(simple_var.getvalue(), -2**31 - 1) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2300_object_var.py000066400000000000000000000441461414105416400221230ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 2300 - Module for testing object variables """ import datetime import decimal import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def __test_data(self, expected_int_value, expected_obj_value, expected_array_value): int_value, object_value, array_value = self.cursor.fetchone() if object_value is not None: object_value = self.get_db_object_as_plain_object(object_value) if array_value is not None: array_value = array_value.aslist() self.assertEqual(int_value, expected_int_value) self.assertEqual(object_value, expected_obj_value) self.assertEqual(array_value, expected_array_value) def test_2300_bind_null_in(self): "2300 - test binding a null value (IN)" var = self.cursor.var(oracledb.DB_TYPE_OBJECT, typename="UDT_OBJECT") result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str, (var,)) self.assertEqual(result, "null") def test_2301_bind_object_in(self): "2301 - test binding an object (IN)" type_obj = self.connection.gettype("UDT_OBJECT") obj = type_obj.newobject() obj.NUMBERVALUE = 13 obj.STRINGVALUE = "Test String" result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str, (obj,)) exp = "udt_Object(13, 'Test String', null, null, null, null, null)" self.assertEqual(result, exp) obj.NUMBERVALUE = None obj.STRINGVALUE = "Test With Dates" obj.DATEVALUE = datetime.datetime(2016, 2, 10) obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 10, 14, 13, 50) result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str, (obj,)) self.assertEqual(result, "udt_Object(null, 'Test With Dates', null, " \ "to_date('2016-02-10', 'YYYY-MM-DD'), " \ "to_timestamp('2016-02-10 14:13:50', " \ "'YYYY-MM-DD HH24:MI:SS'), " \ "null, null)") obj.DATEVALUE = None obj.TIMESTAMPVALUE = None sub_type_obj = self.connection.gettype("UDT_SUBOBJECT") sub_obj = sub_type_obj.newobject() sub_obj.SUBNUMBERVALUE = decimal.Decimal("18.25") sub_obj.SUBSTRINGVALUE = "Sub String" obj.SUBOBJECTVALUE = sub_obj result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str, (obj,)) self.assertEqual(result, "udt_Object(null, 'Test With Dates', null, null, " \ "null, udt_SubObject(18.25, 'Sub String'), null)") def test_2302_copy_object(self): "2302 - test copying an object" type_obj = self.connection.gettype("UDT_OBJECT") obj = type_obj() obj.NUMBERVALUE = 5124 obj.STRINGVALUE = "A test string" obj.DATEVALUE = datetime.datetime(2016, 2, 24) obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 24, 13, 39, 10) copied_obj = obj.copy() self.assertEqual(obj.NUMBERVALUE, copied_obj.NUMBERVALUE) self.assertEqual(obj.STRINGVALUE, copied_obj.STRINGVALUE) self.assertEqual(obj.DATEVALUE, copied_obj.DATEVALUE) self.assertEqual(obj.TIMESTAMPVALUE, copied_obj.TIMESTAMPVALUE) def test_2303_empty_collection_as_list(self): "2303 - test getting an empty collection as a list" type_obj = self.connection.gettype("UDT_ARRAY") obj = type_obj.newobject() self.assertEqual(obj.aslist(), []) def test_2304_fetch_data(self): "2304 - test fetching objects" self.cursor.execute("alter session set time_zone = 'UTC'") self.cursor.execute(""" select IntCol, ObjectCol, ArrayCol from TestObjects order by IntCol""") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('OBJECTCOL', oracledb.DB_TYPE_OBJECT, None, None, None, None, True), ('ARRAYCOL', oracledb.DB_TYPE_OBJECT, None, None, None, None, True) ] self.assertEqual(self.cursor.description, expected_value) expected_value = ( 1, 'First row', 'First ', 'N First Row', 'N First ', b'Raw Data 1', 2, 5, 12.125, 0.5, 12.5, 25.25, 50.125, oracledb.Timestamp(2007, 3, 6, 0, 0, 0), oracledb.Timestamp(2008, 9, 12, 16, 40), oracledb.Timestamp(2009, 10, 13, 17, 50), oracledb.Timestamp(2010, 11, 14, 18, 55), 'Short CLOB value', 'Short NCLOB Value', b'Short BLOB value', (11, 'Sub object 1'), [(5, 'first element'), (6, 'second element')] ) self.__test_data(1, expected_value, [5, 10, None, 20]) self.__test_data(2, None, [3, None, 9, 12, 15]) expected_value = ( 3, 'Third row', 'Third ', 'N Third Row', 'N Third ', b'Raw Data 3', 4, 10, 6.5, 0.75, 43.25, 86.5, 192.125, oracledb.Timestamp(2007, 6, 21, 0, 0, 0), oracledb.Timestamp(2007, 12, 13, 7, 30, 45), oracledb.Timestamp(2017, 6, 21, 23, 18, 45), oracledb.Timestamp(2017, 7, 21, 8, 27, 13), 'Another short CLOB value', 'Another short NCLOB Value', b'Yet another short BLOB value', (13, 'Sub object 3'), [ (10, 'element #1'), (20, 'element #2'), (30, 'element #3'), (40, 'element #4') ] ) self.__test_data(3, expected_value, None) def test_2305_get_object_type(self): "2305 - test getting object type" type_obj = self.connection.gettype("UDT_OBJECT") self.assertEqual(type_obj.iscollection, False) self.assertEqual(type_obj.schema, self.connection.username.upper()) self.assertEqual(type_obj.name, "UDT_OBJECT") sub_object_value_type = self.connection.gettype("UDT_SUBOBJECT") sub_object_array_type = self.connection.gettype("UDT_OBJECTARRAY") expected_attr_names = [ "NUMBERVALUE", "STRINGVALUE", "FIXEDCHARVALUE", "NSTRINGVALUE", "NFIXEDCHARVALUE", "RAWVALUE", "INTVALUE", "SMALLINTVALUE", "REALVALUE", "DOUBLEPRECISIONVALUE", "FLOATVALUE", "BINARYFLOATVALUE", "BINARYDOUBLEVALUE", "DATEVALUE", "TIMESTAMPVALUE", "TIMESTAMPTZVALUE", "TIMESTAMPLTZVALUE", "CLOBVALUE", "NCLOBVALUE", "BLOBVALUE", "SUBOBJECTVALUE", "SUBOBJECTARRAY" ] actual_attr_names = [a.name for a in type_obj.attributes] self.assertEqual(actual_attr_names, expected_attr_names) expected_attr_types = [ oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_CHAR, oracledb.DB_TYPE_NVARCHAR, oracledb.DB_TYPE_NCHAR, oracledb.DB_TYPE_RAW, oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_BINARY_FLOAT, oracledb.DB_TYPE_BINARY_DOUBLE, oracledb.DB_TYPE_DATE, oracledb.DB_TYPE_TIMESTAMP, oracledb.DB_TYPE_TIMESTAMP_TZ, oracledb.DB_TYPE_TIMESTAMP_LTZ, oracledb.DB_TYPE_CLOB, oracledb.DB_TYPE_NCLOB, oracledb.DB_TYPE_BLOB, sub_object_value_type, sub_object_array_type ] actual_attr_types = [a.type for a in type_obj.attributes] self.assertEqual(actual_attr_types, expected_attr_types) self.assertEqual(sub_object_array_type.iscollection, True) self.assertEqual(sub_object_array_type.attributes, []) def test_2306_object_type(self): "2306 - test object type data" self.cursor.execute(""" select ObjectCol from TestObjects where ObjectCol is not null and rownum <= 1""") obj, = self.cursor.fetchone() self.assertEqual(obj.type.schema, self.connection.username.upper()) self.assertEqual(obj.type.name, "UDT_OBJECT") self.assertEqual(obj.type.attributes[0].name, "NUMBERVALUE") def test_2307_round_trip_object(self): "2307 - test inserting and then querying object with all data types" self.cursor.execute("alter session set time_zone = 'UTC'") self.cursor.execute("truncate table TestClobs") self.cursor.execute("truncate table TestNClobs") self.cursor.execute("truncate table TestBlobs") self.cursor.execute("insert into TestClobs values " \ "(1, 'A short CLOB')") self.cursor.execute("insert into TestNClobs values " \ "(1, 'A short NCLOB')") self.cursor.execute("insert into TestBlobs values " \ "(1, utl_raw.cast_to_raw('A short BLOB'))") self.connection.commit() self.cursor.execute("select CLOBCol from TestClobs") clob, = self.cursor.fetchone() self.cursor.execute("select NCLOBCol from TestNClobs") nclob, = self.cursor.fetchone() self.cursor.execute("select BLOBCol from TestBlobs") blob, = self.cursor.fetchone() type_obj = self.connection.gettype("UDT_OBJECT") obj = type_obj.newobject() obj.NUMBERVALUE = 5 obj.STRINGVALUE = "A string" obj.FIXEDCHARVALUE = "Fixed str" obj.NSTRINGVALUE = "A NCHAR string" obj.NFIXEDCHARVALUE = "Fixed N" obj.RAWVALUE = b"Raw Value" obj.INTVALUE = 27 obj.SMALLINTVALUE = 13 obj.REALVALUE = 184.875 obj.DOUBLEPRECISIONVALUE = 1.375 obj.FLOATVALUE = 23.75 obj.DATEVALUE = datetime.date(2017, 5, 9) obj.TIMESTAMPVALUE = datetime.datetime(2017, 5, 9, 9, 41, 13) obj.TIMESTAMPTZVALUE = datetime.datetime(1986, 8, 2, 15, 27, 38) obj.TIMESTAMPLTZVALUE = datetime.datetime(1999, 11, 12, 23, 5, 2) obj.BINARYFLOATVALUE = 14.25 obj.BINARYDOUBLEVALUE = 29.1625 obj.CLOBVALUE = clob obj.NCLOBVALUE = nclob obj.BLOBVALUE = blob sub_type_obj = self.connection.gettype("UDT_SUBOBJECT") sub_obj = sub_type_obj.newobject() sub_obj.SUBNUMBERVALUE = 23 sub_obj.SUBSTRINGVALUE = "Substring value" obj.SUBOBJECTVALUE = sub_obj self.cursor.execute("insert into TestObjects (IntCol, ObjectCol) " \ "values (4, :obj)", obj = obj) self.cursor.execute(""" select IntCol, ObjectCol, ArrayCol from TestObjects where IntCol = 4""") expected_value = ( 5, 'A string', 'Fixed str ', 'A NCHAR string', 'Fixed N ', b'Raw Value', 27, 13, 184.875, 1.375, 23.75, 14.25, 29.1625, oracledb.Timestamp(2017, 5, 9, 0, 0, 0), oracledb.Timestamp(2017, 5, 9, 9, 41, 13), oracledb.Timestamp(1986, 8, 2, 15, 27, 38), oracledb.Timestamp(1999, 11, 12, 23, 5, 2), 'A short CLOB', 'A short NCLOB', b'A short BLOB', (23, 'Substring value'), None ) self.__test_data(4, expected_value, None) obj.CLOBVALUE = "A short CLOB (modified)" obj.NCLOBVALUE = "A short NCLOB (modified)" obj.BLOBVALUE = "A short BLOB (modified)" self.cursor.execute("insert into TestObjects (IntCol, ObjectCol) " \ "values (5, :obj)", obj = obj) self.cursor.execute(""" select IntCol, ObjectCol, ArrayCol from TestObjects where IntCol = 5""") expected_value = ( 5, 'A string', 'Fixed str ', 'A NCHAR string', 'Fixed N ', b'Raw Value', 27, 13, 184.875, 1.375, 23.75, 14.25, 29.1625, oracledb.Timestamp(2017, 5, 9, 0, 0, 0), oracledb.Timestamp(2017, 5, 9, 9, 41, 13), oracledb.Timestamp(1986, 8, 2, 15, 27, 38), oracledb.Timestamp(1999, 11, 12, 23, 5, 2), 'A short CLOB (modified)', 'A short NCLOB (modified)', b'A short BLOB (modified)', (23, 'Substring value'), None ) self.__test_data(5, expected_value, None) self.connection.rollback() def test_2308_invalid_type_object(self): "2308 - test trying to find an object type that does not exist" self.assertRaises(oracledb.DatabaseError, self.connection.gettype, "A TYPE THAT DOES NOT EXIST") def test_2309_appending_wrong_object_type(self): "2309 - test appending an object of the wrong type to a collection" collection_obj_type = self.connection.gettype("UDT_OBJECTARRAY") collection_obj = collection_obj_type.newobject() array_obj_type = self.connection.gettype("UDT_ARRAY") array_obj = array_obj_type.newobject() self.assertRaises(oracledb.DatabaseError, collection_obj.append, array_obj) def test_2310_referencing_sub_obj(self): "2310 - test that referencing a sub object affects the parent object" obj_type = self.connection.gettype("UDT_OBJECT") sub_obj_type = self.connection.gettype("UDT_SUBOBJECT") obj = obj_type.newobject() obj.SUBOBJECTVALUE = sub_obj_type.newobject() obj.SUBOBJECTVALUE.SUBNUMBERVALUE = 5 obj.SUBOBJECTVALUE.SUBSTRINGVALUE = "Substring" self.assertEqual(obj.SUBOBJECTVALUE.SUBNUMBERVALUE, 5) self.assertEqual(obj.SUBOBJECTVALUE.SUBSTRINGVALUE, "Substring") def test_2311_access_sub_object_parent_object_destroyed(self): "2311 - test accessing sub object after parent object destroyed" obj_type = self.connection.gettype("UDT_OBJECT") sub_obj_type = self.connection.gettype("UDT_SUBOBJECT") array_type = self.connection.gettype("UDT_OBJECTARRAY") sub_obj1 = sub_obj_type.newobject() sub_obj1.SUBNUMBERVALUE = 2 sub_obj1.SUBSTRINGVALUE = "AB" sub_obj2 = sub_obj_type.newobject() sub_obj2.SUBNUMBERVALUE = 3 sub_obj2.SUBSTRINGVALUE = "CDE" obj = obj_type.newobject() obj.SUBOBJECTARRAY = array_type.newobject([sub_obj1, sub_obj2]) sub_obj_array = obj.SUBOBJECTARRAY del obj self.assertEqual(self.get_db_object_as_plain_object(sub_obj_array), [(2, "AB"), (3, "CDE")]) def test_2312_setting_attr_wrong_object_type(self): "2312 - test assigning an object of wrong type to an object attribute" obj_type = self.connection.gettype("UDT_OBJECT") obj = obj_type.newobject() wrong_obj_type = self.connection.gettype("UDT_OBJECTARRAY") wrong_obj = wrong_obj_type.newobject() self.assertRaises(oracledb.DatabaseError, setattr, obj, "SUBOBJECTVALUE", wrong_obj) def test_2313_setting_var_wrong_object_type(self): "2313 - test setting value of object variable to wrong object type" wrong_obj_type = self.connection.gettype("UDT_OBJECTARRAY") wrong_obj = wrong_obj_type.newobject() var = self.cursor.var(oracledb.DB_TYPE_OBJECT, typename="UDT_OBJECT") self.assertRaises(oracledb.DatabaseError, var.setvalue, 0, wrong_obj) def test_2314_string_format(self): "2314 - test object string format" obj_type = self.connection.gettype("UDT_OBJECT") user = test_env.get_main_user() self.assertEqual(str(obj_type), "" % user.upper()) self.assertEqual(str(obj_type.attributes[0]), "") def test_2315_trim_collection_list(self): "2315 - test Trim number of elements from collection" sub_obj_type = self.connection.gettype("UDT_SUBOBJECT") array_type = self.connection.gettype("UDT_OBJECTARRAY") data = [(1, "AB"), (2, "CDE"), (3, "FGH"), (4, "IJK")] array_obj = array_type() for num_val, str_val in data: subObj = sub_obj_type() subObj.SUBNUMBERVALUE = num_val subObj.SUBSTRINGVALUE = str_val array_obj.append(subObj) self.assertEqual(self.get_db_object_as_plain_object(array_obj), data) array_obj.trim(2) self.assertEqual(self.get_db_object_as_plain_object(array_obj), data[:2]) array_obj.trim(1) self.assertEqual(self.get_db_object_as_plain_object(array_obj), data[:1]) array_obj.trim(0) self.assertEqual(self.get_db_object_as_plain_object(array_obj), data[:1]) array_obj.trim(1) self.assertEqual(self.get_db_object_as_plain_object(array_obj), []) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2400_session_pool.py000066400000000000000000000626611414105416400225240ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 2400 - Module for testing session pools """ import threading import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): require_connection = False def __connect_and_drop(self): with self.pool.acquire() as connection: cursor = connection.cursor() cursor.execute("select count(*) from TestNumbers") count, = cursor.fetchone() self.assertEqual(count, 10) def __connect_and_generate_error(self): with self.pool.acquire() as connection: cursor = connection.cursor() self.assertRaises(oracledb.DatabaseError, cursor.execute, "select 1 / 0 from dual") def __callable_session_callback(self, conn, requested_tag): self.session_called = True supported_formats = { "SIMPLE" : "'YYYY-MM-DD HH24:MI'", "FULL" : "'YYYY-MM-DD HH24:MI:SS'" } supported_time_zones = { "UTC" : "'UTC'", "MST" : "'-07:00'" } supported_keys = { "NLS_DATE_FORMAT" : supported_formats, "TIME_ZONE" : supported_time_zones } if requested_tag is not None: state_parts = [] for directive in requested_tag.split(";"): parts = directive.split("=") if len(parts) != 2: raise ValueError("Tag must contain key=value pairs") key, value = parts value_dict = supported_keys.get(key) if value_dict is None: raise ValueError("Tag only supports keys: %s" % \ (", ".join(supported_keys))) actual_value = value_dict.get(value) if actual_value is None: raise ValueError("Key %s only supports values: %s" % \ (key, ", ".join(value_dict))) state_parts.append("%s = %s" % (key, actual_value)) sql = "alter session set %s" % " ".join(state_parts) cursor = conn.cursor() cursor.execute(sql) conn.tag = requested_tag def __perform_reconfigure_test(self, parameter_name, parameter_value, min=3, max=30, increment=4, timeout=5, wait_timeout=5000, stmtcachesize=25, max_lifetime_session=1000, max_sessions_per_shard=3, ping_interval=30, getmode=oracledb.SPOOL_ATTRVAL_WAIT): creation_args = dict(min=min, max=max, increment=increment, timeout=timeout, wait_timeout=wait_timeout, stmtcachesize=stmtcachesize, max_lifetime_session=max_lifetime_session, max_sessions_per_shard=max_sessions_per_shard, ping_interval=ping_interval, getmode=getmode) reconfigure_args = {} reconfigure_args[parameter_name] = parameter_value pool = test_env.get_pool(**creation_args) connection = pool.acquire() pool.reconfigure(**reconfigure_args) actual_args = dict(min=pool.min, max=pool.max, increment=pool.increment, timeout=pool.timeout, wait_timeout=pool.wait_timeout, stmtcachesize=pool.stmtcachesize, max_lifetime_session=pool.max_lifetime_session, max_sessions_per_shard=pool.max_sessions_per_shard, ping_interval=pool.ping_interval, getmode=pool.getmode) expected_args = creation_args.copy() expected_args.update(reconfigure_args) self.assertEqual(actual_args, expected_args) def __verify_connection(self, connection, expected_user, expected_proxy_user=None): cursor = connection.cursor() cursor.execute(""" select sys_context('userenv', 'session_user'), sys_context('userenv', 'proxy_user') from dual""") actual_user, actual_proxy_user = cursor.fetchone() self.assertEqual(actual_user, expected_user.upper()) self.assertEqual(actual_proxy_user, expected_proxy_user and expected_proxy_user.upper()) def test_2400_pool(self): "2400 - test that the pool is created and has the right attributes" pool = test_env.get_pool(min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_WAIT) self.assertEqual(pool.username, test_env.get_main_user(), "user name differs") self.assertEqual(pool.tnsentry, test_env.get_connect_string(), "tnsentry differs") self.assertEqual(pool.dsn, test_env.get_connect_string(), "dsn differs") self.assertEqual(pool.max, 8, "max differs") self.assertEqual(pool.min, 2, "min differs") self.assertEqual(pool.increment, 3, "increment differs") self.assertEqual(pool.busy, 0, "busy not 0 at start") connection_1 = pool.acquire() self.assertEqual(pool.busy, 1, "busy not 1 after acquire") connection_2 = pool.acquire() self.assertEqual(pool.busy, 2, "busy not 2 after acquire") self.assertEqual(pool.opened, 2, "opened differs") connection_3 = pool.acquire() self.assertEqual(pool.busy, 3, "busy not 3 after acquire") pool.release(connection_3) self.assertEqual(pool.busy, 2, "busy not 2 after release") pool.release(connection_1) pool.release(connection_2) self.assertEqual(pool.busy, 0, "busy not 0 after release") pool.getmode = oracledb.SPOOL_ATTRVAL_NOWAIT self.assertEqual(pool.getmode, oracledb.SPOOL_ATTRVAL_NOWAIT) if test_env.get_client_version() >= (12, 2): pool.getmode = oracledb.SPOOL_ATTRVAL_TIMEDWAIT self.assertEqual(pool.getmode, oracledb.SPOOL_ATTRVAL_TIMEDWAIT) pool.stmtcachesize = 50 self.assertEqual(pool.stmtcachesize, 50) pool.timeout = 10 self.assertEqual(pool.timeout, 10) if test_env.get_client_version() >= (12, 1): pool.max_lifetime_session = 10 self.assertEqual(pool.max_lifetime_session, 10) def test_2401_proxy_auth(self): "2401 - test that proxy authentication is possible" pool = test_env.get_pool(min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_WAIT) self.assertEqual(pool.homogeneous, True, "homogeneous should be True by default") self.assertRaises(oracledb.DatabaseError, pool.acquire, user="missing_proxyuser") pool = test_env.get_pool(min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_WAIT, homogeneous=False) msg = "homogeneous should be False after setting it in the constructor" self.assertEqual(pool.homogeneous, False, msg) connection = pool.acquire(user=test_env.get_proxy_user()) cursor = connection.cursor() cursor.execute('select user from dual') result, = cursor.fetchone() self.assertEqual(result, test_env.get_proxy_user().upper()) connection.close() def test_2403_rollback_on_release(self): "2403 - connection rolls back before released back to the pool" pool = test_env.get_pool(getmode=oracledb.SPOOL_ATTRVAL_WAIT) connection = pool.acquire() cursor = connection.cursor() cursor.execute("truncate table TestTempTable") cursor.execute("insert into TestTempTable (IntCol) values (1)") cursor.close() pool.release(connection) pool = test_env.get_pool(getmode=oracledb.SPOOL_ATTRVAL_WAIT) connection = pool.acquire() cursor = connection.cursor() cursor.execute("select count(*) from TestTempTable") count, = cursor.fetchone() self.assertEqual(count, 0) connection.close() def test_2404_threading(self): "2404 - test session pool with multiple threads" self.pool = test_env.get_pool(min=5, max=20, increment=2, threaded=True, getmode=oracledb.SPOOL_ATTRVAL_WAIT) threads = [] for i in range(20): thread = threading.Thread(None, self.__connect_and_drop) threads.append(thread) thread.start() for thread in threads: thread.join() def test_2405_threading_with_errors(self): "2405 - test session pool with multiple threads (with errors)" self.pool = test_env.get_pool(min=5, max=20, increment=2, threaded=True, getmode=oracledb.SPOOL_ATTRVAL_WAIT) threads = [] for i in range(20): thread = threading.Thread(None, self.__connect_and_generate_error) threads.append(thread) thread.start() for thread in threads: thread.join() def test_2406_purity(self): "2406 - test session pool with various types of purity" pool = test_env.get_pool(min=1, max=8, increment=1, getmode=oracledb.SPOOL_ATTRVAL_WAIT) # get connection and set the action action = "TEST_ACTION" connection = pool.acquire() connection.action = action cursor = connection.cursor() cursor.execute("select 1 from dual") cursor.close() pool.release(connection) self.assertEqual(pool.opened, 1, "opened (1)") # verify that the connection still has the action set on it connection = pool.acquire() cursor = connection.cursor() cursor.execute("select sys_context('userenv', 'action') from dual") result, = cursor.fetchone() self.assertEqual(result, action) cursor.close() pool.release(connection) self.assertEqual(pool.opened, 1, "opened (2)") # get a new connection with new purity (should not have state) connection = pool.acquire(purity=oracledb.ATTR_PURITY_NEW) cursor = connection.cursor() cursor.execute("select sys_context('userenv', 'action') from dual") result, = cursor.fetchone() self.assertEqual(result, None) cursor.close() pool.release(connection) def test_2407_heterogeneous(self): "2407 - test heterogeneous pool with user and password specified" pool = test_env.get_pool(min=2, max=8, increment=3, homogeneous=False, getmode=oracledb.SPOOL_ATTRVAL_WAIT) self.assertEqual(pool.homogeneous, 0) conn = pool.acquire() self.__verify_connection(pool.acquire(), test_env.get_main_user()) conn.close() conn = pool.acquire(test_env.get_main_user(), test_env.get_main_password()) self.__verify_connection(conn, test_env.get_main_user()) conn.close() conn = pool.acquire(test_env.get_proxy_user(), test_env.get_proxy_password()) self.__verify_connection(conn, test_env.get_proxy_user()) conn.close() user_str = "%s[%s]" % \ (test_env.get_main_user(), test_env.get_proxy_user()) conn = pool.acquire(user_str, test_env.get_main_password()) self.__verify_connection(conn, test_env.get_proxy_user(), test_env.get_main_user()) conn.close() def test_2408_heterogenous_without_user(self): "2408 - test heterogeneous pool without user and password specified" pool = test_env.get_pool(user="", password="", min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_WAIT, homogeneous=False) conn = pool.acquire(test_env.get_main_user(), test_env.get_main_password()) self.__verify_connection(conn, test_env.get_main_user()) conn.close() conn = pool.acquire(test_env.get_proxy_user(), test_env.get_proxy_password()) self.__verify_connection(conn, test_env.get_proxy_user()) conn.close() user_str = "%s[%s]" % \ (test_env.get_main_user(), test_env.get_proxy_user()) conn = pool.acquire(user_str, test_env.get_main_password()) self.__verify_connection(conn, test_env.get_proxy_user(), test_env.get_main_user()) def test_2409_heterogeneous_wrong_password(self): "2409 - test heterogeneous pool with wrong password specified" pool = test_env.get_pool(min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_WAIT, homogeneous=False) self.assertRaises(oracledb.DatabaseError, pool.acquire, test_env.get_proxy_user(), "this is the wrong password") def test_2410_tagging_session(self): "2410 - test tagging a session" pool = test_env.get_pool(min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_NOWAIT) tag_mst = "TIME_ZONE=MST" tag_utc = "TIME_ZONE=UTC" conn = pool.acquire() self.assertEqual(conn.tag, None) pool.release(conn, tag=tag_mst) conn = pool.acquire() self.assertEqual(conn.tag, None) conn.tag = tag_utc conn.close() conn = pool.acquire(tag=tag_mst) self.assertEqual(conn.tag, tag_mst) conn.close() conn = pool.acquire(tag=tag_utc) self.assertEqual(conn.tag, tag_utc) conn.close() def test_2411_plsql_session_callbacks(self): "2411 - test PL/SQL session callbacks" if test_env.get_client_version() < (12, 2): self.skipTest("PL/SQL session callbacks not supported before 12.2") callback = "pkg_SessionCallback.TheCallback" pool = test_env.get_pool(min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_NOWAIT, session_callback=callback) tags = [ "NLS_DATE_FORMAT=SIMPLE", "NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC", "NLS_DATE_FORMAT=FULL;TIME_ZONE=MST" ] actual_tags = [None, None, "NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC"] # truncate PL/SQL session callback log conn = pool.acquire() cursor = conn.cursor() cursor.execute("truncate table PLSQLSessionCallbacks") conn.close() # request sessions with each of the first two tags for tag in tags[:2]: conn = pool.acquire(tag=tag) conn.close() # for the last tag, use the matchanytag flag conn = pool.acquire(tag=tags[2], matchanytag=True) conn.close() # verify the PL/SQL session callback log is accurate conn = pool.acquire() cursor = conn.cursor() cursor.execute(""" select RequestedTag, ActualTag from PLSQLSessionCallbacks order by FixupTimestamp""") results = cursor.fetchall() expected_results = list(zip(tags, actual_tags)) self.assertEqual(results, expected_results) conn.close() def test_2412_tagging_invalid_key(self): "2412 - testTagging with Invalid key" pool = test_env.get_pool(getmode=oracledb.SPOOL_ATTRVAL_NOWAIT) conn = pool.acquire() self.assertRaises(TypeError, pool.release, conn, tag=12345) if test_env.get_client_version() >= (12, 2): self.assertRaises(oracledb.DatabaseError, pool.release, conn, tag="INVALID_TAG") def test_2413_close_and_drop_connection_from_pool(self): "2413 - test dropping/closing a connection from the pool" pool = test_env.get_pool(min=1, max=8, increment=1, getmode=oracledb.SPOOL_ATTRVAL_WAIT) conn = pool.acquire() self.assertEqual(pool.busy, 1, "busy (1)") self.assertEqual(pool.opened, 1, "opened (1)") pool.drop(conn) self.assertEqual(pool.busy, 0, "busy (2)") self.assertEqual(pool.opened, 0, "opened (2)") conn = pool.acquire() self.assertEqual(pool.busy, 1, "busy (3)") self.assertEqual(pool.opened, 1, "opened (3)") conn.close() self.assertEqual(pool.busy, 0, "busy (4)") self.assertEqual(pool.opened, 1, "opened (4)") def test_2414_create_new_pure_connection(self): "2414 - test to ensure pure connections are being created correctly" pool = test_env.get_pool(min=1, max=2, increment=1, getmode=oracledb.SPOOL_ATTRVAL_WAIT) connection_1 = pool.acquire() connection_2 = pool.acquire() self.assertEqual(pool.opened, 2, "opened (1)") pool.release(connection_1) pool.release(connection_2) connection_3 = pool.acquire(purity=oracledb.ATTR_PURITY_NEW) self.assertEqual(pool.opened, 2, "opened (2)") pool.release(connection_3) def test_2415_reconfigure_pool(self): "2415 - test to ensure reconfigure() updates pool properties" pool = test_env.get_pool(min=1, max=2, increment=1, getmode=oracledb.SPOOL_ATTRVAL_WAIT) self.assertEqual(pool.min, 1, "min (1)") self.assertEqual(pool.max, 2, "max (2)") self.assertEqual(pool.increment, 1, "increment (1)") self.assertEqual(pool.getmode, oracledb.SPOOL_ATTRVAL_WAIT, "getmode differs") self.assertEqual(pool.timeout, 0, "timeout (0)") self.assertEqual(pool.wait_timeout, 5000, "wait_timeout (5000)") self.assertEqual(pool.max_lifetime_session, 0, "max_lifetime_sessionmeout (0)") self.assertEqual(pool.max_sessions_per_shard, 0, "max_sessions_per_shard (0)") self.assertEqual(pool.stmtcachesize, 20, "stmtcachesize (20)") self.assertEqual(pool.ping_interval, 60, "ping_interval (60)") pool.reconfigure(min=2, max=5, increment=2, timeout=30, getmode=oracledb.SPOOL_ATTRVAL_TIMEDWAIT, wait_timeout=3000, max_lifetime_session=20, max_sessions_per_shard=2, stmtcachesize=30, ping_interval=30) self.assertEqual(pool.min, 2, "min (2)") self.assertEqual(pool.max, 5, "max (5)") self.assertEqual(pool.increment, 2, "increment (2)") self.assertEqual(pool.getmode, oracledb.SPOOL_ATTRVAL_TIMEDWAIT, "getmode differs") self.assertEqual(pool.timeout, 30, "timeout (30)") self.assertEqual(pool.wait_timeout, 3000, "wait_timeout (3000)") self.assertEqual(pool.max_lifetime_session, 20, "max_lifetime_sessionmeout (20)") self.assertEqual(pool.max_sessions_per_shard, 2, "max_sessions_per_shard (2)") self.assertEqual(pool.stmtcachesize, 30, "stmtcachesize (30)") self.assertEqual(pool.ping_interval, 30, "ping_interval (30)") def test_2416_test_reconfigure_pool_with_missing_values(self): "2416 - test the reconfigure values are changed and rest unchanged" self.__perform_reconfigure_test("min", 5) self.__perform_reconfigure_test("max", 20) self.__perform_reconfigure_test("increment", 5) self.__perform_reconfigure_test("timeout", 10) self.__perform_reconfigure_test("wait_timeout", 8000) self.__perform_reconfigure_test("stmtcachesize", 40) self.__perform_reconfigure_test("max_lifetime_session", 2000) self.__perform_reconfigure_test("max_sessions_per_shard", 5) self.__perform_reconfigure_test("ping_interval", 50) self.__perform_reconfigure_test("getmode", oracledb.SPOOL_ATTRVAL_NOWAIT) def test_2417_setting_each_pool_param(self): "2417 - test to see if specified parameters are set during creation" pool = test_env.get_pool(min=1, max=2, increment=1, timeout=10, wait_timeout=10, max_lifetime_session=20, max_sessions_per_shard=1, stmtcachesize=25, ping_interval=25, getmode=oracledb.SPOOL_ATTRVAL_WAIT) self.assertEqual(pool.min, 1, "min (1)") self.assertEqual(pool.max, 2, "max (2)") self.assertEqual(pool.increment, 1, "increment (1)") self.assertEqual(pool.getmode, oracledb.SPOOL_ATTRVAL_WAIT, "getmode differs") self.assertEqual(pool.timeout, 10, "timeout (10)") self.assertEqual(pool.wait_timeout, 10, "wait_timeout (10)") self.assertEqual(pool.max_lifetime_session, 20, "max_lifetime_sessionmeout (20)") self.assertEqual(pool.max_sessions_per_shard, 1, "max_sessions_per_shard (1)") self.assertEqual(pool.stmtcachesize, 25, "stmtcachesize (25)") self.assertEqual(pool.ping_interval, 25, "ping_interval (25)") def test_2418_deprecations(self): "2418 - test to verify deprecations" callback = "pkg_SessionCallback.TheCallback" self.assertRaises(oracledb.ProgrammingError, test_env.get_pool, min=1, max=2, increment=1, wait_timeout=10, waitTimeout=10) self.assertRaises(oracledb.ProgrammingError, test_env.get_pool, min=1, max=2, increment=1, max_lifetime_session=20, maxLifetimeSession=20) self.assertRaises(oracledb.ProgrammingError, test_env.get_pool, min=1, max=2, increment=1, max_sessions_per_shard=1, maxSessionsPerShard=1) self.assertRaises(oracledb.ProgrammingError, test_env.get_pool, min=2, max=8, increment=3, getmode=oracledb.SPOOL_ATTRVAL_NOWAIT, session_callback=callback, sessionCallback=callback) def test_2419_statement_cache_size(self): "2419 - test to verify statement cache size is retained" pool = test_env.get_pool(min=1, max=2, increment=1, getmode=oracledb.SPOOL_ATTRVAL_WAIT, stmtcachesize=25) self.assertEqual(pool.stmtcachesize, 25, "stmtcachesize (25)") pool.stmtcachesize = 35 self.assertEqual(pool.stmtcachesize, 35, "stmtcachesize (35)") def test_2420_callable_session_callbacks(self): "2420 - test that session callbacks are being called correctly" pool = test_env.get_pool(min=2, max=5, increment=1, session_callback=self.__callable_session_callback) # new connection with a tag should invoke the session callback with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: cursor = conn.cursor() cursor.execute("select to_char(2021-05-20) from dual") result, = cursor.fetchone() self.assertEqual(self.session_called, True) # acquiring a connection with the same tag should not invoke the # session callback self.session_called = False with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: cursor = conn.cursor() cursor.execute("select to_char(2021-05-20) from dual") result, = cursor.fetchone() self.assertEqual(self.session_called, False) # acquiring a connection with a new tag should invoke the session # callback self.session_called = False with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC") as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() self.assertEqual(self.session_called, True) # acquiring a connection with a new tag and specifying that a # connection with any tag can be acquired should invoke the session # callback self.session_called = False with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", \ matchanytag=True) as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() self.assertEqual(self.session_called, True) # new session with no tag should invoke the session callback self.session_called = False with pool.acquire() as conn: cursor = conn.cursor() cursor.execute("select to_char(current_date) from dual") result, = cursor.fetchone() self.assertEqual(self.session_called, True) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2500_string_var.py000066400000000000000000000503071414105416400221610ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 2500 - Module for testing string variables """ import datetime import random import string import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def setUp(self): super().setUp() self.raw_data = [] self.data_by_key = {} for i in range(1, 11): string_col = "String %d" % i fixed_char_col = ("Fixed Char %d" % i).ljust(40) raw_col = ("Raw %d" % i).encode("ascii") if i % 2: nullable_col = "Nullable %d" % i else: nullable_col = None data_tuple = (i, string_col, raw_col, fixed_char_col, nullable_col) self.raw_data.append(data_tuple) self.data_by_key[i] = data_tuple def __return_strings_as_bytes(self, cursor, name, default_type, size, precision, scale): if default_type == oracledb.DB_TYPE_VARCHAR: return cursor.var(str, arraysize=cursor.arraysize, bypass_decode=True) def test_2500_array_with_increased_size(self): "2500 - test creating array var and then increasing the internal size" val = ["12345678901234567890"] * 3 var = self.cursor.arrayvar(str, len(val), 4) var.setvalue(0, val) self.assertEqual(var.getvalue(), val) def test_2501_bind_string(self): "2501 - test binding in a string" self.cursor.execute(""" select * from TestStrings where StringCol = :value""", value="String 5") self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]]) def test_2502_bind_different_var(self): "2502 - test binding a different variable on second execution" retval_1 = self.cursor.var(oracledb.STRING, 30) retval_2 = self.cursor.var(oracledb.STRING, 30) self.cursor.execute("begin :retval := 'Called'; end;", retval=retval_1) self.assertEqual(retval_1.getvalue(), "Called") self.cursor.execute("begin :retval := 'Called'; end;", retval=retval_2) self.assertEqual(retval_2.getvalue(), "Called") def test_2503_exceeds_num_elements(self): "2503 - test exceeding the number of elements returns IndexError" var = self.cursor.var(str) self.assertRaises(IndexError, var.getvalue, 1) def test_2504_bind_string_after_number(self): "2504 - test binding in a string after setting input sizes to a number" self.cursor.setinputsizes(value = oracledb.NUMBER) self.cursor.execute(""" select * from TestStrings where StringCol = :value""", value="String 6") self.assertEqual(self.cursor.fetchall(), [self.data_by_key[6]]) def test_2505_bind_string_array_direct(self): "2505 - test binding in a string array" return_value = self.cursor.var(oracledb.NUMBER) array = [r[1] for r in self.raw_data] statement = """ begin :return_value := pkg_TestStringArrays.TestInArrays( :integer_value, :array); end;""" self.cursor.execute(statement, return_value=return_value, integer_value=5, array=array) self.assertEqual(return_value.getvalue(), 86) array = [ "String - %d" % i for i in range(15) ] self.cursor.execute(statement, integer_value=8, array=array) self.assertEqual(return_value.getvalue(), 163) def test_2506_bind_string_array_by_sizes(self): "2506 - test binding in a string array (with setinputsizes)" return_value = self.cursor.var(oracledb.NUMBER) self.cursor.setinputsizes(array=[oracledb.STRING, 10]) array = [r[1] for r in self.raw_data] self.cursor.execute(""" begin :return_value := pkg_TestStringArrays.TestInArrays( :integer_value, :array); end;""", return_value=return_value, integer_value=6, array=array) self.assertEqual(return_value.getvalue(), 87) def test_2507_bind_string_array_by_var(self): "2507 - test binding in a string array (with arrayvar)" return_value = self.cursor.var(oracledb.NUMBER) array = self.cursor.arrayvar(oracledb.STRING, 10, 20) array.setvalue(0, [r[1] for r in self.raw_data]) self.cursor.execute(""" begin :return_value := pkg_TestStringArrays.TestInArrays( :integer_value, :array); end;""", return_value=return_value, integer_value=7, array=array) self.assertEqual(return_value.getvalue(), 88) def test_2508_bind_in_out_string_array_by_var(self): "2508 - test binding in/out a string array (with arrayvar)" array = self.cursor.arrayvar(oracledb.STRING, 10, 100) original_data = [r[1] for r in self.raw_data] expected_data = ["Converted element # %d originally had length %d" % \ (i, len(original_data[i - 1])) \ for i in range(1, 6)] + original_data[5:] array.setvalue(0, original_data) self.cursor.execute(""" begin pkg_TestStringArrays.TestInOutArrays(:num_elems, :array); end;""", num_elems=5, array=array) self.assertEqual(array.getvalue(), expected_data) def test_2509_bind_out_string_array_by_var(self): "2509 - test binding out a string array (with arrayvar)" array = self.cursor.arrayvar(oracledb.STRING, 6, 100) expected_data = ["Test out element # %d" % i for i in range(1, 7)] self.cursor.execute(""" begin pkg_TestStringArrays.TestOutArrays(:num_elems, :array); end;""", num_elems=6, array=array) self.assertEqual(array.getvalue(), expected_data) def test_2510_bind_raw(self): "2510 - test binding in a raw" self.cursor.setinputsizes(value = oracledb.BINARY) self.cursor.execute(""" select * from TestStrings where RawCol = :value""", value="Raw 4".encode()) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[4]]) def test_2511_bind_and_fetch_rowid(self): "2511 - test binding (and fetching) a rowid" self.cursor.execute(""" select rowid from TestStrings where IntCol = 3""") rowid, = self.cursor.fetchone() self.cursor.execute(""" select * from TestStrings where rowid = :value""", value=rowid) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[3]]) def test_2512_bind_and_fetch_universal_rowids(self): "2512 - test binding (and fetching) universal rowids" self.cursor.execute("truncate table TestUniversalRowids") data = [ (1, "ABC" * 75, datetime.datetime(2017, 4, 11)), (2, "DEF" * 80, datetime.datetime(2017, 4, 12)) ] for row in data: self.cursor.execute(""" insert into TestUniversalRowids values (:1, :2, :3)""", row) self.connection.commit() self.cursor.execute(""" select rowid from TestUniversalRowIds order by IntCol""") rowids = [r for r, in self.cursor] fetched_data = [] for rowid in rowids: self.cursor.execute(""" select * from TestUniversalRowids where rowid = :rid""", rid=rowid) fetched_data.extend(self.cursor.fetchall()) self.assertEqual(fetched_data, data) def test_2513_bind_null(self): "2513 - test binding in a null" self.cursor.execute(""" select * from TestStrings where StringCol = :value""", value=None) self.assertEqual(self.cursor.fetchall(), []) def test_2514_bind_out_set_input_sizes_by_type(self): "2514 - test binding out with set input sizes defined (by type)" bind_vars = self.cursor.setinputsizes(value=oracledb.STRING) self.cursor.execute(""" begin :value := 'TSI'; end;""") self.assertEqual(bind_vars["value"].getvalue(), "TSI") def test_2515_bind_out_set_input_sizes_by_integer(self): "2515 - test binding out with set input sizes defined (by integer)" bind_vars = self.cursor.setinputsizes(value=30) self.cursor.execute(""" begin :value := 'TSI (I)'; end;""") self.assertEqual(bind_vars["value"].getvalue(), "TSI (I)") def test_2516_bind_in_out_set_input_sizes_by_type(self): "2516 - test binding in/out with set input sizes defined (by type)" bind_vars = self.cursor.setinputsizes(value=oracledb.STRING) self.cursor.execute(""" begin :value := :value || ' TSI'; end;""", value="InVal") self.assertEqual(bind_vars["value"].getvalue(), "InVal TSI") def test_2517_bind_in_out_set_input_sizes_by_integer(self): "2517 - test binding in/out with set input sizes defined (by integer)" bind_vars = self.cursor.setinputsizes(value=30) self.cursor.execute(""" begin :value := :value || ' TSI (I)'; end;""", value="InVal") self.assertEqual(bind_vars["value"].getvalue(), "InVal TSI (I)") def test_2518_bind_out_var(self): "2518 - test binding out with cursor.var() method" var = self.cursor.var(oracledb.STRING) self.cursor.execute(""" begin :value := 'TSI (VAR)'; end;""", value=var) self.assertEqual(var.getvalue(), "TSI (VAR)") def test_2519_bind_in_out_var_direct_set(self): "2519 - test binding in/out with cursor.var() method" var = self.cursor.var(oracledb.STRING) var.setvalue(0, "InVal") self.cursor.execute(""" begin :value := :value || ' TSI (VAR)'; end;""", value=var) self.assertEqual(var.getvalue(), "InVal TSI (VAR)") def test_2520_bind_long_string(self): "2520 - test that binding a long string succeeds" self.cursor.setinputsizes(big_string=oracledb.DB_TYPE_LONG) self.cursor.execute(""" declare t_Temp varchar2(20000); begin t_Temp := :big_string; end;""", big_string="X" * 10000) def test_2521_bind_long_string_after_setting_size(self): "2521 - test that setinputsizes() returns a long variable" var = self.cursor.setinputsizes(test=90000)["test"] in_string = "1234567890" * 9000 var.setvalue(0, in_string) out_string = var.getvalue() msg = f"output does not match: in was {len(in_string)}, " \ f"out was {len(out_string)}" self.assertEqual(in_string, out_string, msg) def test_2522_cursor_description(self): "2522 - test cursor description is accurate" self.cursor.execute("select * from TestStrings") varchar_ratio, nvarchar_ratio = test_env.get_charset_ratios() expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('STRINGCOL', oracledb.DB_TYPE_VARCHAR, 20, 20 * varchar_ratio, None, None, False), ('RAWCOL', oracledb.DB_TYPE_RAW, 30, 30, None, None, False), ('FIXEDCHARCOL', oracledb.DB_TYPE_CHAR, 40, 40 * varchar_ratio, None, None, False), ('NULLABLECOL', oracledb.DB_TYPE_VARCHAR, 50, 50 * varchar_ratio, None, None, True) ] self.assertEqual(self.cursor.description, expected_value) def test_2523_fetchall(self): "2523 - test that fetching all of the data returns the correct results" self.cursor.execute("select * From TestStrings order by IntCol") self.assertEqual(self.cursor.fetchall(), self.raw_data) self.assertEqual(self.cursor.fetchall(), []) def test_2524_fetchmany(self): "2524 - test that fetching data in chunks returns the correct results" self.cursor.execute("select * From TestStrings order by IntCol") self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3]) self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5]) self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9]) self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:]) self.assertEqual(self.cursor.fetchmany(3), []) def test_2525_fetchone(self): "2525 - test that fetching a single row returns the correct results" self.cursor.execute(""" select * from TestStrings where IntCol in (3, 4) order by IntCol""") self.assertEqual(self.cursor.fetchone(), self.data_by_key[3]) self.assertEqual(self.cursor.fetchone(), self.data_by_key[4]) self.assertEqual(self.cursor.fetchone(), None) def test_2526_supplemental_characters(self): "2526 - test binding and fetching supplemental charcters" self.cursor.execute(""" select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET'""") charset, = self.cursor.fetchone() if charset != "AL32UTF8": self.skipTest("Database character set must be AL32UTF8") supplemental_chars = "𠜎 𠜱 𠹠𠱓 𠱸 𠲖 𠳠𠳕 𠴕 𠵼 𠵿 𠸎 𠸠𠹷 𠺠" \ "𠺢 𠻗 𠻹 𠻺 𠼭 𠼮 𠽌 𠾴 𠾼 𠿪 𡜠𡯠𡵠𡶠𡻠𡃠𡃉 𡇙 𢃇 " \ "𢞵 𢫕 𢭃 𢯊 𢱑 𢱕 𢳂 𢴈 𢵌 𢵧 𢺳 𣲷 𤓓 𤶸 𤷪 𥄫 𦉘 𦟌 𦧲 " \ "𦧺 𧨾 𨅠𨈇 𨋢 𨳊 𨳠𨳒 𩶘" self.cursor.execute("truncate table TestTempTable") self.cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (:1, :2)""", (1, supplemental_chars)) self.connection.commit() self.cursor.execute("select StringCol from TestTempTable") value, = self.cursor.fetchone() self.assertEqual(value, supplemental_chars) def test_2527_bind_twice_with_large_string_second(self): "2527 - test binding twice with a larger string the second time" self.cursor.execute("truncate table TestTempTable") sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" short_string = "short string" long_string = "long string " * 30 self.cursor.execute(sql, (1, short_string)) self.cursor.execute(sql, (2, long_string)) self.connection.commit() self.cursor.execute(""" select IntCol, StringCol from TestTempTable order by IntCol""") self.assertEqual(self.cursor.fetchall(), [(1, short_string), (2, long_string)]) def test_2528_issue_50(self): "2528 - test issue 50 - avoid error ORA-24816" cursor = self.connection.cursor() try: cursor.execute("drop table issue_50 purge") except oracledb.DatabaseError: pass cursor.execute(""" create table issue_50 ( Id number(11) primary key, Str1 nvarchar2(256), Str2 nvarchar2(256), Str3 nvarchar2(256), NClob1 nclob, NClob2 nclob )""") id_var = cursor.var(oracledb.NUMBER) cursor.execute(""" insert into issue_50 (Id, Str2, Str3, NClob1, NClob2, Str1) values (:arg0, :arg1, :arg2, :arg3, :arg4, :arg5) returning id into :arg6""", [1, '555a4c78', 'f319ef0e', '23009914', '', '', id_var]) cursor = self.connection.cursor() cursor.execute(""" insert into issue_50 (Id, Str2, Str3, NClob1, NClob2, Str1) values (:arg0, :arg1, :arg2, :arg3, :arg4, :arg5) returning id into :arg6""", [2, u'd5ff845a', u'94275767', u'bf161ff6', u'', u'', id_var]) cursor.execute("drop table issue_50 purge") def test_2529_set_rowid_to_string(self): "2529 - test assigning a string to rowid" var = self.cursor.var(oracledb.ROWID) self.assertRaises(oracledb.NotSupportedError, var.setvalue, 0, "ABDHRYTHFJGKDKKDH") def test_2530_short_xml_as_string(self): "2530 - test fetching XMLType object as a string" self.cursor.execute(""" select XMLElement("string", stringCol) from TestStrings where intCol = 1""") actual_value, = self.cursor.fetchone() expected_value = "String 1" self.assertEqual(actual_value, expected_value) def test_2531_long_xml_as_string(self): "2531 - test inserting and fetching an XMLType object (1K) as a string" chars = string.ascii_uppercase + string.ascii_lowercase random_string = ''.join(random.choice(chars) for _ in range(1024)) int_val = 200 xml_string = '' + random_string + '' self.cursor.execute("truncate table TestTempXML") self.cursor.execute(""" insert into TestTempXML (IntCol, XMLCol) values (:1, :2)""", (int_val, xml_string)) self.cursor.execute("select XMLCol from TestTempXML where intCol = :1", (int_val,)) actual_value, = self.cursor.fetchone() self.assertEqual(actual_value.strip(), xml_string) def test_2532_fetch_null_values(self): "2532 - fetching null and not null values can use optimised path" sql = """ select * from TestStrings where IntCol between :start_value and :end_value""" self.cursor.execute(sql, start_value=2, end_value=5) self.assertEqual(self.cursor.fetchall(), self.raw_data[1:5]) self.cursor.execute(sql, start_value=5, end_value=8) self.assertEqual(self.cursor.fetchall(), self.raw_data[4:8]) self.cursor.execute(sql, start_value=8, end_value=10) self.assertEqual(self.cursor.fetchall(), self.raw_data[7:10]) def test_2533_bypass_decode(self): "2533 - test bypass string decode" self.cursor.execute("truncate table TestTempTable") string_val = "I bought a cafetière on the Champs-Élysées" sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" with self.connection.cursor() as cursor: cursor.execute(sql, (1, string_val)) cursor.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor.fetchone(), (1, string_val)) with self.connection.cursor() as cursor: cursor.outputtypehandler = self.__return_strings_as_bytes cursor.execute("select IntCol, StringCol from TestTempTable") expected_value = (1, string_val.encode()) self.assertEqual(cursor.fetchone(), (1, string_val.encode())) with self.connection.cursor() as cursor: cursor.outputtypehandler = None cursor.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor.fetchone(), (1, string_val)) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2600_timestamp_var.py000066400000000000000000000150771414105416400226640ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 2600 - Module for testing timestamp variables """ import time import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def setUp(self): super().setUp() self.raw_data = [] self.data_by_key = {} for i in range(1, 11): time_tuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1) time_in_ticks = time.mktime(time_tuple) + i * 86400 date_value = oracledb.TimestampFromTicks(int(time_in_ticks)) str_value = str(i * 50) fsecond = int(str_value + "0" * (6 - len(str_value))) date_col = oracledb.Timestamp(date_value.year, date_value.month, date_value.day, date_value.hour, date_value.minute, i * 2, fsecond) if i % 2: time_in_ticks = time.mktime(time_tuple) + i * 86400 + 86400 date_value = oracledb.TimestampFromTicks(int(time_in_ticks)) str_value = str(i * 125) fsecond = int(str_value + "0" * (6 - len(str_value))) nullable_col = oracledb.Timestamp(date_value.year, date_value.month, date_value.day, date_value.hour, date_value.minute, i * 3, fsecond) else: nullable_col = None data_tuple = (i, date_col, nullable_col) self.raw_data.append(data_tuple) self.data_by_key[i] = data_tuple def test_2600_bind_timestamp(self): "2600 - test binding in a timestamp" self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP) self.cursor.execute(""" select * from TestTimestamps where TimestampCol = :value""", value=oracledb.Timestamp(2002, 12, 14, 0, 0, 10, 250000)) self.assertEqual(self.cursor.fetchall(), [self.data_by_key[5]]) def test_2601_bind_null(self): "2601 - test binding in a null" self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP) self.cursor.execute(""" select * from TestTimestamps where TimestampCol = :value""", value=None) self.assertEqual(self.cursor.fetchall(), []) def test_2602_bind_out_set_input_sizes(self): "2602 - test binding out with set input sizes defined" bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP) self.cursor.execute(""" begin :value := to_timestamp('20021209', 'YYYYMMDD'); end;""") self.assertEqual(bind_vars["value"].getvalue(), oracledb.Timestamp(2002, 12, 9)) def test_2603_bind_in_out_set_input_sizes(self): "2603 - test binding in/out with set input sizes defined" bind_vars = self.cursor.setinputsizes(value=oracledb.DB_TYPE_TIMESTAMP) self.cursor.execute(""" begin :value := :value + 5.25; end;""", value = oracledb.Timestamp(2002, 12, 12, 10, 0, 0)) self.assertEqual(bind_vars["value"].getvalue(), oracledb.Timestamp(2002, 12, 17, 16, 0, 0)) def test_2604_bind_out_var(self): "2604 - test binding out with cursor.var() method" var = self.cursor.var(oracledb.DB_TYPE_TIMESTAMP) self.cursor.execute(""" begin :value := to_date('20021231 12:31:00', 'YYYYMMDD HH24:MI:SS'); end;""", value=var) self.assertEqual(var.getvalue(), oracledb.Timestamp(2002, 12, 31, 12, 31, 0)) def test_2605_bind_in_out_var_direct_set(self): "2605 - test binding in/out with cursor.var() method" var = self.cursor.var(oracledb.DB_TYPE_TIMESTAMP) var.setvalue(0, oracledb.Timestamp(2002, 12, 9, 6, 0, 0)) self.cursor.execute(""" begin :value := :value + 5.25; end;""", value = var) self.assertEqual(var.getvalue(), oracledb.Timestamp(2002, 12, 14, 12, 0, 0)) def test_2606_cursor_description(self): "2606 - test cursor description is accurate" self.cursor.execute("select * from TestTimestamps") expected_value = [ ('INTCOL', oracledb.DB_TYPE_NUMBER, 10, None, 9, 0, False), ('TIMESTAMPCOL', oracledb.DB_TYPE_TIMESTAMP, 23, None, 0, 6, False), ('NULLABLECOL', oracledb.DB_TYPE_TIMESTAMP, 23, None, 0, 6, True) ] self.assertEqual(self.cursor.description, expected_value) def test_2607_fetchall(self): "2607 - test that fetching all of the data returns the correct results" self.cursor.execute("select * From TestTimestamps order by IntCol") self.assertEqual(self.cursor.fetchall(), self.raw_data) self.assertEqual(self.cursor.fetchall(), []) def test_2608_fetchmany(self): "2608 - test that fetching data in chunks returns the correct results" self.cursor.execute("select * From TestTimestamps order by IntCol") self.assertEqual(self.cursor.fetchmany(3), self.raw_data[0:3]) self.assertEqual(self.cursor.fetchmany(2), self.raw_data[3:5]) self.assertEqual(self.cursor.fetchmany(4), self.raw_data[5:9]) self.assertEqual(self.cursor.fetchmany(3), self.raw_data[9:]) self.assertEqual(self.cursor.fetchmany(3), []) def test_2609_fetchone(self): "2609 - test that fetching a single row returns the correct results" self.cursor.execute(""" select * from TestTimestamps where IntCol in (3, 4) order by IntCol""") self.assertEqual(self.cursor.fetchone(), self.data_by_key[3]) self.assertEqual(self.cursor.fetchone(), self.data_by_key[4]) self.assertEqual(self.cursor.fetchone(), None) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2700_aq.py000066400000000000000000000442711414105416400204110ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 2700 - Module for testing AQ """ import decimal import threading import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): book_type_name = "UDT_BOOK" book_queue_name = "TEST_BOOK_QUEUE" book_data = [ ("Wings of Fire", "A.P.J. Abdul Kalam", decimal.Decimal("15.75")), ("The Story of My Life", "Hellen Keller", decimal.Decimal("10.50")), ("The Chronicles of Narnia", "C.S. Lewis", decimal.Decimal("25.25")) ] def __clear_books_queue(self): books_type = self.connection.gettype(self.book_type_name) queue = self.connection.queue(self.book_queue_name, books_type) queue.deqoptions.wait = oracledb.DEQ_NO_WAIT queue.deqoptions.deliverymode = oracledb.MSG_PERSISTENT_OR_BUFFERED queue.deqoptions.visibility = oracledb.DEQ_IMMEDIATE while queue.deqone(): pass def __deq_in_thread(self, results): connection = test_env.get_connection(threaded=True) books_type = connection.gettype(self.book_type_name) queue = connection.queue(self.book_queue_name, books_type) queue.deqoptions.wait = 10 props = queue.deqone() if props is not None: book = props.payload results.append((book.TITLE, book.AUTHORS, book.PRICE)) connection.commit() def __verify_attr(self, obj, attrName, value): setattr(obj, attrName, value) self.assertEqual(getattr(obj, attrName), value) def test_2700_deq_empty(self): "2700 - test dequeuing an empty queue" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() options = self.connection.deqoptions() options.wait = oracledb.DEQ_NO_WAIT props = self.connection.msgproperties() message_id = self.connection.deq(self.book_queue_name, options, props, book) self.assertTrue(message_id is None) def test_2701_deq_enq(self): "2701 - test enqueuing and dequeuing multiple messages" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) options = self.connection.enqoptions() props = self.connection.msgproperties() for title, authors, price in self.book_data: book = books_type.newobject() book.TITLE = title book.AUTHORS = authors book.PRICE = price self.connection.enq(self.book_queue_name, options, props, book) options = self.connection.deqoptions() options.navigation = oracledb.DEQ_FIRST_MSG options.wait = oracledb.DEQ_NO_WAIT results = [] while self.connection.deq(self.book_queue_name, options, props, book): row = (book.TITLE, book.AUTHORS, book.PRICE) results.append(row) self.connection.commit() self.assertEqual(results, self.book_data) def test_2702_deq_mode_remove_no_data(self): "2702 - test dequeuing with DEQ_REMOVE_NODATA option" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() title, authors, price = self.book_data[1] book.TITLE = title book.AUTHORS = authors book.PRICE = price options = self.connection.enqoptions() props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, options, props, book) options = self.connection.deqoptions() options.navigation = oracledb.DEQ_FIRST_MSG options.wait = oracledb.DEQ_NO_WAIT options.mode = oracledb.DEQ_REMOVE_NODATA book = books_type.newobject() message_id = self.connection.deq(self.book_queue_name, options, props, book) self.connection.commit() self.assertTrue(message_id is not None) self.assertEqual(book.TITLE, "") def test_2703_deq_options(self): "2703 - test getting/setting dequeue options attributes" options = self.connection.deqoptions() self.__verify_attr(options, "condition", "TEST_CONDITION") self.__verify_attr(options, "consumername", "TEST_CONSUMERNAME") self.__verify_attr(options, "correlation", "TEST_CORRELATION") self.__verify_attr(options, "mode", oracledb.DEQ_LOCKED) self.__verify_attr(options, "navigation", oracledb.DEQ_NEXT_TRANSACTION) self.__verify_attr(options, "transformation", "TEST_TRANSFORMATION") self.__verify_attr(options, "visibility", oracledb.ENQ_IMMEDIATE) self.__verify_attr(options, "wait", 1287) self.__verify_attr(options, "msgid", b'mID') def test_2704_deq_with_wait(self): "2704 - test waiting for dequeue" self.__clear_books_queue() results = [] thread = threading.Thread(target=self.__deq_in_thread, args=(results,)) thread.start() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() title, authors, price = self.book_data[0] book.TITLE = title book.AUTHORS = authors book.PRICE = price options = self.connection.enqoptions() props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, options, props, book) self.connection.commit() thread.join() self.assertEqual(results, [(title, authors, price)]) def test_2705_enq_options(self): "2705 - test getting/setting enqueue options attributes" options = self.connection.enqoptions() self.__verify_attr(options, "visibility", oracledb.ENQ_IMMEDIATE) def test_2706_errors_for_invalid_values(self): "2706 - test errors for invalid values for options" books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() options = self.connection.enqoptions() props = self.connection.msgproperties() self.assertRaises(TypeError, self.connection.deq, self.book_queue_name, options, props, book) options = self.connection.deqoptions() self.assertRaises(TypeError, self.connection.enq, self.book_queue_name, options, props, book) def test_2707_msg_props(self): "2707 - test getting/setting message properties attributes" props = self.connection.msgproperties() self.__verify_attr(props, "correlation", "TEST_CORRELATION") self.__verify_attr(props, "delay", 60) self.__verify_attr(props, "exceptionq", "TEST_EXCEPTIONQ") self.__verify_attr(props, "expiration", 30) self.assertEqual(props.attempts, 0) self.__verify_attr(props, "priority", 1) self.assertEqual(props.state, oracledb.MSG_READY) self.assertEqual(props.deliverymode, 0) def test_2708_visibility_mode_commit(self): "2708 - test enqueue visibility option - ENQ_ON_COMMIT" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] enq_options = self.connection.enqoptions() enq_options.visibility = oracledb.ENQ_ON_COMMIT props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() message_id = other_connection.deq(self.book_queue_name, deq_options, props, book) self.assertTrue(message_id is None) self.connection.commit() message_id = other_connection.deq(self.book_queue_name, deq_options, props, book) self.assertTrue(message_id is not None) def test_2709_visibility_mode_immediate(self): "2709 - test enqueue visibility option - ENQ_IMMEDIATE" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] enq_options = self.connection.enqoptions() enq_options.visibility = oracledb.ENQ_IMMEDIATE props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.visibility = oracledb.DEQ_ON_COMMIT deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() other_connection.deq(self.book_queue_name, deq_options, props, book) results = (book.TITLE, book.AUTHORS, book.PRICE) other_connection.commit() self.assertEqual(results, self.book_data[0]) def test_2710_delivery_mode_same_buffered(self): "2710 - test enqueue/dequeue delivery modes identical - buffered" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] enq_options = self.connection.enqoptions() enq_options.deliverymode = oracledb.MSG_BUFFERED enq_options.visibility = oracledb.ENQ_IMMEDIATE props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.deliverymode = oracledb.MSG_BUFFERED deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.visibility = oracledb.DEQ_IMMEDIATE deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() other_connection.deq(self.book_queue_name, deq_options, props, book) results = (book.TITLE, book.AUTHORS, book.PRICE) other_connection.commit() self.assertEqual(results, self.book_data[0]) def test_2711_delivery_mode_same_persistent(self): "2711 - test enqueue/dequeue delivery modes identical - persistent" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] enq_options = self.connection.enqoptions() enq_options.deliverymode = oracledb.MSG_PERSISTENT enq_options.visibility = oracledb.ENQ_IMMEDIATE props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.deliverymode = oracledb.MSG_PERSISTENT deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.visibility = oracledb.DEQ_IMMEDIATE deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() other_connection.deq(self.book_queue_name, deq_options, props, book) results = (book.TITLE, book.AUTHORS, book.PRICE) other_connection.commit() self.assertEqual(results, self.book_data[0]) def test_2712_delivery_mode_same_persistent_buffered(self): "2712 - test enqueue/dequeue delivery modes the same" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] enq_options = self.connection.enqoptions() enq_options.deliverymode = oracledb.MSG_PERSISTENT_OR_BUFFERED enq_options.visibility = oracledb.ENQ_IMMEDIATE props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.deliverymode = oracledb.MSG_PERSISTENT_OR_BUFFERED deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.visibility = oracledb.DEQ_IMMEDIATE deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() other_connection.deq(self.book_queue_name, deq_options, props, book) results = (book.TITLE, book.AUTHORS, book.PRICE) other_connection.commit() self.assertEqual(results, self.book_data[0]) def test_2713_delivery_mode_different(self): "2713 - test enqueue/dequeue delivery modes different" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] enq_options = self.connection.enqoptions() enq_options.deliverymode = oracledb.MSG_BUFFERED enq_options.visibility = oracledb.ENQ_IMMEDIATE props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.deliverymode = oracledb.MSG_PERSISTENT deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.visibility = oracledb.DEQ_IMMEDIATE deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() message_id = other_connection.deq(self.book_queue_name, deq_options, props, book) self.assertTrue(message_id is None) def test_2714_dequeue_transformation(self): "2714 - test dequeue transformation" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] expectedPrice = book.PRICE + 10 enq_options = self.connection.enqoptions() props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) self.connection.commit() other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.visibility = oracledb.DEQ_IMMEDIATE deq_options.transformation = "%s.transform2" % self.connection.username deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() other_connection.deq(self.book_queue_name, deq_options, props, book) otherPrice = book.PRICE self.assertEqual(otherPrice, expectedPrice) def test_2715_enqueue_transformation(self): "2715 - test enqueue transformation" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] expectedPrice = book.PRICE + 5 enq_options = self.connection.enqoptions() enq_options.transformation = "%s.transform1" % self.connection.username props = self.connection.msgproperties() self.connection.enq(self.book_queue_name, enq_options, props, book) self.connection.commit() other_connection = test_env.get_connection() deq_options = other_connection.deqoptions() deq_options.navigation = oracledb.DEQ_FIRST_MSG deq_options.visibility = oracledb.DEQ_IMMEDIATE deq_options.wait = oracledb.DEQ_NO_WAIT books_type = other_connection.gettype(self.book_type_name) book = books_type.newobject() props = other_connection.msgproperties() other_connection.deq(self.book_queue_name, deq_options, props, book) otherPrice = book.PRICE self.assertEqual(otherPrice, expectedPrice) def test_2716_payloadType_deprecation(self): "2716 - test to verify payloadType is deprecated" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) self.assertRaises(oracledb.ProgrammingError, self.connection.queue, self.book_queue_name, books_type, payloadType=books_type) def test_2718_verify_msgid(self): "2718 - verify that the msgid property is returned correctly" self.__clear_books_queue() books_type = self.connection.gettype(self.book_type_name) book = books_type.newobject() book.TITLE, book.AUTHORS, book.PRICE = self.book_data[0] queue = self.connection.queue(self.book_queue_name, books_type) props = self.connection.msgproperties(payload=book) self.assertEqual(props.msgid, None) queue.enqone(props) self.cursor.execute("select msgid from book_queue_tab") actual_msgid, = self.cursor.fetchone() self.assertEqual(props.msgid, actual_msgid) props = queue.deqone() self.assertEqual(props.msgid, actual_msgid) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2800_bulk_aq.py000066400000000000000000000132021414105416400214150ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 2800 - Module for testing AQ Bulk enqueue/dequeue """ import decimal import threading import cx_Oracle as oracledb import test_env RAW_QUEUE_NAME = "TEST_RAW_QUEUE" RAW_PAYLOAD_DATA = [ "The first message", "The second message", "The third message", "The fourth message", "The fifth message", "The sixth message", "The seventh message", "The eighth message", "The ninth message", "The tenth message", "The eleventh message", "The twelfth and final message" ] class TestCase(test_env.BaseTestCase): def __deq_in_thread(self, results): connection = test_env.get_connection(threaded=True) queue = connection.queue(RAW_QUEUE_NAME) queue.deqoptions.wait = 10 queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG while len(results) < len(RAW_PAYLOAD_DATA): messages = queue.deqmany(5) if not messages: break for m in messages: results.append(m.payload.decode(connection.encoding)) connection.commit() def __get_and_clear_raw_queue(self): queue = self.connection.queue(RAW_QUEUE_NAME) queue.deqoptions.wait = oracledb.DEQ_NO_WAIT queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG while queue.deqone(): pass self.connection.commit() return queue def test_2800_enq_and_deq(self): "2800 - test bulk enqueue and dequeue" queue = self.__get_and_clear_raw_queue() messages = [self.connection.msgproperties(payload=d) \ for d in RAW_PAYLOAD_DATA] queue.enqmany(messages) messages = queue.deqmany(len(RAW_PAYLOAD_DATA)) data = [m.payload.decode(self.connection.encoding) for m in messages] self.connection.commit() self.assertEqual(data, RAW_PAYLOAD_DATA) def test_2801_dequeue_empty(self): "2801 - test empty bulk dequeue" queue = self.__get_and_clear_raw_queue() messages = queue.deqmany(5) self.connection.commit() self.assertEqual(messages, []) def test_2802_deq_with_wait(self): "2802 - test bulk dequeue with wait" queue = self.__get_and_clear_raw_queue() results = [] thread = threading.Thread(target=self.__deq_in_thread, args=(results,)) thread.start() messages = [self.connection.msgproperties(payload=d) \ for d in RAW_PAYLOAD_DATA] queue.enqoptions.visibility = oracledb.ENQ_IMMEDIATE queue.enqmany(messages) thread.join() self.assertEqual(results, RAW_PAYLOAD_DATA) def test_2803_enq_and_deq_multiple_times(self): "2803 - test enqueue and dequeue multiple times" queue = self.__get_and_clear_raw_queue() data_to_enqueue = RAW_PAYLOAD_DATA for num in (2, 6, 4): messages = [self.connection.msgproperties(payload=d) \ for d in data_to_enqueue[:num]] data_to_enqueue = data_to_enqueue[num:] queue.enqmany(messages) self.connection.commit() all_data = [] for num in (3, 5, 10): messages = queue.deqmany(num) all_data.extend(m.payload.decode(self.connection.encoding) \ for m in messages) self.connection.commit() self.assertEqual(all_data, RAW_PAYLOAD_DATA) def test_2804_enq_and_deq_visibility(self): "2804 - test visibility option for enqueue and dequeue" queue = self.__get_and_clear_raw_queue() # first test with ENQ_ON_COMMIT (commit required) queue.enqoptions.visibility = oracledb.ENQ_ON_COMMIT props1 = self.connection.msgproperties(payload="A first message") props2 = self.connection.msgproperties(payload="A second message") queue.enqmany([props1, props2]) other_connection = test_env.get_connection() other_queue = other_connection.queue(RAW_QUEUE_NAME) other_queue.deqoptions.wait = oracledb.DEQ_NO_WAIT other_queue.deqoptions.visibility = oracledb.DEQ_ON_COMMIT messages = other_queue.deqmany(5) self.assertEqual(len(messages), 0) self.connection.commit() messages = other_queue.deqmany(5) self.assertEqual(len(messages), 2) other_connection.rollback() # second test with ENQ_IMMEDIATE (no commit required) queue.enqoptions.visibility = oracledb.ENQ_IMMEDIATE other_queue.deqoptions.visibility = oracledb.DEQ_IMMEDIATE queue.enqmany([props1, props2]) messages = other_queue.deqmany(5) self.assertEqual(len(messages), 4) other_connection.rollback() messages = other_queue.deqmany(5) self.assertEqual(len(messages), 0) def test_2806_verify_msgid(self): "2806 - verify that the msgid property is returned correctly" queue = self.__get_and_clear_raw_queue() messages = [self.connection.msgproperties(payload=d) \ for d in RAW_PAYLOAD_DATA] queue.enqmany(messages) self.cursor.execute("select msgid from raw_queue_tab") actual_msgids = set(m for m, in self.cursor) msgids = set(m.msgid for m in messages) self.assertEqual(msgids, actual_msgids) messages = queue.deqmany(len(RAW_PAYLOAD_DATA)) msgids = set(m.msgid for m in messages) self.assertEqual(msgids, actual_msgids) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_2900_rowid.py000066400000000000000000000047111414105416400211310ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 2900 - Module for testing Rowids """ import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def __test_select_rowids(self, table_name): self.cursor.execute("select rowid, IntCol from %s""" % table_name) rowid_dict = dict(self.cursor) sql = "select IntCol from %s where rowid = :val" % table_name for rowid, int_val in rowid_dict.items(): self.cursor.execute(sql, val = rowid) rows = self.cursor.fetchall() self.assertEqual(len(rows), 1) self.assertEqual(rows[0][0], int_val) def test_2900_select_rowids_regular(self): "2900 - test selecting all rowids from a regular table" self.__test_select_rowids("TestNumbers") def test_2901_select_rowids_index_organised(self): "2901 - test selecting all rowids from an index organised table" self.__test_select_rowids("TestUniversalRowids") def test_2902_insert_invalid_rowid(self): "2902 - test inserting an invalid rowid" sql = "insert into TestRowids (IntCol, RowidCol) values (1, :rid)" self.assertRaises(oracledb.DatabaseError, self.cursor.execute, sql, rid=12345) self.assertRaises(oracledb.DatabaseError, self.cursor.execute, sql, rid="523lkhlf") def test_2903_insert_rowids(self): "2903 - test inserting rowids and verify they are inserted correctly" self.cursor.execute("select IntCol, rowid from TestNumbers") rows = self.cursor.fetchall() self.cursor.execute("truncate table TestRowids") self.cursor.executemany(""" insert into TestRowids (IntCol, RowidCol) values (:1, :2)""", rows) self.connection.commit() self.cursor.execute("select IntCol, RowidCol from TestRowids") rows = self.cursor.fetchall() sql = "select IntCol from TestNumbers where rowid = :val" for int_val, rowid in rows: self.cursor.execute(sql, val = rowid) rows = self.cursor.fetchall() self.assertEqual(len(rows), 1) self.assertEqual(rows[0][0], int_val) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3000_subscription.py000066400000000000000000000123041414105416400225160ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 3000 - Module for testing subscriptions """ import threading import cx_Oracle as oracledb import test_env class SubscriptionData(object): def __init__(self, num_messages_expected): self.condition = threading.Condition() self.num_messages_expected = num_messages_expected self.num_messages_received = 0 self.table_operations = [] self.row_operations = [] self.rowids = [] def CallbackHandler(self, message): if message.type != oracledb.EVENT_DEREG: table, = message.tables self.table_operations.append(table.operation) for row in table.rows: self.row_operations.append(row.operation) self.rowids.append(row.rowid) self.num_messages_received += 1 if message.type == oracledb.EVENT_DEREG or \ self.num_messages_received == self.num_messages_expected: self.condition.acquire() self.condition.notify() self.condition.release() class TestCase(test_env.BaseTestCase): def test_3000_subscription(self): "3000 - test Subscription for insert, update, delete and truncate" # skip if running on the Oracle Cloud, which does not support # subscriptions currently if self.is_on_oracle_cloud(): message = "Oracle Cloud does not support subscriptions currently" self.skipTest(message) # truncate table in order to run test in known state self.cursor.execute("truncate table TestTempTable") # expected values table_operations = [ oracledb.OPCODE_INSERT, oracledb.OPCODE_UPDATE, oracledb.OPCODE_INSERT, oracledb.OPCODE_DELETE, oracledb.OPCODE_ALTER | oracledb.OPCODE_ALLROWS ] row_operations = [ oracledb.OPCODE_INSERT, oracledb.OPCODE_UPDATE, oracledb.OPCODE_INSERT, oracledb.OPCODE_DELETE ] rowids = [] # set up subscription data = SubscriptionData(5) connection = test_env.get_connection(threaded=True, events=True) sub = connection.subscribe(callback=data.CallbackHandler, timeout=10, qos=oracledb.SUBSCR_QOS_ROWIDS) sub.registerquery("select * from TestTempTable") connection.autocommit = True cursor = connection.cursor() # insert statement cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (1, 'test')""") cursor.execute("select rowid from TestTempTable where IntCol = 1") rowids.extend(r for r, in cursor) # update statement cursor.execute(""" update TestTempTable set StringCol = 'update' where IntCol = 1""") cursor.execute("select rowid from TestTempTable where IntCol = 1") rowids.extend(r for r, in cursor) # second insert statement cursor.execute(""" insert into TestTempTable (IntCol, StringCol) values (2, 'test2')""") cursor.execute("select rowid from TestTempTable where IntCol = 2") rowids.extend(r for r, in cursor) # delete statement cursor.execute("delete TestTempTable where IntCol = 2") rowids.append(rowids[-1]) # truncate table cursor.execute("truncate table TestTempTable") # wait for all messages to be sent data.condition.acquire() data.condition.wait(10) # verify the correct messages were sent self.assertEqual(data.table_operations, table_operations) self.assertEqual(data.row_operations, row_operations) self.assertEqual(data.rowids, rowids) # test string format of subscription object is as expected fmt = ">" expected = fmt % \ (test_env.get_main_user(), test_env.get_connect_string()) self.assertEqual(str(sub), expected) def test_3001_deprecations(self): "3001 - test to verify deprecations" connection = test_env.get_connection(threaded=True, events=True) self.assertRaises(oracledb.ProgrammingError, connection.subscribe, ip_address='www.oracle.in', ipAddress='www.oracle.in') self.assertRaises(oracledb.ProgrammingError, connection.subscribe, grouping_class=1, groupingClass=1) self.assertRaises(oracledb.ProgrammingError, connection.subscribe, grouping_value=3, groupingValue=3) self.assertRaises(oracledb.ProgrammingError, connection.subscribe, grouping_type=2, groupingType=2) self.assertRaises(oracledb.ProgrammingError, connection.subscribe, client_initiated=True, clientInitiated=True) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3100_boolean_var.py000066400000000000000000000056711414105416400222730ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 3100 - Module for testing boolean variables """ import unittest import cx_Oracle as oracledb import test_env @unittest.skipUnless(test_env.get_client_version() >= (12, 1), "unsupported client") class TestCase(test_env.BaseTestCase): def __test_bind_value_as_boolean(self, value): expected_result = str(bool(value)).upper() var = self.cursor.var(bool) var.setvalue(0, value) result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str, (var,)) self.assertEqual(result, expected_result) def test_3100_bind_false(self): "3100 - test binding in a False value" result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str, (False,)) self.assertEqual(result, "FALSE") def test_3101_bind_float_as_boolean(self): "3101 - test binding in a float as a boolean" self.__test_bind_value_as_boolean(0.0) self.__test_bind_value_as_boolean(1.0) def test_3102_bind_integer_as_boolean(self): "3102 - test binding in an integer as a boolean" self.__test_bind_value_as_boolean(0) self.__test_bind_value_as_boolean(1) def test_3103_bind_null(self): "3103 - test binding in a null value" self.cursor.setinputsizes(None, bool) result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str, (None,)) self.assertEqual(result, "NULL") def test_3104_bind_out_false(self): "3104 - test binding out a boolean value (False)" result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10", oracledb.DB_TYPE_BOOLEAN, (15,)) self.assertEqual(result, False) def test_3105_bind_out_true(self): "3105 - test binding out a boolean value (True)" result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10", bool, (5,)) self.assertEqual(result, True) def test_3106_bind_string_as_boolean(self): "3106 - test binding in a string as a boolean" self.__test_bind_value_as_boolean("") self.__test_bind_value_as_boolean("0") def test_3107_bind_true(self): "3107 - test binding in a True value" result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str, (True,)) self.assertEqual(result, "TRUE") if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3200_features_12_1.py000066400000000000000000000517771414105416400223550ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ """ 3200 - Module for testing features introduced in 12.1 """ import datetime import unittest import cx_Oracle as oracledb import test_env @unittest.skipUnless(test_env.get_client_version() >= (12, 1), "unsupported client") class TestCase(test_env.BaseTestCase): def test_3200_array_dml_row_counts_off(self): "3200 - test executing with arraydmlrowcounts mode disabled" self.cursor.execute("truncate table TestArrayDML") rows = [(1, "First"), (2, "Second")] sql = "insert into TestArrayDML (IntCol,StringCol) values (:1,:2)" self.cursor.executemany(sql, rows, arraydmlrowcounts=False) self.assertRaises(oracledb.DatabaseError, self.cursor.getarraydmlrowcounts) rows = [(3, "Third"), (4, "Fourth")] self.cursor.executemany(sql, rows) self.assertRaises(oracledb.DatabaseError, self.cursor.getarraydmlrowcounts) def test_3201_array_dml_row_counts_on(self): "3201 - test executing with arraydmlrowcounts mode enabled" self.cursor.execute("truncate table TestArrayDML") rows = [ (1, "First", 100), (2, "Second", 200), (3, "Third", 300), (4, "Fourth", 300), (5, "Fifth", 300) ] sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \ "values (:1,:2,:3)" self.cursor.executemany(sql, rows, arraydmlrowcounts=True) self.connection.commit() self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 1, 1, 1]) self.cursor.execute("select count(*) from TestArrayDML") count, = self.cursor.fetchone() self.assertEqual(count, len(rows)) def test_3202_bind_plsql_boolean_collection_in(self): "3202 - test binding a boolean collection (in)" type_obj = self.connection.gettype("PKG_TESTBOOLEANS.UDT_BOOLEANLIST") obj = type_obj.newobject() obj.setelement(1, True) obj.extend([True, False, True, True, False, True]) result = self.cursor.callfunc("pkg_TestBooleans.TestInArrays", int, (obj,)) self.assertEqual(result, 5) def test_3203_bind_plsql_boolean_collection_out(self): "3203 - test binding a boolean collection (out)" type_obj = self.connection.gettype("PKG_TESTBOOLEANS.UDT_BOOLEANLIST") obj = type_obj.newobject() self.cursor.callproc("pkg_TestBooleans.TestOutArrays", (6, obj)) self.assertEqual(obj.aslist(), [True, False, True, False, True, False]) def test_3204_bind_plql_date_collection_in(self): "3204 - test binding a PL/SQL date collection (in)" type_obj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST") obj = type_obj.newobject() obj.setelement(1, datetime.datetime(2016, 2, 5)) obj.append(datetime.datetime(2016, 2, 8, 12, 15, 30)) obj.append(datetime.datetime(2016, 2, 12, 5, 44, 30)) result = self.cursor.callfunc("pkg_TestDateArrays.TestInArrays", oracledb.NUMBER, (2, datetime.datetime(2016, 2, 1), obj)) self.assertEqual(result, 24.75) def test_3205_bind_plqsl_date_collection_in_out(self): "3205 - test binding a PL/SQL date collection (in/out)" type_obj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST") obj = type_obj.newobject() obj.setelement(1, datetime.datetime(2016, 1, 1)) obj.append(datetime.datetime(2016, 1, 7)) obj.append(datetime.datetime(2016, 1, 13)) obj.append(datetime.datetime(2016, 1, 19)) self.cursor.callproc("pkg_TestDateArrays.TestInOutArrays", (4, obj)) expected_values = [ datetime.datetime(2016, 1, 8), datetime.datetime(2016, 1, 14), datetime.datetime(2016, 1, 20), datetime.datetime(2016, 1, 26) ] self.assertEqual(obj.aslist(), expected_values) def test_3206_bind_plsql_date_collection_out(self): "3206 - test binding a PL/SQL date collection (out)" type_obj = self.connection.gettype("PKG_TESTDATEARRAYS.UDT_DATELIST") obj = type_obj.newobject() self.cursor.callproc("pkg_TestDateArrays.TestOutArrays", (3, obj)) expected_values = [ datetime.datetime(2002, 12, 13, 4, 48), datetime.datetime(2002, 12, 14, 9, 36), datetime.datetime(2002, 12, 15, 14, 24) ] self.assertEqual(obj.aslist(), expected_values) def test_3207_bind_plsql_number_collection_in(self): "3207 - test binding a PL/SQL number collection (in)" type_name = "PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST" type_obj = self.connection.gettype(type_name) obj = type_obj.newobject() obj.setelement(1, 10) obj.extend([20, 30, 40, 50]) result = self.cursor.callfunc("pkg_TestNumberArrays.TestInArrays", int, (5, obj)) self.assertEqual(result, 155) def test_3208_bind_plsql_number_collection_in_out(self): "3208 - test binding a PL/SQL number collection (in/out)" type_name = "PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST" type_obj = self.connection.gettype(type_name) obj = type_obj.newobject() obj.setelement(1, 5) obj.extend([8, 3, 2]) self.cursor.callproc("pkg_TestNumberArrays.TestInOutArrays", (4, obj)) self.assertEqual(obj.aslist(), [50, 80, 30, 20]) def test_3209_bind_plsql_number_collection_out(self): "3209 - test binding a PL/SQL number collection (out)" type_name = "PKG_TESTNUMBERARRAYS.UDT_NUMBERLIST" type_obj = self.connection.gettype(type_name) obj = type_obj.newobject() self.cursor.callproc("pkg_TestNumberArrays.TestOutArrays", (3, obj)) self.assertEqual(obj.aslist(), [100, 200, 300]) def test_3210_bind_plsql_record_array(self): "3210 - test binding an array of PL/SQL records (in)" rec_type = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD") array_type = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORDARRAY") array_obj = array_type.newobject() for i in range(3): obj = rec_type.newobject() obj.NUMBERVALUE = i + 1 obj.STRINGVALUE = "String in record #%d" % (i + 1) obj.DATEVALUE = datetime.datetime(2017, i + 1, 1) obj.TIMESTAMPVALUE = datetime.datetime(2017, 1, i + 1) obj.BOOLEANVALUE = (i % 2) == 1 obj.PLSINTEGERVALUE = i * 5 obj.BINARYINTEGERVALUE = i * 2 array_obj.append(obj) result = self.cursor.callfunc("pkg_TestRecords.TestInArrays", str, (array_obj,)) self.assertEqual(result, "udt_Record(1, 'String in record #1', " \ "to_date('2017-01-01', 'YYYY-MM-DD'), " \ "to_timestamp('2017-01-01 00:00:00', " \ "'YYYY-MM-DD HH24:MI:SS'), false, 0, 0); " \ "udt_Record(2, 'String in record #2', " \ "to_date('2017-02-01', 'YYYY-MM-DD'), " \ "to_timestamp('2017-01-02 00:00:00', " \ "'YYYY-MM-DD HH24:MI:SS'), true, 5, 2); " \ "udt_Record(3, 'String in record #3', " \ "to_date('2017-03-01', 'YYYY-MM-DD'), " \ "to_timestamp('2017-01-03 00:00:00', " \ "'YYYY-MM-DD HH24:MI:SS'), false, 10, 4)") def test_3211_bind_plsql_record_in(self): "3211 - test binding a PL/SQL record (in)" type_obj = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD") obj = type_obj.newobject() obj.NUMBERVALUE = 18 obj.STRINGVALUE = "A string in a record" obj.DATEVALUE = datetime.datetime(2016, 2, 15) obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 12, 14, 25, 36) obj.BOOLEANVALUE = False obj.PLSINTEGERVALUE = 21 obj.BINARYINTEGERVALUE = 5 result = self.cursor.callfunc("pkg_TestRecords.GetStringRep", str, (obj,)) self.assertEqual(result, "udt_Record(18, 'A string in a record', " \ "to_date('2016-02-15', 'YYYY-MM-DD'), " \ "to_timestamp('2016-02-12 14:25:36', " \ "'YYYY-MM-DD HH24:MI:SS'), false, 21, 5)") def test_3212_bind_plsql_record_out(self): "3212 - test binding a PL/SQL record (out)" type_obj = self.connection.gettype("PKG_TESTRECORDS.UDT_RECORD") obj = type_obj.newobject() obj.NUMBERVALUE = 5 obj.STRINGVALUE = "Test value" obj.DATEVALUE = datetime.datetime.today() obj.TIMESTAMPVALUE = datetime.datetime.today() obj.BOOLEANVALUE = False obj.PLSINTEGERVALUE = 23 obj.BINARYINTEGERVALUE = 9 self.cursor.callproc("pkg_TestRecords.TestOut", (obj,)) self.assertEqual(obj.NUMBERVALUE, 25) self.assertEqual(obj.STRINGVALUE, "String in record") self.assertEqual(obj.DATEVALUE, datetime.datetime(2016, 2, 16)) self.assertEqual(obj.TIMESTAMPVALUE, datetime.datetime(2016, 2, 16, 18, 23, 55)) self.assertEqual(obj.BOOLEANVALUE, True) self.assertEqual(obj.PLSINTEGERVALUE, 45) self.assertEqual(obj.BINARYINTEGERVALUE, 10) def test_3213_bind_plsql_string_collection_in(self): "3213 - test binding a PL/SQL string collection (in)" type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST" type_obj = self.connection.gettype(type_name) obj = type_obj.newobject() obj.setelement(1, "First element") obj.setelement(2, "Second element") obj.setelement(3, "Third element") result = self.cursor.callfunc("pkg_TestStringArrays.TestInArrays", int, (5, obj)) self.assertEqual(result, 45) def test_3214_bind_plsql_string_collection_in_out(self): "3214 - test binding a PL/SQL string collection (in/out)" type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST" type_obj = self.connection.gettype(type_name) obj = type_obj.newobject() obj.setelement(1, "The first element") obj.append("The second element") obj.append("The third and final element") self.cursor.callproc("pkg_TestStringArrays.TestInOutArrays", (3, obj)) expected_values = [ 'Converted element # 1 originally had length 17', 'Converted element # 2 originally had length 18', 'Converted element # 3 originally had length 27' ] self.assertEqual(obj.aslist(), expected_values) def test_3215_bind_plsql_string_collection_out(self): "3215 - test binding a PL/SQL string collection (out)" type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST" type_obj = self.connection.gettype(type_name) obj = type_obj.newobject() self.cursor.callproc("pkg_TestStringArrays.TestOutArrays", (4, obj)) expected_values = [ 'Test out element # 1', 'Test out element # 2', 'Test out element # 3', 'Test out element # 4' ] self.assertEqual(obj.aslist(), expected_values) def test_3216_bind_plsql_string_collection_out_with_holes(self): "3216 - test binding a PL/SQL string collection (out with holes)" type_name = "PKG_TESTSTRINGARRAYS.UDT_STRINGLIST" type_obj = self.connection.gettype(type_name) obj = type_obj.newobject() self.cursor.callproc("pkg_TestStringArrays.TestIndexBy", (obj,)) self.assertEqual(obj.first(), -1048576) self.assertEqual(obj.last(), 8388608) self.assertEqual(obj.next(-576), 284) self.assertEqual(obj.prev(284), -576) self.assertEqual(obj.size(), 4) self.assertEqual(obj.exists(-576), True) self.assertEqual(obj.exists(-577), False) self.assertEqual(obj.getelement(284), 'Third element') expected_list = [ "First element", "Second element", "Third element", "Fourth element" ] self.assertEqual(obj.aslist(), expected_list) expected_dict = { -1048576: 'First element', -576: 'Second element', 284: 'Third element', 8388608: 'Fourth element' } self.assertEqual(obj.asdict(), expected_dict) obj.delete(-576) obj.delete(284) expected_list.pop(2) expected_list.pop(1) self.assertEqual(obj.aslist(), expected_list) expected_dict.pop(-576) expected_dict.pop(284) self.assertEqual(obj.asdict(), expected_dict) def test_3217_exception_in_iteration(self): "3217 - test executing with arraydmlrowcounts with exception" self.cursor.execute("truncate table TestArrayDML") rows = [ (1, "First"), (2, "Second"), (2, "Third"), (4, "Fourth") ] sql = "insert into TestArrayDML (IntCol,StringCol) values (:1,:2)" self.assertRaises(oracledb.DatabaseError, self.cursor.executemany, sql, rows, arraydmlrowcounts=True) self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1]) def test_3218_executing_delete(self): "3218 - test executing delete statement with arraydmlrowcount mode" self.cursor.execute("truncate table TestArrayDML") rows = [ (1, "First", 100), (2, "Second", 200), (3, "Third", 300), (4, "Fourth", 300), (5, "Fifth", 300), (6, "Sixth", 400), (7, "Seventh", 400), (8, "Eighth", 500) ] sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \ "values (:1, :2, :3)" self.cursor.executemany(sql, rows) rows = [(200,), (300,), (400,)] statement = "delete from TestArrayDML where IntCol2 = :1" self.cursor.executemany(statement, rows, arraydmlrowcounts=True) self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 3, 2]) self.assertEqual(self.cursor.rowcount, 6) def test_3219_executing_update(self): "3219 - test executing update statement with arraydmlrowcount mode" self.cursor.execute("truncate table TestArrayDML") rows = [ (1, "First",100), (2, "Second",200), (3, "Third",300), (4, "Fourth",300), (5, "Fifth",300), (6, "Sixth",400), (7, "Seventh",400), (8, "Eighth",500) ] sql = "insert into TestArrayDML (IntCol,StringCol,IntCol2) " \ "values (:1, :2, :3)" self.cursor.executemany(sql, rows) rows = [ ("One", 100), ("Two", 200), ("Three", 300), ("Four", 400) ] sql = "update TestArrayDML set StringCol = :1 where IntCol2 = :2" self.cursor.executemany(sql, rows, arraydmlrowcounts=True) self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 3, 2]) self.assertEqual(self.cursor.rowcount, 7) def test_3220_implicit_results(self): "3220 - test getimplicitresults() returns the correct data" self.cursor.execute(""" declare c1 sys_refcursor; c2 sys_refcursor; begin open c1 for select NumberCol from TestNumbers where IntCol between 3 and 5; dbms_sql.return_result(c1); open c2 for select NumberCol from TestNumbers where IntCol between 7 and 10; dbms_sql.return_result(c2); end;""") results = self.cursor.getimplicitresults() self.assertEqual(len(results), 2) self.assertEqual([n for n, in results[0]], [3.75, 5, 6.25]) self.assertEqual([n for n, in results[1]], [8.75, 10, 11.25, 12.5]) def test_3221_implicit_results_no_statement(self): "3221 - test getimplicitresults() without executing a statement" self.assertRaises(oracledb.InterfaceError, self.cursor.getimplicitresults) def test_3222_insert_with_batch_error(self): "3222 - test executing insert with multiple distinct batch errors" self.cursor.execute("truncate table TestArrayDML") rows = [ (1, "First", 100), (2, "Second", 200), (2, "Third", 300), (4, "Fourth", 400), (5, "Fourth", 1000) ] sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \ "values (:1, :2, :3)" self.cursor.executemany(sql, rows, batcherrors=True, arraydmlrowcounts=True) user = test_env.get_main_user() expected_errors = [ ( 4, 1438, "ORA-01438: value larger than specified " \ "precision allowed for this column" ), ( 2, 1, "ORA-00001: unique constraint " \ "(%s.TESTARRAYDML_PK) violated" % user.upper()) ] actual_errors = [(e.offset, e.code, e.message) \ for e in self.cursor.getbatcherrors()] self.assertEqual(actual_errors, expected_errors) self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 1, 0, 1, 0]) def test_3223_batch_error_false(self): "3223 - test batcherrors mode set to False" self.cursor.execute("truncate table TestArrayDML") rows = [ (1, "First", 100), (2, "Second", 200), (2, "Third", 300) ] sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \ "values (:1, :2, :3)" self.assertRaises(oracledb.IntegrityError, self.cursor.executemany, sql, rows, batcherrors=False) def test_3224_update_with_batch_error(self): "3224 - test executing in succession with batch error" self.cursor.execute("truncate table TestArrayDML") rows = [ (1, "First", 100), (2, "Second", 200), (3, "Third", 300), (4, "Second", 300), (5, "Fifth", 300), (6, "Sixth", 400), (6, "Seventh", 400), (8, "Eighth", 100) ] sql = "insert into TestArrayDML (IntCol, StringCol, IntCol2) " \ "values (:1, :2, :3)" self.cursor.executemany(sql, rows, batcherrors=True) user = test_env.get_main_user() expected_errors = [ ( 6, 1, "ORA-00001: unique constraint " \ "(%s.TESTARRAYDML_PK) violated" % user.upper()) ] actual_errors = [(e.offset, e.code, e.message) \ for e in self.cursor.getbatcherrors()] self.assertEqual(actual_errors, expected_errors) rows = [ (101, "First"), (201, "Second"), (3000, "Third"), (900, "Ninth"), (301, "Third") ] sql = "update TestArrayDML set IntCol2 = :1 where StringCol = :2" self.cursor.executemany(sql, rows, arraydmlrowcounts=True, batcherrors=True) expected_errors = [ (2, 1438, "ORA-01438: value larger than specified " \ "precision allowed for this column") ] actual_errors = [(e.offset, e.code, e.message) \ for e in self.cursor.getbatcherrors()] self.assertEqual(actual_errors, expected_errors) self.assertEqual(self.cursor.getarraydmlrowcounts(), [1, 2, 0, 0, 1]) self.assertEqual(self.cursor.rowcount, 4) def test_3225_implicit_results(self): "3225 - test using implicit cursors to execute new statements" cursor = self.connection.cursor() cursor.execute(""" declare c1 sys_refcursor; begin open c1 for select NumberCol from TestNumbers where IntCol between 3 and 5; dbms_sql.return_result(c1); end;""") results = cursor.getimplicitresults() self.assertEqual(len(results), 1) self.assertEqual([n for n, in results[0]], [3.75, 5, 6.25]) results[0].execute("select :1 from dual", (7,)) row, = results[0].fetchone() self.assertEqual(row, 7) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3300_soda_database.py000066400000000000000000000112461414105416400225530ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 3300 - Module for testing Simple Oracle Document Access (SODA) Database """ import json import unittest import cx_Oracle as oracledb import test_env @unittest.skipIf(test_env.skip_soda_tests(), "unsupported client/server combination") class TestCase(test_env.BaseTestCase): def __drop_existing_collections(self, soda_db): for name in soda_db.getCollectionNames(): soda_db.openCollection(name).drop() def __verify_doc(self, doc, raw_content, str_content=None, content=None, key=None, media_type='application/json'): self.assertEqual(doc.getContentAsBytes(), raw_content) if str_content is not None: self.assertEqual(doc.getContentAsString(), str_content) if content is not None: self.assertEqual(doc.getContent(), content) self.assertEqual(doc.key, key) self.assertEqual(doc.mediaType, media_type) def test_3300_create_document_with_json(self): "3300 - test creating documents with JSON data" soda_db = self.connection.getSodaDatabase() val = {"testKey1": "testValue1", "testKey2": "testValue2"} str_val = json.dumps(val) bytes_val = str_val.encode() key = "MyKey" media_type = "text/plain" doc = soda_db.createDocument(val) self.__verify_doc(doc, bytes_val, str_val, val) doc = soda_db.createDocument(str_val, key) self.__verify_doc(doc, bytes_val, str_val, val, key) doc = soda_db.createDocument(bytes_val, key, media_type) self.__verify_doc(doc, bytes_val, str_val, val, key, media_type) def test_3301_create_document_with_raw(self): "3301 - test creating documents with raw data" soda_db = self.connection.getSodaDatabase() val = b"" key = "MyRawKey" media_type = "text/html" doc = soda_db.createDocument(val) self.__verify_doc(doc, val) doc = soda_db.createDocument(val, key) self.__verify_doc(doc, val, key=key) doc = soda_db.createDocument(val, key, media_type) self.__verify_doc(doc, val, key=key, media_type=media_type) def test_3302_get_collection_names(self): "3302 - test getting collection names from the database" soda_db = self.connection.getSodaDatabase() self.__drop_existing_collections(soda_db) self.assertEqual(soda_db.getCollectionNames(), []) names = ["zCol", "dCol", "sCol", "aCol", "gCol"] sorted_names = list(sorted(names)) for name in names: soda_db.createCollection(name) self.assertEqual(soda_db.getCollectionNames(), sorted_names) self.assertEqual(soda_db.getCollectionNames(limit=2), sorted_names[:2]) self.assertEqual(soda_db.getCollectionNames("a"), sorted_names) self.assertEqual(soda_db.getCollectionNames("C"), sorted_names) self.assertEqual(soda_db.getCollectionNames("b", limit=3), sorted_names[1:4]) self.assertEqual(soda_db.getCollectionNames("z"), sorted_names[-1:]) def test_3303_open_collection(self): "3303 - test opening a collection" soda_db = self.connection.getSodaDatabase() self.__drop_existing_collections(soda_db) coll = soda_db.openCollection("CollectionThatDoesNotExist") self.assertEqual(coll, None) created_coll = soda_db.createCollection("TestOpenCollection") coll = soda_db.openCollection(created_coll.name) self.assertEqual(coll.name, created_coll.name) coll.drop() def test_3304_repr(self): "3304 - test SodaDatabase representation" con1 = self.connection con2 = test_env.get_connection() soda_db1 = self.connection.getSodaDatabase() soda_db2 = con1.getSodaDatabase() soda_db3 = con2.getSodaDatabase() self.assertEqual(str(soda_db1), str(soda_db2)) self.assertEqual(str(soda_db2), str(soda_db3)) def test_3305_negative(self): "3305 - test negative cases for SODA database methods" soda_db = self.connection.getSodaDatabase() self.assertRaises(TypeError, soda_db.createCollection) self.assertRaises(TypeError, soda_db.createCollection, 1) self.assertRaises(oracledb.DatabaseError, soda_db.createCollection, None) self.assertRaises(TypeError, soda_db.getCollectionNames, 1) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3400_soda_collection.py000066400000000000000000000351151414105416400231440ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 3400 - Module for testing Simple Oracle Document Access (SODA) Collections """ import unittest import cx_Oracle as oracledb import test_env @unittest.skipIf(test_env.skip_soda_tests(), "unsupported client/server combination") class TestCase(test_env.BaseTestCase): def __test_skip(self, coll, num_to_skip, expected_content): filter_spec = {'$orderby': [{'path': 'name', 'order': 'desc'}]} doc = coll.find().filter(filter_spec).skip(num_to_skip).getOne() content = doc.getContent() if doc is not None else None self.assertEqual(content, expected_content) def test_3400_invalid_json(self): "3400 - test inserting invalid JSON value into SODA collection" invalid_json = "{testKey:testValue}" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("InvalidJSON") doc = soda_db.createDocument(invalid_json) self.assertRaises(oracledb.DatabaseError, coll.insertOne, doc) coll.drop() def test_3401_insert_documents(self): "3401 - test inserting documents into a SODA collection" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoInsertDocs") coll.find().remove() values_to_insert = [ {"name": "George", "age": 47}, {"name": "Susan", "age": 39}, {"name": "John", "age": 50}, {"name": "Jill", "age": 54} ] inserted_keys = [] for value in values_to_insert: doc = coll.insertOneAndGet(value) inserted_keys.append(doc.key) self.connection.commit() self.assertEqual(coll.find().count(), len(values_to_insert)) for key, value in zip(inserted_keys, values_to_insert): doc = coll.find().key(key).getOne() self.assertEqual(doc.getContent(), value) coll.drop() def test_3402_skip_documents(self): "3402 - test skipping documents in a SODA collection" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoSkipDocs") coll.find().remove() values_to_insert = [ {"name": "Anna", "age": 62}, {"name": "Mark", "age": 37}, {"name": "Martha", "age": 43}, {"name": "Matthew", "age": 28} ] for value in values_to_insert: coll.insertOne(value) self.connection.commit() self.__test_skip(coll, 0, values_to_insert[3]) self.__test_skip(coll, 1, values_to_insert[2]) self.__test_skip(coll, 3, values_to_insert[0]) self.__test_skip(coll, 4, None) self.__test_skip(coll, 125, None) def test_3403_replace_document(self): "3403 - test replace documents in SODA collection" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoReplaceDoc") coll.find().remove() content = {'name': 'John', 'address': {'city': 'Sydney'}} doc = coll.insertOneAndGet(content) new_content = {'name': 'John', 'address': {'city':'Melbourne'}} coll.find().key(doc.key).replaceOne(new_content) self.connection.commit() self.assertEqual(coll.find().key(doc.key).getOne().getContent(), new_content) coll.drop() def test_3404_search_documents_with_content(self): "3404 - test search documents with content using $like and $regex" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoSearchDocContent") coll.find().remove() data = [ {'name': 'John', 'address': {'city': 'Bangalore'}}, {'name': 'Johnson', 'address': {'city': 'Banaras'}}, {'name': 'Joseph', 'address': {'city': 'Bangalore'}}, {'name': 'Jibin', 'address': {'city': 'Secunderabad'}}, {'name': 'Andrew', 'address': {'city': 'Hyderabad'}}, {'name': 'Matthew', 'address': {'city': 'Mumbai'}} ] for value in data: coll.insertOne(value) self.connection.commit() filter_specs = [ ({'name': {'$like': 'And%'}}, 1), ({'name': {'$like': 'J%n'}}, 3), ({'name': {'$like': '%hn%'}}, 2), ({'address.city': {'$like': 'Ban%'}}, 3), ({'address.city': {'$like': '%bad'}}, 2), ({'address.city': {'$like': 'Hyderabad'}}, 1), ({'address.city': {'$like': 'China%'}}, 0), ({'name': {'$regex': 'Jo.*'}}, 3), ({'name': {'$regex': '.*[ho]n'}}, 2), ({'name': {'$regex': 'J.*h'}}, 1), ({'address.city': {'$regex': 'Ba.*'}}, 3), ({'address.city': {'$regex': '.*bad'}}, 2), ({'address.city': {'$regex': 'Hyderabad'}}, 1), ({'name': {'$regex': 'Js.*n'}}, 0) ] for filter_spec, expected_count in filter_specs: self.assertEqual(coll.find().filter(filter_spec).count(), expected_count, filter_spec) coll.drop() def test_3405_document_remove(self): "3405 - test removing documents" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoRemoveDocs") coll.find().remove() data = [ {'name': 'John', 'address': {'city': 'Bangalore'}}, {'name': 'Johnson', 'address': {'city': 'Banaras'}}, {'name': 'Joseph', 'address': {'city': 'Mangalore'}}, {'name': 'Jibin', 'address': {'city': 'Secunderabad'}}, {'name': 'Andrew', 'address': {'city': 'Hyderabad'}}, {'name': 'Matthew', 'address': {'city': 'Mumbai'}} ] docs = [coll.insertOneAndGet(v) for v in data] coll.find().key(docs[3].key).remove() self.assertEqual(coll.find().count(), len(data) - 1) searchResults = coll.find().filter({'name': {'$like': 'Jibin'}}) self.assertEqual(searchResults.count(), 0) coll.find().filter({'name': {'$like': 'John%'}}).remove() self.assertEqual(coll.find().count(), len(data) - 3) coll.find().filter({'name': {'$regex': 'J.*'}}).remove() self.assertEqual(coll.find().count(), len(data) - 4) self.connection.commit() coll.drop() def test_3406_create_and_drop_index(self): "3406 - test create and drop Index" index_name = "cxoTestIndexes_ix_1" index_spec = { 'name': index_name, 'fields': [ { 'path': 'address.city', 'datatype': 'string', 'order': 'asc' } ] } soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("TestIndexes") coll.find().remove() self.connection.commit() coll.dropIndex(index_name) coll.createIndex(index_spec) self.assertRaises(oracledb.DatabaseError, coll.createIndex, index_spec) self.assertEqual(coll.dropIndex(index_name), True) self.assertEqual(coll.dropIndex(index_name), False) coll.drop() def test_3407_get_documents(self): "3407 - test getting documents from Collection" self.connection.autocommit = True soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoTestGetDocs") coll.find().remove() data = [ {'name': 'John', 'address': {'city': 'Bangalore'}}, {'name': 'Johnson', 'address': {'city': 'Banaras'}}, {'name': 'Joseph', 'address': {'city': 'Mangalore'}}, {'name': 'Jibin', 'address': {'city': 'Secunderabad'}}, {'name': 'Andrew', 'address': {'city': 'Hyderabad'}} ] inserted_keys = list(sorted(coll.insertOneAndGet(v).key for v in data)) fetched_keys = list(sorted(d.key for d in coll.find().getDocuments())) self.assertEqual(fetched_keys, inserted_keys) coll.drop() def test_3408_cursor(self): "3408 - test fetching documents from a cursor" self.connection.autocommit = True soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoFindViaCursor") coll.find().remove() data = [ {'name': 'John', 'address': {'city': 'Bangalore'}}, {'name': 'Johnson', 'address': {'city': 'Banaras'}}, {'name': 'Joseph', 'address': {'city': 'Mangalore'}}, ] inserted_keys = list(sorted(coll.insertOneAndGet(v).key for v in data)) fetched_keys = list(sorted(d.key for d in coll.find().getCursor())) self.assertEqual(fetched_keys, inserted_keys) coll.drop() def test_3409_multiple_document_remove(self): "3409 - test removing multiple documents using multiple keys" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoRemoveMultipleDocs") coll.find().remove() data = [ {'name': 'John', 'address': {'city': 'Bangalore'}}, {'name': 'Johnson', 'address': {'city': 'Banaras'}}, {'name': 'Joseph', 'address': {'city': 'Mangalore'}}, {'name': 'Jibin', 'address': {'city': 'Secunderabad'}}, {'name': 'Andrew', 'address': {'city': 'Hyderabad'}}, {'name': 'Matthew', 'address': {'city': 'Mumbai'}} ] docs = [coll.insertOneAndGet(v) for v in data] keys = [docs[i].key for i in (1, 3, 5)] num_removed = coll.find().keys(keys).remove() self.assertEqual(num_removed, len(keys)) self.assertEqual(coll.find().count(), len(data) - len(keys)) self.connection.commit() coll.drop() def test_3410_document_version(self): "3410 - test using version to get documents and remove them" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoDocumentVersion") coll.find().remove() content = {'name': 'John', 'address': {'city': 'Bangalore'}} inserted_doc = coll.insertOneAndGet(content) key = inserted_doc.key version = inserted_doc.version doc = coll.find().key(key).version(version).getOne() self.assertEqual(doc.getContent(), content) new_content = {'name': 'James', 'address': {'city': 'Delhi'}} replacedDoc = coll.find().key(key).replaceOneAndGet(new_content) new_version = replacedDoc.version doc = coll.find().key(key).version(version).getOne() self.assertEqual(doc, None) doc = coll.find().key(key).version(new_version).getOne() self.assertEqual(doc.getContent(), new_content) self.assertEqual(coll.find().key(key).version(version).remove(), 0) self.assertEqual(coll.find().key(key).version(new_version).remove(), 1) self.assertEqual(coll.find().count(), 0) self.connection.commit() coll.drop() def test_3411_get_cursor(self): "3411 - test keys with GetCursor" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoKeysWithGetCursor") coll.find().remove() data = [ {'name': 'John', 'address': {'city': 'Bangalore'}}, {'name': 'Johnson', 'address': {'city': 'Banaras'}}, {'name': 'Joseph', 'address': {'city': 'Mangalore'}}, {'name': 'Jibin', 'address': {'city': 'Secunderabad'}}, {'name': 'Andrew', 'address': {'city': 'Hyderabad'}}, {'name': 'Matthew', 'address': {'city': 'Mumbai'}} ] docs = [coll.insertOneAndGet(v) for v in data] keys = [docs[i].key for i in (2, 4, 5)] fetched_keys = [d.key for d in coll.find().keys(keys).getCursor()] self.assertEqual(list(sorted(fetched_keys)), list(sorted(keys))) self.connection.commit() coll.drop() def test_3412_created_on(self): "3412 - test createdOn attribute of Document" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("CreatedOn") coll.find().remove() data = {'name': 'John', 'address': {'city': 'Bangalore'}} doc = coll.insertOneAndGet(data) self.assertEqual(doc.createdOn, doc.lastModified) @unittest.skipIf(test_env.get_client_version() < (20, 1), "unsupported client") def test_3413_soda_truncate(self): "3413 - test Soda truncate" soda_db = self.connection.getSodaDatabase() coll = soda_db.createCollection("cxoTruncateDocs") coll.find().remove() values_to_insert = [ {"name": "George", "age": 47}, {"name": "Susan", "age": 39}, {"name": "John", "age": 50}, {"name": "Jill", "age": 54} ] for value in values_to_insert: coll.insertOne(value) self.connection.commit() self.assertEqual(coll.find().count(), len(values_to_insert)) coll.truncate() self.assertEqual(coll.find().count(), 0) coll.drop() @unittest.skipIf(test_env.skip_client_version_old_multi((19, 11), (21, 3)), "unsupported client") def test_3414_soda_hint(self): "3414 - verify hints are reflected in the executed SQL statement" soda_db = self.connection.getSodaDatabase() cursor = self.connection.cursor() statement = """ SELECT ( SELECT t2.sql_fulltext FROM v$sql t2 WHERE t2.sql_id = t1.prev_sql_id AND t2.child_number = t1.prev_child_number ) FROM v$session t1 WHERE t1.audsid = sys_context('userenv', 'sessionid')""" coll = soda_db.createCollection("cxoSodaHint") coll.find().remove() values_to_insert = [ {"name": "George", "age": 47}, {"name": "Susan", "age": 39}, ] coll.insertOneAndGet(values_to_insert[0], hint="MONITOR") cursor.execute(statement) result, = cursor.fetchone() self.assertTrue('MONITOR' in result.read()) coll.find().hint("MONITOR").getOne().getContent() cursor.execute(statement) result, = cursor.fetchone() self.assertTrue('MONITOR' in result.read()) coll.insertOneAndGet(values_to_insert[1], hint="NO_MONITOR") cursor.execute(statement) result, = cursor.fetchone() self.assertTrue('NO_MONITOR' in result.read()) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3500_json.py000066400000000000000000000140531414105416400207530ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 3500 - Module for testing the JSON data type. """ import datetime import decimal import unittest import cx_Oracle as oracledb import test_env @unittest.skipUnless(test_env.get_client_version() >= (21, 0), "unsupported client") @unittest.skipUnless(test_env.get_server_version() >= (21, 0), "unsupported server") class TestCase(test_env.BaseTestCase): json_data = [ True, False, 'String', b'Some Bytes', {}, {"name": None}, {"name": "John"}, {"age": 30}, {"Permanent": True}, { "employee": { "name":"John", "age": 30, "city": "Delhi", "Parmanent": True } }, { "employees": ["John", "Matthew", "James"] }, { "employees": [ { "employee1": {"name": "John", "city": "Delhi"} }, { "employee2": {"name": "Matthew", "city": "Mumbai"} }, { "employee3": {"name": "James", "city": "Bangalore"} } ] } ] def __bind_scalar_as_json(self, data): self.cursor.execute("truncate table TestJson") out_var = self.cursor.var(oracledb.DB_TYPE_JSON, arraysize=len(data)) self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON, out_var) bind_data = list(enumerate(data)) self.cursor.executemany(""" insert into TestJson values (:1, :2) returning JsonCol into :json_out""", bind_data) self.connection.commit() self.assertEqual(out_var.values, [[v] for v in data]) def test_3500_insert_and_fetch_single_json(self): "3500 - insert and fetch single row with JSON" self.cursor.execute("truncate table TestJson") self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON) self.cursor.execute("insert into TestJson values (:1, :2)", [1, self.json_data]) self.cursor.execute("select JsonCol from TestJson") result, = self.cursor.fetchone() self.assertEqual(result, self.json_data) def test_3501_execute_with_dml_returning(self): "3501 - inserting single rows with JSON and DML returning" json_val = self.json_data[11] self.cursor.execute("truncate table TestJson") json_out = self.cursor.var(oracledb.DB_TYPE_JSON) self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON, json_out) self.cursor.execute(""" insert into TestJson values (:1, :2) returning JsonCol into :json_out""", [1, json_val]) self.assertEqual(json_out.getvalue(0), [json_val]) def test_3502_insert_and_fetch_multiple_json(self): "3502 - insert and fetch multiple rows with JSON" self.cursor.execute("truncate table TestJson") self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON) data = list(enumerate(self.json_data)) self.cursor.executemany("insert into TestJson values(:1, :2)", data) self.cursor.execute("select * from TestJson") fetched_data = self.cursor.fetchall() self.assertEqual(fetched_data, data) def test_3503_executemany_with_dml_returning(self): "3503 - inserting multiple rows with JSON and DML returning" self.cursor.execute("truncate table TestJson") int_values = [i for i in range(len(self.json_data))] out_int_var = self.cursor.var(int, arraysize=len(int_values)) out_json_var = self.cursor.var(oracledb.DB_TYPE_JSON, arraysize=len(int_values)) self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON, out_int_var, out_json_var) data = list(zip(int_values, self.json_data)) self.cursor.executemany(""" insert into TestJson values(:int_val, :json_val) returning IntCol, JsonCol into :int_var, :json_var""", data) self.assertEqual(out_int_var.values, [[v] for v in int_values]) self.assertEqual(out_json_var.values, [[v] for v in self.json_data]) def test_3504_boolean(self): "3504 - test binding boolean values as scalar JSON values" data = [ True, False, True, True, False, True ] self.__bind_scalar_as_json(data) def test_3505_strings_and_bytes(self): "3505 - test binding strings/bytes values as scalar JSON values" data = [ "String 1", b"A raw value", "A much longer string", b"A much longer RAW value", "Short string", b"Y" ] self.__bind_scalar_as_json(data) def test_3506_datetime(self): "3506 - test binding dates/intervals as scalar JSON values" data = [ datetime.datetime.today(), datetime.datetime(2004, 2, 1, 3, 4, 5), datetime.datetime(2020, 12, 2, 13, 29, 14), datetime.timedelta(8.5), datetime.datetime(2002, 12, 13, 9, 36, 0), oracledb.Timestamp(2002, 12, 13, 9, 36, 0), datetime.datetime(2002, 12, 13) ] self.__bind_scalar_as_json(data) def test_3507_bind_number(self): "3507 - test binding number in json values" data = [ 0, 1, 25.25, 6088343244, -9999999999999999999, decimal.Decimal("0.25"), decimal.Decimal("10.25"), decimal.Decimal("319438950232418390.273596") ] self.__bind_scalar_as_json(data) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3600_outputtypehandler.py000066400000000000000000000506761414105416400236160ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 3600 - Module for testing the conversions of outputtype handler. """ import datetime import decimal import sys import unittest import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def __test_type_handler(self, input_type, output_type, in_value, expected_out_value): def type_handler(cursor, name, default_type, size, precision, scale): return cursor.var(output_type, arraysize=cursor.arraysize) self.cursor.outputtypehandler = type_handler var = self.cursor.var(input_type) var.setvalue(0, in_value) self.cursor.execute("select :1 from dual", [var]) fetched_value, = self.cursor.fetchone() self.assertEqual(fetched_value, expected_out_value) def __test_type_handler_blob(self, output_type): def type_handler(cursor, name, default_type, size, precision, scale): if default_type == oracledb.DB_TYPE_BLOB: return cursor.var(output_type, arraysize=cursor.arraysize) self.cursor.outputtypehandler = type_handler in_value = b"Some binary data" self.cursor.execute("truncate table TestBLOBs") self.cursor.execute("insert into TestBLOBs values(1, :val)", val=in_value) self.connection.commit() self.cursor.execute("select BlobCol, IntCol, BlobCol from TestBLOBs") self.assertEqual(self.cursor.fetchone(), (in_value, 1, in_value)) def __test_type_handler_clob(self, output_type): def type_handler(cursor, name, default_type, size, precision, scale): if default_type == oracledb.DB_TYPE_CLOB: return cursor.var(output_type, arraysize=cursor.arraysize) self.cursor.outputtypehandler = type_handler in_value = "Some clob data" self.cursor.execute("truncate table TestCLOBs") self.cursor.execute("insert into TestCLOBs values(1, :val)", val=in_value) self.connection.commit() self.cursor.execute("select ClobCol, IntCol, ClobCol from TestCLOBs") self.assertEqual(self.cursor.fetchone(), (in_value, 1, in_value)) def __test_type_handler_nclob(self, output_type): def type_handler(cursor, name, default_type, size, precision, scale): if default_type == oracledb.DB_TYPE_NCLOB: return cursor.var(output_type, arraysize=cursor.arraysize) self.cursor.outputtypehandler = type_handler in_value = "Some nclob data" self.cursor.execute("truncate table TestNCLOBs") self.cursor.execute("insert into TestNCLOBs values(1, :val)", val=in_value) self.connection.commit() self.cursor.execute("select NClobCol, IntCol, NClobCol from TestNCLOBs") self.assertEqual(self.cursor.fetchone(), (in_value, 1, in_value)) def setUp(self): super().setUp() stmt = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" \ "NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF6'" \ "NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF6'" \ "time_zone='Europe/London'" self.cursor.execute(stmt) def test_3600_VARCHAR_to_NUMBER(self): "3600 - outtypehandler conversion from varchar to number" self.__test_type_handler(oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_NUMBER, "31.5", 31.5) def test_3601_CHAR_to_NUMBER(self): "3601 - outtypehandler conversion from char to number" self.__test_type_handler(oracledb.DB_TYPE_CHAR, oracledb.DB_TYPE_NUMBER, "31.5", 31.5) def test_3602_LONG_to_NUMBER(self): "3602 - outtypehandler conversion from long to number" self.__test_type_handler(oracledb.DB_TYPE_LONG, oracledb.DB_TYPE_NUMBER, "31.5", 31.5) def test_3603_BINT_to_NUMBER(self): "3603 -test outtypehandler conversion from bint to number" self.__test_type_handler(oracledb.DB_TYPE_BINARY_INTEGER, oracledb.DB_TYPE_NUMBER, 31, 31) def test_3604_VARCHAR_to_BINT(self): "3604 outtypehandler conversion from varchar to bint" self.__test_type_handler(oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_BINARY_INTEGER, "31.5", 31) def test_3605_CHAR_to_BINT(self): "3605 outtypehandler conversion from char to bint" self.__test_type_handler(oracledb.DB_TYPE_CHAR, oracledb.DB_TYPE_BINARY_INTEGER, "31.5", 31) def test_3606_LONG_to_BINT(self): "3606 outtypehandler conversion from long to bint" self.__test_type_handler(oracledb.DB_TYPE_LONG, oracledb.DB_TYPE_BINARY_INTEGER, "31.5", 31) def test_3607_NUMBER_to_BINT(self): "3607 outtypehandler conversion from number to bint" self.__test_type_handler(oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_BINARY_INTEGER, 31.5, 31) def test_3608_BINARY_DOUBLE_to_BINT(self): "3608 outtypehandler conversion from binary double to bint" self.__test_type_handler(oracledb.DB_TYPE_BINARY_DOUBLE, oracledb.DB_TYPE_BINARY_INTEGER, 31.5, 31) def test_3609_BINARY_FLOAT_to_BINT(self): "3609 outtypehandler conversion from varchar to bint" self.__test_type_handler(oracledb.DB_TYPE_BINARY_FLOAT, oracledb.DB_TYPE_BINARY_INTEGER, 31.5, 31) def test_3610_DATE_to_VARCHAR(self): "3610 outtypehandler conversion from date to varchar" in_val = datetime.date(2021, 2, 1) out_val = "2021-02-01 00:00:00" self.__test_type_handler(oracledb.DB_TYPE_DATE, oracledb.DB_TYPE_VARCHAR, in_val, out_val) def test_3611_DATE_to_CHAR(self): "3611 outtypehandler conversion from date to char" in_val = datetime.date(2021, 2, 1) out_val = "2021-02-01 00:00:00" self.__test_type_handler(oracledb.DB_TYPE_DATE, oracledb.DB_TYPE_CHAR, in_val, out_val) def test_3612_DATE_to_LONG(self): "3612 outtypehandler conversion from date to long" in_val = datetime.date(2021, 2, 1) out_val = "2021-02-01 00:00:00" self.__test_type_handler(oracledb.DB_TYPE_DATE, oracledb.DB_TYPE_LONG, in_val , out_val) def test_3613_NUMBER_to_VARCHAR(self): "3613 outtypehandler conversion from number to varchar" self.__test_type_handler(oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_VARCHAR, 31.5, "31.5") def test_3614_NUMBER_to_CHAR(self): "3614 outtypehandler conversion from number to char" self.__test_type_handler(oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_CHAR, 31.5, "31.5") def test_3615_NUMBER_to_LONG(self): "3615 outtypehandler conversion from number to long" self.__test_type_handler(oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_LONG, 31.5, "31.5") def test_3616_INTERVAL_to_VARCHAR(self): "3616 outtypehandler conversion from interval to varchar" in_val = datetime.timedelta(days=-1, seconds=86314, microseconds=431152) out_val = "-000000001 23:58:34.431152000" self.__test_type_handler(oracledb.DB_TYPE_INTERVAL_DS, oracledb.DB_TYPE_VARCHAR, in_val, out_val) def test_3617_INTERVAL_to_CHAR(self): "3617 outtypehandler conversion from interval to char" in_val = datetime.timedelta(days=-1, seconds=86314, microseconds=431152) out_val = "-000000001 23:58:34.431152000" self.__test_type_handler(oracledb.DB_TYPE_INTERVAL_DS, oracledb.DB_TYPE_CHAR, in_val, out_val) def test_3618_INTERVAL_to_LONG(self): "3618 outtypehandler conversion from interval to long" in_val = datetime.timedelta(days=-1, seconds=86314, microseconds=431152) out_val = "-000000001 23:58:34.431152000" self.__test_type_handler(oracledb.DB_TYPE_INTERVAL_DS, oracledb.DB_TYPE_LONG, in_val, out_val) def test_3619_TIMESTAMP_to_VARCHAR(self): "3619 outtypehandler conversion from timestamp to varchar" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP, oracledb.DB_TYPE_VARCHAR, in_val, str(in_val)) def test_3620_TIMESTAMP_to_CHAR(self): "3620 outtypehandler conversion from timestamp to char" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP, oracledb.DB_TYPE_CHAR, in_val, str(in_val)) def test_3621_TIMESTAMP_to_LONG(self): "3621 outtypehandler conversion from timestamp to long" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP, oracledb.DB_TYPE_LONG, in_val, str(in_val)) def test_3622_TIMESTAMP_TZ_to_VARCHAR(self): "3622 outtypehandler conversion from timestamptz to varchar" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP_TZ, oracledb.DB_TYPE_VARCHAR, in_val, str(in_val)) def test_3623_TIMESTAMP_TZ_to_CHAR(self): "3623 outtypehandler conversion from timestamptz to char" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP_TZ, oracledb.DB_TYPE_CHAR, in_val, str(in_val)) def test_3624_TIMESTAMP_TZ_to_LONG(self): "3624 outtypehandler conversion from timestamptz to long" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP_TZ, oracledb.DB_TYPE_LONG, in_val, str(in_val)) def test_3625_TIMESTAMP_LTZ_to_VARCHAR(self): "3625 outtypehandler conversion from timestampltz to varchar" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP_LTZ, oracledb.DB_TYPE_VARCHAR, in_val, str(in_val)) def test_3626_TIMESTAMP_LTZ_to_CHAR(self): "3626 outtypehandler conversion from timestamp_ltz to char" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP_LTZ, oracledb.DB_TYPE_CHAR, in_val, str(in_val)) def test_3627_TIMESTAMP_LTZ_to_LONG(self): "3627 outtypehandler conversion from timestamp_LTZ to long" in_val = datetime.datetime(2002, 12, 17, 1, 2, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP_LTZ, oracledb.DB_TYPE_LONG, in_val, str(in_val)) def test_3628_BINT_to_VARCHAR(self): "3628 outtypehandler conversion from bint to varchar" self.__test_type_handler(oracledb.DB_TYPE_BINARY_INTEGER, oracledb.DB_TYPE_VARCHAR, 31, "31") def test_3629_BINT_to_CHAR(self): "3629 outtypehandler conversion from bint to char" self.__test_type_handler(oracledb.DB_TYPE_BINARY_INTEGER, oracledb.DB_TYPE_CHAR, 31, "31") def test_3630_BINT_to_LONG(self): "3630 outtypehandler conversion from bint to long" self.__test_type_handler(oracledb.DB_TYPE_BINARY_INTEGER, oracledb.DB_TYPE_LONG, 31, "31") def test_3631_NUMBER_to_BINARY_DOUBLE(self): "3631 outtypehandler conversion from number to binary double" self.__test_type_handler(oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_BINARY_DOUBLE, 31.5, 31.5) def test_3632_BINARY_FLOAT_to_BINARY_DOUBLE(self): "3632 outtypehandler conversion from binary float to binary double" self.__test_type_handler(oracledb.DB_TYPE_BINARY_FLOAT, oracledb.DB_TYPE_BINARY_DOUBLE, 31.5, 31.5) def test_3633_VARCHAR_to_BINARY_DOUBLE(self): "3633 outtypehandler conversion from varchar to binary double" self.__test_type_handler(oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_BINARY_DOUBLE, "31.5", 31.5) def test_3634_CHAR_to_BINARY_DOUBLE(self): "3634 outtypehandler conversion from char to binary double" self.__test_type_handler(oracledb.DB_TYPE_CHAR, oracledb.DB_TYPE_BINARY_DOUBLE, "31.5", 31.5) def test_3635_LONG_to_BINARY_DOUBLE(self): "3635 outtypehandler conversion from long to binary double" self.__test_type_handler(oracledb.DB_TYPE_LONG, oracledb.DB_TYPE_BINARY_DOUBLE, "31.5", 31.5) def test_3636_NUMBER_to_BINARY_FLOAT(self): "3636 outtypehandler conversion from number to binary float" self.__test_type_handler(oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_BINARY_FLOAT, 31.5, 31.5) def test_3637_BINARY_DOUBLE_to_BINARY_FLOAT(self): "3637 outtypehandler conversion from binary double to binary float" self.__test_type_handler(oracledb.DB_TYPE_BINARY_DOUBLE, oracledb.DB_TYPE_BINARY_FLOAT, 31.5, 31.5) def test_3638_VARCHAR_to_BINARY_FLOAT(self): "3638 outtypehandler conversion from varchar to binary float" self.__test_type_handler(oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_BINARY_FLOAT, "31.5", 31.5) def test_3639_CHAR_to_BINARY_FLOAT(self): "3639 outtypehandler conversion from char to binary float" self.__test_type_handler(oracledb.DB_TYPE_CHAR, oracledb.DB_TYPE_BINARY_FLOAT, "31.5", 31.5) def test_3640_LONG_to_BINARY_FLOAT(self): "3640 outtypehandler conversion from varchar to binary float" self.__test_type_handler(oracledb.DB_TYPE_LONG, oracledb.DB_TYPE_BINARY_FLOAT, "31.5", 31.5) def test_3641_VARCHAR_to_CHAR(self): "3641 outtypehandler from varchar to char" self.__test_type_handler(oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_CHAR, "31.5", "31.5") def test_3642_VARCHAR_to_LONG(self): "3642 outtypehandler from varchar to long" self.__test_type_handler(oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_LONG, "31.5", "31.5") def test_3643_LONG_to_VARCHAR(self): "3643 outtypehandler from long to varchar" self.__test_type_handler(oracledb.DB_TYPE_LONG, oracledb.DB_TYPE_VARCHAR, "31.5", "31.5") def test_3644_LONG_to_CHAR(self): "3644 outtypehandler from long to varhcar" self.__test_type_handler(oracledb.DB_TYPE_LONG, oracledb.DB_TYPE_CHAR, "31.5", "31.5") def test_3645_CHAR_to_VARCHAR(self): "3645 outtypehandler from char to varchar" self.__test_type_handler(oracledb.DB_TYPE_CHAR, oracledb.DB_TYPE_VARCHAR, "31.5", "31.5") def test_3646_CHAR_to_LONG(self): "3646 outtypehandler from char to long" self.__test_type_handler(oracledb.DB_TYPE_CHAR, oracledb.DB_TYPE_LONG, "31.5", "31.5") def test_3647_TIMESTAMP_to_TIMESTAMP_TZ(self): "3647 outtypehandler from timestamp to timestamptz" val = datetime.datetime(2002, 12, 17, 0, 0, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP, oracledb.DB_TYPE_TIMESTAMP_LTZ, val, val) def test_3648_TIMESTAMP_to_TIMESTAMP_LTZ(self): "3648 outtypehandler from timestamp to timestampltz" val = datetime.datetime(2002, 12, 17, 0, 0, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP, oracledb.DB_TYPE_TIMESTAMP_LTZ, val, val) def test_3649_TIMESTAMP_TZ_to_TIMESTAMP(self): "3649 outtypehandler from timestamptz to timestamp" val = datetime.datetime(2002, 12, 17, 0, 0, 16, 400000) self.__test_type_handler(oracledb.DB_TYPE_TIMESTAMP_TZ, oracledb.DB_TYPE_TIMESTAMP, val, val) def test_3650_NUMBER_TO_DATE(self): "3650 outtypehandler conversion from number to date is invalid" self.assertRaises(oracledb.DatabaseError, self.__test_type_handler, oracledb.DB_TYPE_NUMBER, oracledb.DB_TYPE_DATE, 3, 3) def test_3651_CLOB_TO_CHAR(self): "3651 outtypehandler from CLOB to CHAR" val = "Some Clob String" self.__test_type_handler(oracledb.DB_TYPE_CLOB, oracledb.DB_TYPE_CHAR, val, val) def test_3652_CLOB_TO_VARCHAR(self): "3652 outtypehandler from CLOB to VARCHAR" val = "Some Clob String" self.__test_type_handler(oracledb.DB_TYPE_CLOB, oracledb.DB_TYPE_VARCHAR, val, val) def test_3653_CLOB_TO_LONG(self): "3653 outtypehandler from CLOB to LONG" val = "Some Clob String" self.__test_type_handler(oracledb.DB_TYPE_CLOB, oracledb.DB_TYPE_LONG, val, val) def test_3654_BLOB_TO_RAW(self): "3654 outtypehandler from BLOB to RAW" val = b"Some binary data" self.__test_type_handler(oracledb.DB_TYPE_BLOB, oracledb.DB_TYPE_RAW, val, val) def test_3655_BLOB_TO_LONG_RAW(self): "3655 outtypehandler from BLOB to LONGRAW" val = b"Some binary data" self.__test_type_handler(oracledb.DB_TYPE_BLOB, oracledb.DB_TYPE_LONG_RAW, val, val) def test_3656_BLOB_TO_LONG_RAW(self): "3656 outtypehandler from permanant BLOBs to LONG_RAW" self.__test_type_handler_blob(oracledb.DB_TYPE_LONG_RAW) def test_3657_BLOB_TO_RAW(self): "3657 outtypehandler from permanant BLOBs to RAW" self.__test_type_handler_blob(oracledb.DB_TYPE_RAW) def test_3658_CLOB_TO_VARCHAR(self): "3658 outtypehandler from permanant CLOBs to VARCHAR" self.__test_type_handler_clob(oracledb.DB_TYPE_VARCHAR) def test_3659_CLOB_TO_CHAR(self): "3659 outtypehandler from permanant CLOBs to CHAR" self.__test_type_handler_clob(oracledb.DB_TYPE_CHAR) def test_3660_CLOB_TO_LONG(self): "3660 outtypehandler from permanant CLOBs to LONG" self.__test_type_handler_clob(oracledb.DB_TYPE_LONG) def test_3661_NCLOB_TO_CHAR(self): "3661 outtypehandler from NCLOB to CHAR" val = "Some nclob data" self.__test_type_handler(oracledb.DB_TYPE_NCLOB, oracledb.DB_TYPE_CHAR, val, val) def test_3662_NCLOB_TO_VARCHAR(self): "3662 outtypehandler from NCLOB to VARCHAR" val = "Some nclob data" self.__test_type_handler(oracledb.DB_TYPE_NCLOB, oracledb.DB_TYPE_VARCHAR, val, val) def test_3663_NCLOB_TO_LONG(self): "3663 outtypehandler from NCLOB to LONG" val = "Some nclob data" self.__test_type_handler(oracledb.DB_TYPE_NCLOB, oracledb.DB_TYPE_LONG, val, val) def test_3664_NCLOB_TO_VARCHAR(self): "3664 outtypehandler from permanant NCLOBs to VARCHAR" self.__test_type_handler_nclob(oracledb.DB_TYPE_VARCHAR) def test_3665_NCLOB_TO_CHAR(self): "3665 outtypehandler from permanant NCLOBs to CHAR" self.__test_type_handler_nclob(oracledb.DB_TYPE_CHAR) def test_3666_NCLOB_TO_LONG(self): "3666 outtypehandler from permanant NCLOBs to LONG" self.__test_type_handler_nclob(oracledb.DB_TYPE_LONG) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3700_var.py000066400000000000000000000436131414105416400206000ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 3700 - Module for testing all variable types. """ import datetime import decimal import time import unittest import cx_Oracle as oracledb import test_env class TestCase(test_env.BaseTestCase): def _test_positive_set_and_get(self, var_type, value_to_set, expected_value, type_name=None): var = self.cursor.var(var_type, typename=type_name) var.setvalue(0, value_to_set) result = var.getvalue() if isinstance(result, oracledb.LOB): result = result.read() elif isinstance(result, oracledb.Object): result = self.get_db_object_as_plain_object(result) self.assertEqual(result, expected_value) def _test_negative_set_and_get(self, var_type, value_to_set, type_name=None): var = self.cursor.var(var_type, typename=type_name) self.assertRaises((TypeError, oracledb.DatabaseError), var.setvalue, 0, value_to_set) def test_3700_DB_TYPE_NUMBER(self): "3700 - setting values on variables of type DB_TYPE_NUMBER" self._test_positive_set_and_get(oracledb.DB_TYPE_NUMBER, 5, 5) self._test_positive_set_and_get(oracledb.DB_TYPE_NUMBER, 3.5, 3.5) self._test_positive_set_and_get(oracledb.DB_TYPE_NUMBER, decimal.Decimal("24.8"), 24.8) self._test_positive_set_and_get(oracledb.DB_TYPE_NUMBER, True, 1) self._test_positive_set_and_get(oracledb.DB_TYPE_NUMBER, False, 0) self._test_positive_set_and_get(oracledb.DB_TYPE_NUMBER, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_NUMBER, "abc") def test_3701_DB_TYPE_BINARY_INTEGER(self): "3701 - setting values on variables of type DB_TYPE_BINARY_INTEGER" self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_INTEGER, 5, 5) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_INTEGER, 3.5, 3) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_INTEGER, decimal.Decimal("24.8"), 24) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_INTEGER, True, 1) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_INTEGER, False, 0) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_INTEGER, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_BINARY_INTEGER, "abc") def test_3702_DB_TYPE_VARCHAR(self): "3702 - setting values on variables of type DB_TYPE_VARCHAR" value = "A VARCHAR string" self._test_positive_set_and_get(oracledb.DB_TYPE_VARCHAR, value, value) value = b"A raw string for VARCHAR" self._test_positive_set_and_get(oracledb.DB_TYPE_VARCHAR, value, value.decode()) self._test_positive_set_and_get(oracledb.DB_TYPE_VARCHAR, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_VARCHAR, 5) def test_3703_DB_TYPE_NVARCHAR(self): "3703 - setting values on variables of type DB_TYPE_NVARCHAR" value = "A NVARCHAR string" self._test_positive_set_and_get(oracledb.DB_TYPE_NVARCHAR, value, value) value = b"A raw string for NVARCHAR" self._test_positive_set_and_get(oracledb.DB_TYPE_NVARCHAR, value, value.decode()) self._test_positive_set_and_get(oracledb.DB_TYPE_NVARCHAR, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_NVARCHAR, 5) def test_3704_DB_TYPE_CHAR(self): "3704 - setting values on variables of type DB_TYPE_CHAR" value = "A CHAR string" self._test_positive_set_and_get(oracledb.DB_TYPE_CHAR, value, value) value = b"A raw string for CHAR" self._test_positive_set_and_get(oracledb.DB_TYPE_CHAR, value, value.decode()) self._test_positive_set_and_get(oracledb.DB_TYPE_CHAR, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_CHAR, 5) def test_3705_DB_TYPE_NCHAR(self): "3705 - setting values on variables of type DB_TYPE_NCHAR" value = "A NCHAR string" self._test_positive_set_and_get(oracledb.DB_TYPE_NCHAR, value, value) value = b"A raw string for NCHAR" self._test_positive_set_and_get(oracledb.DB_TYPE_CHAR, value, value.decode()) self._test_positive_set_and_get(oracledb.DB_TYPE_NCHAR, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_NCHAR, 5) def test_3706_DB_TYPE_LONG(self): "3706 - setting values on variables of type DB_TYPE_LONG" value = "Long Data" * 15000 self._test_positive_set_and_get(oracledb.DB_TYPE_LONG, value, value) value = b"Raw data for LONG" * 15000 self._test_positive_set_and_get(oracledb.DB_TYPE_LONG, value, value.decode()) self._test_positive_set_and_get(oracledb.DB_TYPE_LONG, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_LONG, 5) def test_3707_DB_TYPE_RAW(self): "3707 - setting values on variables of type DB_TYPE_RAW" value = b'Raw Data' self._test_positive_set_and_get(oracledb.DB_TYPE_RAW, value, value) value = "String data for RAW" self._test_positive_set_and_get(oracledb.DB_TYPE_RAW, value, value.encode()) self._test_positive_set_and_get(oracledb.DB_TYPE_RAW, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_RAW, 5) def test_3708_DB_TYPE_LONG_RAW(self): "3708 - setting values on variables of type DB_TYPE_LONG_RAW" value = b'Long Raw Data' * 15000 self._test_positive_set_and_get(oracledb.DB_TYPE_LONG_RAW, value, value) value = "String data for LONG RAW" * 15000 self._test_positive_set_and_get(oracledb.DB_TYPE_LONG_RAW, value, value.encode()) self._test_positive_set_and_get(oracledb.DB_TYPE_LONG_RAW, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_LONG_RAW, 5) def test_3709_DB_TYPE_DATE(self): "3709 - setting values on variables of type DB_TYPE_DATE" self._test_positive_set_and_get(oracledb.DB_TYPE_DATE, datetime.date(2017, 5, 6), datetime.datetime(2017, 5, 6)) value = datetime.datetime(2017, 5, 6, 9, 36, 0) self._test_positive_set_and_get(oracledb.DB_TYPE_DATE, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_DATE, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_DATE, 5) def test_3710_DB_TYPE_TIMESTAMP(self): "3710 - setting values on variables of type DB_TYPE_TIMESTAMP" self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP, datetime.date(2017, 5, 6), datetime.datetime(2017, 5, 6)) value = datetime.datetime(2017, 5, 6, 9, 36, 0, 300000) self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_TIMESTAMP, 5) def test_3711_DB_TYPE_TIMESTAMP_TZ(self): "3711 - setting values on variables of type DB_TYPE_TIMESTAMP_TZ" self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP_TZ, datetime.date(2017, 5, 6), datetime.datetime(2017, 5, 6)) value = datetime.datetime(2017, 5, 6, 9, 36, 0, 300000) self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP_TZ, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP_TZ, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_TIMESTAMP_TZ, 5) def test_3712_DB_TYPE_TIMESTAMP_LTZ(self): "3712 - setting values on variables of type DB_TYPE_TIMESTAMP_LTZ" self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP_LTZ, datetime.date(2017, 5, 6), datetime.datetime(2017, 5, 6)) value = datetime.datetime(2017, 5, 6, 9, 36, 0, 300000) self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP_LTZ, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_TIMESTAMP_LTZ, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_TIMESTAMP_LTZ, 5) def test_3713_DB_TYPE_BLOB(self): "3713 - setting values on variables of type DB_TYPE_BLOB" value = b'Short temp BLOB value' temp_blob = self.connection.createlob(oracledb.DB_TYPE_BLOB) temp_blob.write(value) self._test_positive_set_and_get(oracledb.DB_TYPE_BLOB, temp_blob, value) self._test_negative_set_and_get(oracledb.DB_TYPE_CLOB, temp_blob) self._test_negative_set_and_get(oracledb.DB_TYPE_NCLOB, temp_blob) value = b'Short BLOB value' self._test_positive_set_and_get(oracledb.DB_TYPE_BLOB, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_BLOB, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_BLOB, 5) def test_3714_DB_TYPE_CLOB(self): "3714 - setting values on variables of type DB_TYPE_CLOB" value = 'Short temp CLOB value' temp_clob = self.connection.createlob(oracledb.DB_TYPE_CLOB) temp_clob.write(value) self._test_positive_set_and_get(oracledb.DB_TYPE_CLOB, temp_clob, value) self._test_negative_set_and_get(oracledb.DB_TYPE_BLOB, temp_clob) self._test_negative_set_and_get(oracledb.DB_TYPE_NCLOB, temp_clob) value = 'Short CLOB value' self._test_positive_set_and_get(oracledb.DB_TYPE_CLOB, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_CLOB, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_CLOB, 5) def test_3715_DB_TYPE_NCLOB(self): "3715 - setting values on variables of type DB_TYPE_NCLOB" value = 'Short temp NCLOB value' temp_nclob = self.connection.createlob(oracledb.DB_TYPE_NCLOB) temp_nclob.write(value) self._test_positive_set_and_get(oracledb.DB_TYPE_NCLOB, temp_nclob, value) self._test_negative_set_and_get(oracledb.DB_TYPE_BLOB, temp_nclob) self._test_negative_set_and_get(oracledb.DB_TYPE_CLOB, temp_nclob) value = 'Short NCLOB Value' self._test_positive_set_and_get(oracledb.DB_TYPE_NCLOB, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_NCLOB, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_NCLOB, 5) def test_3716_DB_TYPE_BINARY_FLOAT(self): "3716 - setting values on variables of type DB_TYPE_BINARY_FLOAT" self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_FLOAT, 5, 5.0) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_FLOAT, 3.5, 3.5) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_FLOAT, decimal.Decimal("24.5"), 24.5) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_FLOAT, True, 1.0) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_FLOAT, False, 0.0) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_FLOAT, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_BINARY_FLOAT, "abc") def test_3717_DB_TYPE_BINARY_DOUBLE(self): "3717 - setting values on variables of type DB_TYPE_BINARY_DOUBLE" self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_DOUBLE, 5, 5.0) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_DOUBLE, 3.5, 3.5) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_DOUBLE, decimal.Decimal("192.125"), 192.125) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_DOUBLE, True, 1.0) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_DOUBLE, False, 0.0) self._test_positive_set_and_get(oracledb.DB_TYPE_BINARY_DOUBLE, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_BINARY_DOUBLE, "abc") def test_3718_DB_TYPE_BOOLEAN(self): "3718 - setting values on variables of type DB_TYPE_BOOLEAN" self._test_positive_set_and_get(oracledb.DB_TYPE_BOOLEAN, 5, True) self._test_positive_set_and_get(oracledb.DB_TYPE_BOOLEAN, 2.0, True) self._test_positive_set_and_get(oracledb.DB_TYPE_BOOLEAN, "abc", True) self._test_positive_set_and_get(oracledb.DB_TYPE_BOOLEAN, decimal.Decimal("24.8"), True) self._test_positive_set_and_get(oracledb.DB_TYPE_BOOLEAN, 0.0, False) self._test_positive_set_and_get(oracledb.DB_TYPE_BOOLEAN, 0, False) self._test_positive_set_and_get(oracledb.DB_TYPE_BOOLEAN, None, None) def test_3719_DB_TYPE_INTERVAL_DS(self): "3719 - setting values on variables of type DB_TYPE_INTERVAL_DS" value = datetime.timedelta(days=5, seconds=56000, microseconds=123780) self._test_positive_set_and_get(oracledb.DB_TYPE_INTERVAL_DS, value, value) self._test_positive_set_and_get(oracledb.DB_TYPE_INTERVAL_DS, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_INTERVAL_DS, 5) def test_3720_DB_TYPE_ROWID(self): "3720 - setting values on variables of type DB_TYPE_ROWID" self._test_negative_set_and_get(oracledb.DB_TYPE_ROWID, 12345) self._test_negative_set_and_get(oracledb.DB_TYPE_ROWID, "523lkhlf") def test_3721_DB_TYPE_OBJECT(self): "3721 - setting values on variables of type DB_TYPE_OBJECT" obj_type = self.connection.gettype("UDT_OBJECT") obj = obj_type.newobject() plain_obj = self.get_db_object_as_plain_object(obj) self._test_positive_set_and_get(oracledb.DB_TYPE_OBJECT, obj, plain_obj, "UDT_OBJECT") self._test_positive_set_and_get(obj_type, obj, plain_obj) self._test_positive_set_and_get(oracledb.DB_TYPE_OBJECT, None, None, "UDT_OBJECT") self._test_positive_set_and_get(obj_type, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_OBJECT, "abc", "UDT_OBJECT") self._test_negative_set_and_get(oracledb.DB_TYPE_OBJECT, obj, "UDT_OBJECTARRAY") wrong_obj_type = self.connection.gettype("UDT_OBJECTARRAY") self._test_negative_set_and_get(wrong_obj_type, obj) @unittest.skipIf(test_env.get_client_version() < (21, 0), "unsupported client") @unittest.skipIf(test_env.get_server_version() < (21, 0), "unsupported server") def test_3722_DB_TYPE_JSON(self): "3722 - setting values on variables of type DB_TYPE_JSON" json_data = [ 5, 25.25, decimal.Decimal("10.25"), True, False, datetime.datetime(2017, 5, 6), datetime.datetime(2017, 5, 6, 9, 36, 0, 300000), datetime.timedelta(days=5, seconds=56000, microseconds=123780), {}, 'String', b'Some bytes', {'keyA': 1, 'KeyB': 'Melbourne'}, [], [1, "A"], {"name": None}, {"name": "John"}, {"age": 30}, {"Permanent": True}, { "employee": { "name":"John", "age": 30, "city": "Delhi", "Parmanent": True } }, { "employees": ["John", "Matthew", "James"] }, { "employees": [ { "employee1": {"name": "John", "city": "Delhi"} }, { "employee2": {"name": "Matthew", "city": "Mumbai"} }, { "employee3": {"name": "James", "city": "Bangalore"} } ] } ] self._test_positive_set_and_get(oracledb.DB_TYPE_JSON, json_data, json_data) self._test_positive_set_and_get(oracledb.DB_TYPE_JSON, None, None) def test_3723_DB_TYPE_CURSOR(self): "3723 - test setting values on variables of type DB_TYPE_CURSOR" cursor = self.connection.cursor() var = self.cursor.var(oracledb.DB_TYPE_CURSOR) var.setvalue(0, cursor) self._test_positive_set_and_get(oracledb.DB_TYPE_CURSOR, None, None) self._test_negative_set_and_get(oracledb.DB_TYPE_CURSOR, 5) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_3800_typehandler.py000066400000000000000000000135611414105416400223270ustar00rootroot00000000000000#------------------------------------------------------------------------------ #Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ """ 3800 - Module for testing the input and output type handlers. """ import json import cx_Oracle as oracledb import test_env class Building(object): def __init__(self, building_id, description, num_floors): self.building_id = building_id self.description = description self.num_floors = num_floors def __repr__(self): return "" % (self.building_id, self.description) def __eq__(self, other): if isinstance(other, Building): return other.building_id == self.building_id \ and other.description == self.description \ and other.num_floors == self.num_floors return NotImplemented def to_json(self): return json.dumps(self.__dict__) @classmethod def from_json(cls, value): result = json.loads(value) return cls(**result) class TestCase(test_env.BaseTestCase): def building_in_converter(self, value): return value.to_json() def input_type_handler(self, cursor, value, num_elements): if isinstance(value, Building): return cursor.var(oracledb.STRING, arraysize=num_elements, inconverter=self.building_in_converter) def output_type_handler(self, cursor, name, default_type, size, precision, scale): if default_type == oracledb.STRING: return cursor.var(default_type, arraysize=cursor.arraysize, outconverter=Building.from_json) def test_3800(self): "3800 - binding unsupported python object without input type handler" self.cursor.execute("truncate table TestTempTable") sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" building = Building(1, "The First Building", 5) self.assertRaises(oracledb.NotSupportedError, self.cursor.execute, sql, (building.building_id, building)) def test_3801(self): "3801 - not callable input type handler" self.cursor.execute("truncate table TestTempTable") building = Building(1, "The First Building", 5) sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" self.cursor.inputtypehandler = 5 self.assertRaises(TypeError, self.cursor.execute, sql, (building.building_id, building)) def test_3802(self): "3802 - binding unsupported python object with input type handler" self.cursor.execute("truncate table TestTempTable") building = Building(1, "The First Building", 5) sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" self.cursor.inputtypehandler = self.input_type_handler self.cursor.execute(sql, (building.building_id, building)) self.connection.commit() self.cursor.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(self.cursor.fetchall(), [(building.building_id, building.to_json())]) def test_3803(self): "3803 - input type handler and output type handler on cursor level" self.cursor.execute("truncate table TestTempTable") building_one = Building(1, "The First Building", 5) building_two = Building(2, "The Second Building", 87) sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" cursor_one = self.connection.cursor() cursor_two = self.connection.cursor() cursor_one.inputtypehandler = self.input_type_handler cursor_one.execute(sql, (building_one.building_id, building_one)) self.connection.commit() cursor_one.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor_one.fetchall(), [(building_one.building_id, building_one.to_json())]) self.assertRaises(oracledb.NotSupportedError, cursor_two.execute, sql, (building_two.building_id, building_two)) cursor_two.outputtypehandler = self.output_type_handler cursor_two.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor_two.fetchall(), [(building_one.building_id, building_one)]) def test_3804(self): "3804 - input type handler and output type handler on connection level" self.cursor.execute("truncate table TestTempTable") building_one = Building(1, "The First Building", 5) building_two = Building(2, "The Second Building", 87) sql = "insert into TestTempTable (IntCol, StringCol) values (:1, :2)" connection = test_env.get_connection() connection.inputtypehandler = self.input_type_handler cursor_one = connection.cursor() cursor_two = connection.cursor() cursor_one.execute(sql, (building_one.building_id, building_one)) cursor_two.execute(sql, (building_two.building_id, building_two)) connection.commit() expected_data = [ (building_one.building_id, building_one), (building_two.building_id, building_two) ] connection.outputtypehandler = self.output_type_handler cursor_one.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor_one.fetchall(), expected_data) cursor_two.execute("select IntCol, StringCol from TestTempTable") self.assertEqual(cursor_two.fetchall(), expected_data) other_cursor = self.connection.cursor() self.assertRaises(oracledb.NotSupportedError, other_cursor.execute, sql, (building_one.building_id, building_one)) if __name__ == "__main__": test_env.run_test_cases() python-cx_Oracle-8.3.0/test/test_env.py000066400000000000000000000263051414105416400201260ustar00rootroot00000000000000#------------------------------------------------------------------------------ # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. # # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # # Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, # Canada. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Sets the environment used by the cx_Oracle test suite. Production # applications should consider using External Authentication to # avoid hard coded credentials. # # You can set values in environment variables to bypass having the test suite # request the information it requires. # # CX_ORACLE_TEST_MAIN_USER: user used for most test cases # CX_ORACLE_TEST_MAIN_PASSWORD: password of user used for most test cases # CX_ORACLE_TEST_PROXY_USER: user for testing proxying # CX_ORACLE_TEST_PROXY_PASSWORD: password of user for testing proxying # CX_ORACLE_TEST_CONNECT_STRING: connect string for test suite # CX_ORACLE_TEST_ADMIN_USER: administrative user for test suite # CX_ORACLE_TEST_ADMIN_PASSWORD: administrative password for test suite # # CX_ORACLE_TEST_CONNECT_STRING can be set to an Easy Connect string, or a # Net Service Name from a tnsnames.ora file or external naming service, # or it can be the name of a local Oracle database instance. # # If cx_Oracle is using Instant Client, then an Easy Connect string is # generally appropriate. The syntax is: # # [//]host_name[:port][/service_name][:server_type][/instance_name] # # Commonly just the host_name and service_name are needed # e.g. "localhost/orclpdb1" or "localhost/XEPDB1" # # If using a tnsnames.ora file, the file can be in a default # location such as $ORACLE_HOME/network/admin/tnsnames.ora or # /etc/tnsnames.ora. Alternatively set the TNS_ADMIN environment # variable and put the file in $TNS_ADMIN/tnsnames.ora. # # The administrative user for cloud databases is ADMIN and the administrative # user for on premises databases is SYSTEM. #------------------------------------------------------------------------------ import getpass import os import sys import unittest import cx_Oracle as oracledb # default values DEFAULT_MAIN_USER = "pythontest" DEFAULT_PROXY_USER = "pythontestproxy" DEFAULT_CONNECT_STRING = "localhost/orclpdb1" # dictionary containing all parameters; these are acquired as needed by the # methods below (which should be used instead of consulting this dictionary # directly) and then stored so that a value is not requested more than once PARAMETERS = {} def get_value(name, label, default_value=""): value = PARAMETERS.get(name) if value is not None: return value env_name = "CX_ORACLE_TEST_" + name value = os.environ.get(env_name) if value is None: if default_value: label += " [%s]" % default_value label += ": " if default_value: value = input(label).strip() else: value = getpass.getpass(label) if not value: value = default_value PARAMETERS[name] = value return value def get_admin_connect_string(): admin_user = get_value("ADMIN_USER", "Administrative user", "admin") admin_password = get_value("ADMIN_PASSWORD", "Password for %s" % admin_user) return "%s/%s@%s" % (admin_user, admin_password, get_connect_string()) def get_charset_ratios(): value = PARAMETERS.get("CS_RATIO") if value is None: connection = get_connection() cursor = connection.cursor() cursor.execute(""" select cast('X' as varchar2(1)), cast('Y' as nvarchar2(1)) from dual""") varchar_column_info, nvarchar_column_info = cursor.description value = (varchar_column_info[3], nvarchar_column_info[3]) PARAMETERS["CS_RATIO"] = value return value def get_client_version(): name = "CLIENT_VERSION" value = PARAMETERS.get(name) if value is None: value = oracledb.clientversion()[:2] PARAMETERS[name] = value return value def get_connection(**kwargs): return oracledb.connect(dsn=get_connect_string(), user=get_main_user(), password=get_main_password(), **kwargs) def get_connect_string(): return get_value("CONNECT_STRING", "Connect String", DEFAULT_CONNECT_STRING) def get_main_password(): return get_value("MAIN_PASSWORD", "Password for %s" % get_main_user()) def get_main_user(): return get_value("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER) def get_pool(user=None, password=None, **kwargs): if user is None: user = get_main_user() if password is None: password = get_main_password() return oracledb.SessionPool(user, password, get_connect_string(), **kwargs) def get_proxy_password(): return get_value("PROXY_PASSWORD", "Password for %s" % get_proxy_user()) def get_proxy_user(): return get_value("PROXY_USER", "Proxy User Name", DEFAULT_PROXY_USER) def get_sleep_proc_name(): server_version = get_server_version() return "dbms_session.sleep" if server_version[0] >= 18 \ else "dbms_lock.sleep" def get_server_version(): name = "SERVER_VERSION" value = PARAMETERS.get(name) if value is None: conn = get_connection() value = tuple(int(s) for s in conn.version.split("."))[:2] PARAMETERS[name] = value return value def run_sql_script(conn, script_name, **kwargs): statement_parts = [] cursor = conn.cursor() replace_values = [("&" + k + ".", v) for k, v in kwargs.items()] + \ [("&" + k, v) for k, v in kwargs.items()] script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) file_name = os.path.join(script_dir, "sql", script_name + "_exec.sql") for line in open(file_name): if line.strip() == "/": statement = "".join(statement_parts).strip() if statement: for search_value, replace_value in replace_values: statement = statement.replace(search_value, replace_value) try: cursor.execute(statement) except: print("Failed to execute SQL:", statement) raise statement_parts = [] else: statement_parts.append(line) cursor.execute(""" select name, type, line, position, text from dba_errors where owner = upper(:owner) order by name, type, line, position""", owner = get_main_user()) prev_name = prev_obj_type = None for name, obj_type, line_num, position, text in cursor: if name != prev_name or obj_type != prev_obj_type: print("%s (%s)" % (name, obj_type)) prev_name = name prev_obj_type = obj_type print(" %s/%s %s" % (line_num, position, text)) def run_test_cases(): print("Running tests for cx_Oracle version", oracledb.version, "built at", oracledb.buildtime) print("File:", oracledb.__file__) print("Client Version:", ".".join(str(i) for i in oracledb.clientversion())) with get_connection() as connection: print("Server Version:", connection.version) print() unittest.main(testRunner=unittest.TextTestRunner(verbosity=2)) def skip_client_version_old_multi(min_version1, min_version2): ver = get_client_version() return ver < min_version1 or \ (ver[0] > min_version1[0] and ver[0] < min_version2[0]) or \ (ver[0] == min_version2[0] and ver[1] < min_version2[1]) def skip_soda_tests(): client = get_client_version() if client < (18, 3): return True server = get_server_version() if server < (18, 0): return True if server > (20, 1) and client < (20, 1): return True return False class RoundTripInfo: def __init__(self, connection): self.prev_round_trips = 0 self.admin_conn = oracledb.connect(get_admin_connect_string()) with connection.cursor() as cursor: cursor.execute("select sys_context('userenv', 'sid') from dual") self.sid, = cursor.fetchone() self.get_round_trips() def get_round_trips(self): with self.admin_conn.cursor() as cursor: cursor.execute(""" select ss.value from v$sesstat ss, v$statname sn where ss.sid = :sid and ss.statistic# = sn.statistic# and sn.name like '%roundtrip%client%'""", sid=self.sid) current_round_trips, = cursor.fetchone() diff_round_trips = current_round_trips - self.prev_round_trips self.prev_round_trips = current_round_trips return diff_round_trips class BaseTestCase(unittest.TestCase): requires_connection = True def assertRoundTrips(self, n): self.assertEqual(self.round_trip_info.get_round_trips(), n) def get_db_object_as_plain_object(self, obj): if obj.type.iscollection: element_values = [] for value in obj.aslist(): if isinstance(value, oracledb.Object): value = self.get_db_object_as_plain_object(value) elif isinstance(value, oracledb.LOB): value = value.read() element_values.append(value) return element_values attr_values = [] for attribute in obj.type.attributes: value = getattr(obj, attribute.name) if isinstance(value, oracledb.Object): value = self.get_db_object_as_plain_object(value) elif isinstance(value, oracledb.LOB): value = value.read() attr_values.append(value) return tuple(attr_values) def get_soda_database(self, minclient=(18, 3), minserver=(18, 0), message="not supported with this client/server " \ "combination"): client = get_client_version() if client < minclient: self.skipTest(message) server = get_server_version() if server < minserver: self.skipTest(message) if server > (20, 1) and client < (20, 1): self.skipTest(message) return self.connection.getSodaDatabase() def is_on_oracle_cloud(self, connection=None): if connection is None: connection = self.connection cursor = connection.cursor() cursor.execute(""" select sys_context('userenv', 'service_name') from dual""") service_name, = cursor.fetchone() return service_name.endswith("oraclecloud.com") def setUp(self): if self.requires_connection: self.connection = get_connection() self.cursor = self.connection.cursor() def setup_round_trip_checker(self): self.round_trip_info = RoundTripInfo(self.connection) def tearDown(self): if self.requires_connection: self.connection.close() del self.cursor del self.connection python-cx_Oracle-8.3.0/tox.ini000066400000000000000000000005641414105416400162600ustar00rootroot00000000000000[tox] envlist = py{36,37,38,39,310} [testenv] commands = {envpython} -m unittest discover -v -s test passenv = CX_ORACLE_TEST_MAIN_USER CX_ORACLE_TEST_MAIN_PASSWORD CX_ORACLE_TEST_PROXY_USER CX_ORACLE_TEST_PROXY_PASSWORD CX_ORACLE_TEST_CONNECT_STRING CX_ORACLE_TEST_ADMIN_USER CX_ORACLE_TEST_ADMIN_PASSWORD DPI_DEBUG_LEVEL ORACLE_HOME