All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.ziclix.python.sql.Procedure Maven / Gradle / Ivy

Go to download

Jython is an implementation of the high-level, dynamic, object-oriented language Python written in 100% Pure Java, and seamlessly integrated with the Java platform. It thus allows you to run Python on any Java platform.

There is a newer version: 2.7.4
Show newest version
/*
 * Jython Database Specification API 2.0
 *
 *
 * Copyright (c) 2001 brian zimmer 
 *
 */
package com.ziclix.python.sql;

import org.python.core.Py;
import org.python.core.PyInteger;
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyString;

import java.sql.CallableStatement;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.BitSet;

/**
 * This class provides the necessary functionality to call stored
 * procedures.  It handles managing the database metadata and binding
 * the appropriate parameters.
 *
 * @author brian zimmer
 */
public class Procedure extends Object {

    /**
     * Field NAME
     */
    protected static final int NAME = 3;

    /**
     * Field COLUMN_TYPE
     */
    protected static final int COLUMN_TYPE = 4;

    /**
     * Field DATA_TYPE
     */
    protected static final int DATA_TYPE = 5;

    /**
     * Field DATA_TYPE_NAME
     */
    protected static final int DATA_TYPE_NAME = 6;

    /**
     * Field PRECISION
     */
    protected static final int PRECISION = 7;

    /**
     * Field LENGTH
     */
    protected static final int LENGTH = 8;

    /**
     * Field SCALE
     */
    protected static final int SCALE = 9;

    /**
     * Field NULLABLE
     */
    protected static final int NULLABLE = 11;

    /**
     * Field cursor
     */
    protected PyCursor cursor;

    /**
     * Field columns
     */
    protected PyObject columns;

    /**
     * Field procedureCatalog
     */
    protected PyObject procedureCatalog;

    /**
     * Field procedureSchema
     */
    protected PyObject procedureSchema;

    /**
     * Field procedureName
     */
    protected PyObject procedureName;

    /**
     * Field inputSet
     */
    protected BitSet inputSet;

    /**
     * Constructor Procedure
     *
     * @param cursor cursor an open cursor
     * @param name   name a string or tuple representing the name
     * @throws SQLException
     */
    public Procedure(PyCursor cursor, PyObject name) throws SQLException {

        this.cursor = cursor;
        this.inputSet = new BitSet();

        if (name instanceof PyString) {
            this.procedureCatalog = getDefault();
            this.procedureSchema = getDefault();
            this.procedureName = name;
        } else if (PyCursor.isSeq(name)) {
            if (name.__len__() == 3) {
                this.procedureCatalog = name.__getitem__(0);
                this.procedureSchema = name.__getitem__(1);
                this.procedureName = name.__getitem__(2);
            } else {

                // throw an exception
            }
        } else {

            // throw an exception
        }

        fetchColumns();
    }

    /**
     * Prepares the statement and registers the OUT/INOUT parameters (if any).
     *
     * @return CallableStatement
     * @throws SQLException
     */
    public CallableStatement prepareCall() throws SQLException {
        return prepareCall(Py.None, Py.None);
    }

    /**
     * Prepares the statement and registers the OUT/INOUT parameters (if any).
     *
     * @param rsType   the value of to be created ResultSet type
     * @param rsConcur the value of the to be created ResultSet concurrency
     * @return CallableStatement
     * @throws SQLException
     */
    public CallableStatement prepareCall(PyObject rsType, PyObject rsConcur) throws SQLException {

        // prepare the statement
        CallableStatement statement = null;
        boolean normal = ((rsType == Py.None) && (rsConcur == Py.None));

        try {

            // build the full call syntax
            String sqlString = toSql();

            if (normal) {
                statement = cursor.connection.connection.prepareCall(sqlString);
            } else {
                int t = rsType.asInt();
                int c = rsConcur.asInt();

                statement = cursor.connection.connection.prepareCall(sqlString, t, c);
            }

            // prepare the OUT parameters
            registerOutParameters(statement);
        } catch (SQLException e) {
            if (statement != null) {
                try {
                    statement.close();
                } catch (Exception ex) {
                }
            }

            throw e;
        }

        return statement;
    }

