/Languages/IronPython/IronPython.SQLite/Cursor.cs
C# | 598 lines | 467 code | 113 blank | 18 comment | 104 complexity | 7217f3a4eb170aa54988edc3d4ade408 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
- /* ****************************************************************************
- *
- * Copyright (c) Jeff Hardy 2010-2012.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
- using System.Collections;
- using System.Collections.Generic;
- using System.Runtime.InteropServices;
- using System.Text.RegularExpressions;
- using Community.CsharpSqlite;
- using IronPython.Runtime;
- using IronPython.Runtime.Exceptions;
- using IronPython.Runtime.Operations;
- using Microsoft.Scripting.Runtime;
- using Microsoft.Scripting.Utils;
- using sqlite3_stmt = Community.CsharpSqlite.Sqlite3.Vdbe;
- namespace IronPython.SQLite
- {
- public static partial class PythonSQLite
- {
- [PythonType]
- public class Cursor : IEnumerable
- {
- public const string __doc__ = "SQLite database cursor class.";
- Statement statement;
- object next_row;
- bool resultsDone;
- int last_step_rc;
- List<object> row_cast_map = new List<object>();
- public PythonTuple description { get; private set; }
- public int rowcount { get; private set; }
- public int? rownumber
- {
- get { return null; }
- }
- public long? lastrowid { get; private set; }
- public object row_factory { get; set; }
- public int arraysize { get; set; }
- public Connection connection { get; private set; }
- public object callproc(string procname)
- {
- throw PythonSQLite.MakeNotSupportedError();
- }
- private CodeContext context;
- public Cursor(CodeContext context, Connection connection)
- {
- this.context = context;
- this.connection = connection;
- this.arraysize = 1;
- this.rowcount = -1;
- if(this.connection != null)
- this.connection.checkThread();
- }
- ~Cursor()
- {
- if(this.statement != null)
- this.statement.Reset();
- }
- [Documentation("Closes the cursor.")]
- public void close()
- {
- connection.checkThread(); connection.checkConnection();
- if(this.statement != null)
- {
- this.statement.Reset();
- }
- }
- [Documentation("Executes a SQL statement.")]
- public object execute(CodeContext context, object operation, [Optional][DefaultParameterValue(null)]object args)
- {
- return queryExecute(context, false, operation, args);
- }
- [Documentation("Repeatedly executes a SQL statement.")]
- public object executemany(CodeContext context, object operation, object args)
- {
- return queryExecute(context, true, operation, args);
- }
- private object queryExecute(CodeContext context, bool multiple, object operation_obj, object args)
- {
- if(!(operation_obj is string))
- throw CreateThrowable(PythonExceptions.ValueError, "operation parameter must be str or unicode");
- string operation = (string)operation_obj;
- if(string.IsNullOrEmpty(operation))
- return null;
- int rc;
- connection.checkThread(); connection.checkConnection();
- this.next_row = null;
- IEnumerator parameters_iter = null;
- if(multiple)
- {
- if(args != null)
- parameters_iter = PythonOps.CreatePythonEnumerator(args);
- }
- else
- {
- object[] parameters_list = { args };
- if(parameters_list[0] == null)
- parameters_list[0] = new PythonTuple();
- parameters_iter = parameters_list.GetEnumerator();
- }
- if(this.statement != null)
- rc = this.statement.Reset();
- this.description = null;
- this.rowcount = -1;
- // TODO: use stmt cache instead ?
- this.statement = (Statement)this.connection.__call__(operation);
- if(this.statement.in_use)
- this.statement = new Statement(connection, operation);
- this.statement.Reset();
- this.statement.MarkDirty();
- if(!string.IsNullOrEmpty(connection.begin_statement))
- {
- switch(statement.StatementType)
- {
- case StatementType.Update:
- case StatementType.Insert:
- case StatementType.Delete:
- case StatementType.Replace:
- if(!connection.inTransaction)
- connection.begin();
-
- break;
- case StatementType.Other:
- // it's a DDL statement or something similar
- // we better COMMIT first so it works for all cases
- if(connection.inTransaction)
- connection.commit();
- break;
- case StatementType.Select:
- if(multiple)
- throw MakeProgrammingError("You cannot execute SELECT statements in executemany().");
- break;
- default:
- break;
- }
- }
- while(true)
- {
- if(!parameters_iter.MoveNext())
- break;
- object parameters = parameters_iter.Current;
- this.statement.MarkDirty();
- this.statement.BindParameters(context, parameters);
- while(true)
- {
- rc = this.statement.RawStep();
- if(rc == Sqlite3.SQLITE_DONE || rc == Sqlite3.SQLITE_ROW)
- break;
- rc = this.statement.Reset();
- if(rc == Sqlite3.SQLITE_SCHEMA)
- {
- rc = this.statement.Recompile(context, parameters);
- if(rc == Sqlite3.SQLITE_OK)
- {
- continue;
- }
- else
- {
- this.statement.Reset();
- throw GetSqliteError(this.connection.db, null);
- }
- }
- else
- {
- this.statement.Reset();
- throw GetSqliteError(this.connection.db, null);
- }
- }
- if(!buildRowCastMap())
- throw MakeOperationalError("Error while building row_cast_map");
- if(rc == Sqlite3.SQLITE_ROW || (rc == Sqlite3.SQLITE_DONE && this.statement.StatementType == StatementType.Select))
- {
- if(this.description == null)
- {
- int numcols = Sqlite3.sqlite3_column_count(this.statement.st);
- object[] new_description = new object[numcols];
- for(int i = 0; i < numcols; ++i)
- {
- string name = buildColumnName(Sqlite3.sqlite3_column_name(this.statement.st, i));
- object descriptor = new object[] {
- name,
- (object)null,
- (int?)null,
- (int?)null,
- (int?)null,
- (int?)null,
- (bool?)null
- };
- new_description[i] = new PythonTuple(descriptor);
- }
- this.description = new PythonTuple(new_description);
- }
- }
- if(rc == Sqlite3.SQLITE_ROW)
- {
- if(multiple)
- throw MakeProgrammingError("executemany() can only execute DML statements.");
- this.next_row = fetchOneRow(context);
- }
- else if(rc == Sqlite3.SQLITE_DONE && !multiple)
- {
- this.statement.Reset();
- }
- switch(this.statement.StatementType)
- {
- case StatementType.Update:
- case StatementType.Delete:
- case StatementType.Insert:
- case StatementType.Replace:
- if(this.rowcount == -1)
- this.rowcount = 0;
- this.rowcount += Sqlite3.sqlite3_changes(this.connection.db);
- break;
- }
- if(!multiple && this.statement.StatementType == StatementType.Insert)
- {
- this.lastrowid = Sqlite3.sqlite3_last_insert_rowid(this.connection.db);
- }
- else
- {
- this.lastrowid = null;
- }
- if(multiple)
- rc = this.statement.Reset();
- }
- return this;
- }
- private string buildColumnName(string colname)
- {
- int n = colname.IndexOf('[');
- return n < 0 ? colname : colname.Substring(0, n).Trim();
- }
- private object fetchOneRow(CodeContext context)
- {
- int numcols = Sqlite3.sqlite3_data_count(this.statement.st);
- object[] row = new object[numcols];
- object converter = null;
- for(int i = 0; i < numcols; ++i)
- {
- object converted = null;
- if(this.connection.detect_types != 0)
- {
- converter = row_cast_map[i];
- }
- else
- {
- converter = null;
- }
- if(converter != null)
- {
- byte[] val = Sqlite3.sqlite3_column_blob(this.statement.st, i);
- if(val == null)
- {
- converted = null;
- }
- else
- {
- string item = Latin1.GetString(val, 0, val.Length);
- converted = PythonCalls.Call(context, converter, item);
- }
- }
- else
- {
- int coltype = Sqlite3.sqlite3_column_type(this.statement.st, i);
- switch(coltype)
- {
- case Sqlite3.SQLITE_NULL:
- converted = null;
- break;
- case Sqlite3.SQLITE_INTEGER:
- long l = Sqlite3.sqlite3_column_int64(this.statement.st, i);
- if(l < int.MinValue || l > int.MaxValue)
- converted = l;
- else
- converted = (int)l;
- break;
- case Sqlite3.SQLITE_FLOAT:
- converted = Sqlite3.sqlite3_column_double(this.statement.st, i);
- break;
- case Sqlite3.SQLITE_TEXT:
- converted = Sqlite3.sqlite3_column_text(this.statement.st, i);
- break;
- case Sqlite3.SQLITE_BLOB:
- default:
- byte[] blob = Sqlite3.sqlite3_column_blob(this.statement.st, i);
- PythonBuffer buffer = new PythonBuffer(context, blob);
- converted = buffer;
- break;
- }
- }
- row[i] = converted;
- }
- return new PythonTuple(row);
- }
- public Cursor executescript(string operation)
- {
- connection.checkThread(); connection.checkConnection();
- this.connection.commit();
- sqlite3_stmt statement = null;
- string script = operation;
- bool statement_completed = false;
- while(true)
- {
- if(Sqlite3.sqlite3_complete(operation) == 0)
- break;
- statement_completed = true;
- int rc = Sqlite3.sqlite3_prepare(this.connection.db,
- operation,
- -1,
- ref statement,
- ref script);
- if(rc != Sqlite3.SQLITE_OK)
- throw GetSqliteError(this.connection.db, null);
- /* execute statement, and ignore results of SELECT statements */
- rc = Sqlite3.SQLITE_ROW;
- while(rc == Sqlite3.SQLITE_ROW)
- rc = Sqlite3.sqlite3_step(statement);
- if(rc != Sqlite3.SQLITE_DONE)
- {
- Sqlite3.sqlite3_finalize(statement);
- throw GetSqliteError(this.connection.db, null);
- }
- rc = Sqlite3.sqlite3_finalize(statement);
- if(rc != Sqlite3.SQLITE_OK)
- throw GetSqliteError(this.connection.db, null);
- }
- if(!statement_completed)
- throw MakeProgrammingError("you did not provide a complete SQL statement");
- return this;
- }
- public object __iter__()
- {
- return this;
- }
- public object next(CodeContext context)
- {
- object next_row_tuple, next_row;
- connection.checkThread(); connection.checkConnection();
- if(this.next_row == null)
- {
- if(this.statement != null)
- {
- this.statement.Reset();
- this.statement = null;
- }
- throw new StopIterationException();
- }
- next_row_tuple = this.next_row;
- this.next_row = null;
- if(this.row_factory != null)
- {
- next_row = PythonCalls.Call(context, this.row_factory, this, next_row_tuple);
- }
- else
- {
- next_row = next_row_tuple;
- }
- if(this.statement != null)
- {
- int rc = this.statement.RawStep();
- if(rc != Sqlite3.SQLITE_DONE && rc != Sqlite3.SQLITE_ROW)
- {
- this.statement.Reset();
- throw GetSqliteError(this.connection.db, this.statement.st);
- }
- if(rc == Sqlite3.SQLITE_ROW)
- this.next_row = fetchOneRow(context);
- }
- return next_row;
- }
- [Documentation("Fetches one row from the resultset.")]
- public object fetchone(CodeContext context)
- {
- try
- {
- return this.next(context);
- }
- catch(StopIterationException)
- {
- return null;
- }
- }
- public object fetchmany(CodeContext context)
- {
- return fetchmany(context, this.arraysize);
- }
- [Documentation("Fetches several rows from the resultset.")]
- public object fetchmany(CodeContext context, int size)
- {
- List result = new List();
- object item = fetchone(context);
- for(int i = 0; i < size && item != null; ++i, item = fetchone(context))
- result.Add(item);
- return result;
- }
- [Documentation("Fetches all rows from the resultset.")]
- public object fetchall(CodeContext context)
- {
- List result = new List();
- object item = fetchone(context);
- while(item != null)
- {
- result.Add(item);
- item = fetchone(context);
- }
- return result;
- }
- public object nextset()
- {
- return null;
- }
- [Documentation("Required by DB-API. Does nothing in IronPython.Sqlite3.")]
- public void setinputsizes(object sizes) { }
- [Documentation("Required by DB-API. Does nothing in IronPython.Sqlite3.")]
- public void setoutputsize(params object[] args) { }
- private bool buildRowCastMap()
- {
- if(this.connection.detect_types == 0)
- return true;
- row_cast_map = new List<object>();
- object converter = null;
- for(int i = 0; i < Sqlite3.sqlite3_column_count(this.statement.st); ++i)
- {
- converter = null;
- if((this.connection.detect_types & PARSE_COLNAMES) != 0)
- {
- string colname = Sqlite3.sqlite3_column_name(this.statement.st, i);
- if(colname != null)
- {
- Regex matchColname = new Regex(@"\[(\w+)\]");
- Match match = matchColname.Match(colname);
- if(match.Success)
- {
- string key = match.Groups[1].ToString();
- converter = getConverter(key);
- }
- }
- }
- if((converter == null) && ((this.connection.detect_types & PARSE_DECLTYPES) != 0))
- {
- string decltype = Sqlite3.sqlite3_column_decltype(this.statement.st, i);
- if(decltype != null)
- {
- Regex matchDecltype = new Regex(@"\b(\w+)\b");
- Match match = matchDecltype.Match(decltype);
- if(match.Success)
- {
- string py_decltype = match.Groups[1].ToString();
- converter = getConverter(py_decltype);
- }
- }
- }
- row_cast_map.Add(converter);
- }
- return true;
- }
- private object getConverter(string key)
- {
- object converter;
- return converters.TryGetValue(key.ToUpperInvariant(), out converter) ? converter : null;
- }
- #region IEnumerable Members
- public IEnumerator GetEnumerator()
- {
- List results = new List();
- try
- {
- while(true)
- results.append(this.next(this.context));
- }
- catch(StopIterationException) { }
- return results.GetEnumerator();
- }
- #endregion
- }
- }
- }