    /**
     * Prepare the binding dictionary with the correct datatypes.
     *
     * @param params   a non-None list of params
     * @param bindings a dictionary of bindings
     */
    public void normalizeInput(PyObject params, PyObject bindings) throws SQLException {

        if (this.columns == Py.None) {
            return;
        }

        // do nothing with params at the moment
        for (int i = 0, len = this.columns.__len__(), binding = 0; i < len; i++) {
            PyObject column = this.columns.__getitem__(i);
            int colType = column.__getitem__(COLUMN_TYPE).asInt();

            switch (colType) {

                case DatabaseMetaData.procedureColumnIn:
                case DatabaseMetaData.procedureColumnInOut:

                    // bindings are Python-indexed
                    PyInteger key = Py.newInteger(binding++);

                    if (bindings.__finditem__(key) == null) {
                        int dataType = column.__getitem__(DATA_TYPE).asInt();
                        bindings.__setitem__(key, Py.newInteger(dataType));
                    }

                    // inputs are JDBC-indexed
                    this.inputSet.set(i + 1);
                    break;
            }
        }
    }

    /**
     * This method determines whether the param at the specified index is an
     * IN or INOUT param for a stored procedure.  This is only configured properly
     * AFTER a call to normalizeInput().
     *
     * @param index JDBC indexed column index (1, 2, ...)
     * @return true if the column is an input, false otherwise
     * @throws SQLException
     */
    public boolean isInput(int index) throws SQLException {
        return this.inputSet.get(index);
    }

    /**
     * Returns the call in the syntax: 
{@literal
     * {? = call (?, ?, ...)}
     * {call (?, ?, ...)}
     * }
* * As of now, all parameters variables are created and no support for named variable calling is * supported. * * @return String */ public String toSql() throws SQLException { int colParam = 0; int colReturn = 0; if (this.columns != Py.None) { for (int i = 0, len = this.columns.__len__(); i < len; i++) { PyObject column = this.columns.__getitem__(i); int colType = column.__getitem__(COLUMN_TYPE).asInt(); switch (colType) { case DatabaseMetaData.procedureColumnUnknown: throw zxJDBC.makeException(zxJDBC.NotSupportedError, "procedureColumnUnknown"); case DatabaseMetaData.procedureColumnResult: throw zxJDBC.makeException(zxJDBC.NotSupportedError, "procedureColumnResult"); // these go on the right hand side case DatabaseMetaData.procedureColumnIn: case DatabaseMetaData.procedureColumnInOut: case DatabaseMetaData.procedureColumnOut: colParam++; break; // these go on the left hand side case DatabaseMetaData.procedureColumnReturn: colReturn++; break; default : throw zxJDBC.makeException(zxJDBC.DataError, "unknown column type [" + colType + "]"); } } } StringBuffer sql = new StringBuffer("{"); if (colReturn > 0) { PyList list = new PyList(); for (; colReturn > 0; colReturn--) { list.append(Py.newString("?")); } sql.append(Py.newString(",").join(list)).append(" = "); } String name = this.getProcedureName(); sql.append("call ").append(name).append("("); if (colParam > 0) { PyList list = new PyList(); for (; colParam > 0; colParam--) { list.append(Py.newString("?")); } sql.append(Py.newString(",").join(list)); } return sql.append(")}").toString(); } /** * Registers the OUT/INOUT parameters of the statement. * * @param statement statement * @throws SQLException */ protected void registerOutParameters(CallableStatement statement) throws SQLException { if (this.columns == Py.None) { return; } for (int i = 0, len = this.columns.__len__(); i < len; i++) { PyObject column = this.columns.__getitem__(i); int colType = column.__getitem__(COLUMN_TYPE).asInt(); int dataType = column.__getitem__(DATA_TYPE).asInt(); String dataTypeName = column.__getitem__(DATA_TYPE_NAME).toString(); switch (colType) { case DatabaseMetaData.procedureColumnInOut: case DatabaseMetaData.procedureColumnOut: case DatabaseMetaData.procedureColumnReturn: cursor.datahandler.registerOut(statement, i + 1, colType, dataType, dataTypeName); break; } } } /** * Get the columns for the stored procedure. * * @throws SQLException */ protected void fetchColumns() throws SQLException { PyExtendedCursor pec = (PyExtendedCursor) cursor.connection.cursor(); try { pec.datahandler = this.cursor.datahandler; pec.procedurecolumns(procedureCatalog, procedureSchema, procedureName, Py.None); this.columns = pec.fetchall(); } finally { pec.close(); } } /** * The value for a missing schema or catalog. This value is used to find * the column names for the procedure. Not all DBMS use the same default * value; for instance Oracle uses an empty string and SQLServer a null. * This implementation returns the empty string. * * @return the default value (the empty string) * @see java.sql.DatabaseMetaData#getProcedureColumns */ protected PyObject getDefault() { return Py.EmptyString; } /** * Construct a procedure name for the relevant schema and catalog information. */ protected String getProcedureName() { StringBuffer proc = new StringBuffer(); if (this.procedureCatalog.__nonzero__()) { proc.append(this.procedureCatalog.toString()).append("."); } return proc.append(this.procedureName.toString()).toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy