From 65961ed407a3d5614df40d1812e6331429c3729a Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 19 Jun 2013 01:03:43 +0700 Subject: [PATCH] #24 --- nejdb/Ejdb.DB/EJDB.cs | 47 +++++++----- nejdb/Ejdb.DB/EJDBQCursor.cs | 105 +++++++++++++++++++++++++++ nejdb/Ejdb.DB/EJDBQuery.cs | 139 +++++++++++++++++++++++++++++++++++- nejdb/Ejdb.DB/EJDBQueryException.cs | 25 +++++++ nejdb/nejdb.csproj | 2 + nejdb/nejdb.userprefs | 34 +++++---- tcejdb/ejdb.c | 13 +++- tcejdb/ejdb.h | 9 +-- 8 files changed, 331 insertions(+), 43 deletions(-) create mode 100644 nejdb/Ejdb.DB/EJDBQCursor.cs create mode 100644 nejdb/Ejdb.DB/EJDBQueryException.cs diff --git a/nejdb/Ejdb.DB/EJDB.cs b/nejdb/Ejdb.DB/EJDB.cs index 197d5f4..32a4aa1 100644 --- a/nejdb/Ejdb.DB/EJDB.cs +++ b/nejdb/Ejdb.DB/EJDB.cs @@ -57,17 +57,18 @@ namespace Ejdb.DB { public const string EJDB_LIB_NAME = "tcejdb"; IntPtr _db = IntPtr.Zero; - #region Functions + // + #region NativeRefs [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbnew")] - static extern IntPtr _ejdbnew(); + internal static extern IntPtr _ejdbnew(); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbdel")] - static extern IntPtr _ejdbdel([In] IntPtr db); + internal static extern IntPtr _ejdbdel([In] IntPtr db); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbopen")] - static extern bool _ejdbopen([In] IntPtr db, [In] IntPtr path, int mode); + internal static extern bool _ejdbopen([In] IntPtr db, [In] IntPtr path, int mode); - static bool _ejdbopen(IntPtr db, string path, int mode) { + internal static bool _ejdbopen(IntPtr db, string path, int mode) { IntPtr pptr = UnixMarshal.StringToHeap(path, Encoding.UTF8); try { return _ejdbopen(db, pptr, mode); @@ -77,21 +78,21 @@ namespace Ejdb.DB { } [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbclose")] - static extern bool _ejdbclose([In] IntPtr db); + internal static extern bool _ejdbclose([In] IntPtr db); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbisopen")] - static extern bool _ejdbisopen([In] IntPtr db); + internal static extern bool _ejdbisopen([In] IntPtr db); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbecode")] - static extern int _ejdbecode([In] IntPtr db); + internal static extern int _ejdbecode([In] IntPtr db); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdberrmsg")] - static extern IntPtr _ejdberrmsg(int ecode); + internal static extern IntPtr _ejdberrmsg(int ecode); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbgetcoll")] - static extern IntPtr _ejdbgetcoll([In] IntPtr db, [In] IntPtr cname); + internal static extern IntPtr _ejdbgetcoll([In] IntPtr db, [In] IntPtr cname); - static IntPtr _ejdbgetcoll(IntPtr db, string cname) { + internal static IntPtr _ejdbgetcoll(IntPtr db, string cname) { IntPtr cptr = UnixMarshal.StringToHeap(cname, Encoding.UTF8); try { return _ejdbgetcoll(db, cptr); @@ -101,9 +102,9 @@ namespace Ejdb.DB { } [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbcreatecoll")] - static extern IntPtr _ejdbcreatecoll([In] IntPtr db, [In] IntPtr cname, ref EJDBCollectionOptionsN? opts); + internal static extern IntPtr _ejdbcreatecoll([In] IntPtr db, [In] IntPtr cname, ref EJDBCollectionOptionsN? opts); - static IntPtr _ejdbcreatecoll(IntPtr db, String cname, EJDBCollectionOptionsN? opts) { + internal static IntPtr _ejdbcreatecoll(IntPtr db, String cname, EJDBCollectionOptionsN? opts) { IntPtr cptr = UnixMarshal.StringToHeap(cname, Encoding.UTF8); try { return _ejdbcreatecoll(db, cptr, ref opts); @@ -113,16 +114,16 @@ namespace Ejdb.DB { } //EJDB_EXPORT bool ejdbsavebson3(EJCOLL *jcoll, void *bsdata, bson_oid_t *oid, bool merge); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbsavebson3")] - static extern bool _ejdbsavebson([In] IntPtr coll, [In] byte[] bsdata, [Out] byte[] oid, [In] bool merge); + internal static extern bool _ejdbsavebson([In] IntPtr coll, [In] byte[] bsdata, [Out] byte[] oid, [In] bool merge); //EJDB_EXPORT bson* ejdbloadbson(EJCOLL *coll, const bson_oid_t *oid); [DllImport(EJDB_LIB_NAME, EntryPoint="ejdbloadbson")] - static extern IntPtr _ejdbloadbson([In] IntPtr coll, [In] byte[] oid); + internal static extern IntPtr _ejdbloadbson([In] IntPtr coll, [In] byte[] oid); //EJDB_EXPORT const char* bson_data2(const bson *b, int *bsize); [DllImport(EJDB_LIB_NAME, EntryPoint="bson_data2")] - static extern IntPtr _bson_data2([In] IntPtr bsptr, out int size); + internal static extern IntPtr _bson_data2([In] IntPtr bsptr, out int size); //EJDB_EXPORT void bson_del(bson *b); [DllImport(EJDB_LIB_NAME, EntryPoint="bson_del")] - static extern void _bson_del([In] IntPtr bsptr); + internal static extern void _bson_del([In] IntPtr bsptr); #endregion /// /// Gets the last DB error code or null if underlying native database object does not exist. @@ -264,11 +265,19 @@ namespace Ejdb.DB { /// The query object. /// BSON query spec. public EJDBQuery CreateQuery(BSONDocument qdoc) { - return new EJDBQuery(qdoc); + CheckDisposed(); + return new EJDBQuery(this, qdoc); } //.////////////////////////////////////////////////////////////////// // Private staff // //.////////////////////////////////////////////////////////////////// + internal IntPtr DBPtr { + get { + CheckDisposed(); + return _db; + } + } + byte[] BsonPtrIntoByteArray(IntPtr bsptr, bool deletebsptr = true) { if (bsptr == IntPtr.Zero) { return new byte[0]; @@ -283,7 +292,7 @@ namespace Ejdb.DB { return bsdata; } - void CheckDisposed() { + internal void CheckDisposed() { if (_db == IntPtr.Zero) { throw new ObjectDisposedException("Database is disposed"); } diff --git a/nejdb/Ejdb.DB/EJDBQCursor.cs b/nejdb/Ejdb.DB/EJDBQCursor.cs new file mode 100644 index 0000000..b81a0ca --- /dev/null +++ b/nejdb/Ejdb.DB/EJDBQCursor.cs @@ -0,0 +1,105 @@ +// ============================================================================================ +// .NET API for EJDB database library http://ejdb.org +// Copyright (C) 2012-2013 Softmotions Ltd +// +// This file is part of EJDB. +// EJDB is free software; you can redistribute it and/or modify it under the terms of +// the GNU Lesser General Public License as published by the Free Software Foundation; either +// version 2.1 of the License or any later version. EJDB is distributed in the hope +// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +// License for more details. +// You should have received a copy of the GNU Lesser General Public License along with EJDB; +// if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA. +// ============================================================================================ +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using Ejdb.BSON; + +namespace Ejdb.DB { + + /// + /// Query result set container. + /// + public class EJDBQCursor : IDisposable, IEnumerable { + //optional query execution log buffer + string _log; + //current cursor position + int _pos; + //cursor length + int _len; + //Pointer to the result set list + IntPtr _qresptr; + //EJDB_EXPORT void ejdbqresultdispose(EJQRESULT qr); + [DllImport(EJDB.EJDB_LIB_NAME, EntryPoint="ejdbqresultdispose")] + static extern void _ejdbqresultdispose([In] IntPtr qres); + //const void* ejdbqresultbsondata(EJQRESULT qr, int pos, int *size) + [DllImport(EJDB.EJDB_LIB_NAME, EntryPoint="ejdbqresultbsondata")] + static extern IntPtr _ejdbqresultbsondata([In] IntPtr qres, [In] int pos, out int size); + + /// + /// Gets the number of result records stored in this cursor. + /// + public int Length { + get { + return _len; + } + } + + /// + /// Gets optional query execution log. + /// + /// The log. + public string Log { + get { + return _log; + } + } + + internal EJDBQCursor(IntPtr qresptr, int len, string log = null) { + _qresptr = qresptr; + _log = log; + _len = len; + } + + public BSONIterator Next() { + if (_qresptr == IntPtr.Zero || _pos >= _len) { + return null; + } + //static extern IntPtr _ejdbqresultbsondata([In] IntPtr qres, [In] int idx, out int size) + int size; + IntPtr bsdataptr = _ejdbqresultbsondata(_qresptr, _pos, out size); + if (bsdataptr == IntPtr.Zero) { + return null; + } + byte[] bsdata = new byte[size]; + Marshal.Copy(bsdataptr, bsdata, 0, bsdata.Length); + _pos++; + return new BSONIterator(bsdata); + } + + public IEnumerator GetEnumerator() { + BSONIterator it; + while ((it = Next()) != null) { + yield return it; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + public void Dispose() { + _log = null; + _pos = 0; + if (_qresptr != IntPtr.Zero) { + //static extern void _ejdbqresultdispose([In] IntPtr qres); + _ejdbqresultdispose(_qresptr); + _qresptr = IntPtr.Zero; + } + } + } +} + diff --git a/nejdb/Ejdb.DB/EJDBQuery.cs b/nejdb/Ejdb.DB/EJDBQuery.cs index 1e69891..4213c0a 100644 --- a/nejdb/Ejdb.DB/EJDBQuery.cs +++ b/nejdb/Ejdb.DB/EJDBQuery.cs @@ -15,18 +15,151 @@ // ============================================================================================ using System; using Ejdb.BSON; +using System.Runtime.InteropServices; namespace Ejdb.DB { + /// + /// EJDB query. + /// public class EJDBQuery : IDisposable { - IntPtr _qptr; + //Query search mode flags in ejdbqryexecute() + /// + /// Query only count(*) + /// + public const int JBQRYCOUNT_FLAG = 1; - internal EJDBQuery(BSONDocument qdoc) { + /// + /// Fetch first record only. + /// + public const int JBQRYFINDONE_FLAG = 1 << 1; + + /// + /// Explain query execution and + /// store query execution log into + /// + public const int EXPLAIN_FLAG = 1 << 16; + + /// + /// EJDB query object EJQ pointer. + /// + IntPtr _qptr = IntPtr.Zero; + + /// + /// Database reference. + /// + EJDB _jb; + + /// + /// Last used query hints document. + /// + BSONDocument _hints; + // + #region NativeRefs + //EJDB_EXPORT void ejdbquerydel(EJQ *q); + [DllImport(EJDB.EJDB_LIB_NAME, EntryPoint="ejdbquerydel")] + static extern void _ejdbquerydel([In] IntPtr qptr); + //EJDB_EXPORT EJQ* ejdbcreatequery2(EJDB *jb, void *qbsdata); + [DllImport(EJDB.EJDB_LIB_NAME, EntryPoint="ejdbcreatequery2")] + static extern IntPtr _ejdbcreatequery([In] IntPtr jb, [In] byte[] bsdata); + //EJDB_EXPORT EJQ* ejdbqueryhints(EJDB *jb, EJQ *q, void *hintsbsdata) + [DllImport(EJDB.EJDB_LIB_NAME, EntryPoint="ejdbqueryhints")] + static extern IntPtr _ejdbqueryhints([In] IntPtr jb, [In] IntPtr qptr, [In] byte[] bsdata); + //EJDB_EXPORT EJQ* ejdbqueryaddor(EJDB *jb, EJQ *q, void *orbsdata) + [DllImport(EJDB.EJDB_LIB_NAME, EntryPoint="ejdbqueryaddor")] + static extern IntPtr _ejdbqueryaddor([In] IntPtr jb, [In] IntPtr qptr, [In] byte[] bsdata); + //EJDB_EXPORT EJQRESULT ejdbqryexecute(EJCOLL *jcoll, const EJQ *q, uint32_t *count, int qflags, TCXSTR *log) + [DllImport(EJDB.EJDB_LIB_NAME, EntryPoint="ejdbqryexecute")] + static extern IntPtr _ejdbqryexecute([In] IntPtr jcoll, [In] IntPtr q, out int count, [In] int qflags, [In] IntPtr logxstr); + #endregion + internal EJDBQuery(EJDB jb, BSONDocument qdoc) { + _qptr = _ejdbcreatequery(jb.DBPtr, qdoc.ToByteArray()); + if (_qptr == IntPtr.Zero) { + throw new EJDBQueryException(jb); + } + _jb = jb; + } + + /// + /// Append OR joined restriction to this query. + /// + /// This query object. + /// Query document. + public EJDBQuery AppendOR(BSONDocument doc) { + CheckDisposed(); + //static extern IntPtr _ejdbqueryaddor([In] IntPtr jb, [In] IntPtr qptr, [In] byte[] bsdata); + IntPtr qptr = _ejdbqueryaddor(_jb.DBPtr, _qptr, doc.ToByteArray()); + if (qptr == IntPtr.Zero) { + throw new EJDBQueryException(_jb); + } + return this; + } + + /// + /// Sets the query hints. + /// + /// + /// Replaces previous hints associated with this query. + /// + /// This query object. + /// Hints document. + public EJDBQuery SetHints(BSONDocument hints) { + CheckDisposed(); + //static extern IntPtr _ejdbqueryhints([In] IntPtr jb, [In] IntPtr qptr, [In] byte[] bsdata); + IntPtr qptr = _ejdbqueryhints(_jb.DBPtr, _qptr, hints.ToByteArray()); + if (qptr == IntPtr.Zero) { + throw new EJDBQueryException(_jb); + } + _hints = hints; + return this; + } + + public EJDBQCursor Find(string cname, int flags = 0) { + IntPtr cptr = EJDB._ejdbgetcoll(_jb.DBPtr, cname); + if (cptr == IntPtr.Zero) { + return new EJDBQCursor(IntPtr.Zero, 0); + } + //static extern IntPtr _ejdbqryexecute([In] IntPtr jcoll, [In] IntPtr q, out int count, [In] int qflags, [In] IntPtr logxstr); + //todo + //return new EJDBQCursor(); + return null; + } + + public BSONIterator FinOne(string cname, int flags = 0) { + using (EJDBQCursor cur = Find(cname, flags | JBQRYFINDONE_FLAG)) { + return cur.Next(); + } + } + + public int Count(string cname, int flags = 0) { + using (EJDBQCursor cur = Find(cname, flags | JBQRYCOUNT_FLAG)) { + return cur.Length; + } + } + + public int Update(string cname, int flags = 0) { + using (EJDBQCursor cur = Find(cname, flags | JBQRYCOUNT_FLAG)) { + return cur.Length; + } } public void Dispose() { - throw new NotImplementedException(); + if (_qptr != IntPtr.Zero) { + //static extern void _ejdbquerydel([In] IntPtr qptr); + _ejdbquerydel(_qptr); + _qptr = IntPtr.Zero; + } + if (_jb != null) { + _jb = null; + } + _hints = null; + } + + internal void CheckDisposed() { + if (_jb == null || _qptr == IntPtr.Zero) { + throw new ObjectDisposedException("Query object is disposed"); + } } } } diff --git a/nejdb/Ejdb.DB/EJDBQueryException.cs b/nejdb/Ejdb.DB/EJDBQueryException.cs new file mode 100644 index 0000000..fc92d12 --- /dev/null +++ b/nejdb/Ejdb.DB/EJDBQueryException.cs @@ -0,0 +1,25 @@ +// ============================================================================================ +// .NET API for EJDB database library http://ejdb.org +// Copyright (C) 2012-2013 Softmotions Ltd +// +// This file is part of EJDB. +// EJDB is free software; you can redistribute it and/or modify it under the terms of +// the GNU Lesser General Public License as published by the Free Software Foundation; either +// version 2.1 of the License or any later version. EJDB is distributed in the hope +// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +// License for more details. +// You should have received a copy of the GNU Lesser General Public License along with EJDB; +// if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA. +// ============================================================================================ +using System; + +namespace Ejdb.DB { + + public class EJDBQueryException : EJDBException { + public EJDBQueryException(EJDB db) : base(db) { + } + } +} + diff --git a/nejdb/nejdb.csproj b/nejdb/nejdb.csproj index aceb9c7..8a7fbdc 100644 --- a/nejdb/nejdb.csproj +++ b/nejdb/nejdb.csproj @@ -65,6 +65,8 @@ + + diff --git a/nejdb/nejdb.userprefs b/nejdb/nejdb.userprefs index 78732cb..258944f 100644 --- a/nejdb/nejdb.userprefs +++ b/nejdb/nejdb.userprefs @@ -1,16 +1,13 @@  - + - - - - - - - - - + + + + + + @@ -20,7 +17,7 @@ - + @@ -42,12 +39,21 @@ _input.BaseStream.Position - - - + + + + + + + + + + + + diff --git a/tcejdb/ejdb.c b/tcejdb/ejdb.c index 4cdde67..e902816 100644 --- a/tcejdb/ejdb.c +++ b/tcejdb/ejdb.c @@ -746,9 +746,14 @@ int ejdbqresultnum(EJQRESULT qr) { return qr ? tclistnum(qr) : 0; } -const void* ejdbqresultbsondata(EJQRESULT qr, int idx) { - if (!qr || idx < 0) return NULL; - return tclistval2(qr, idx); +const void* ejdbqresultbsondata(EJQRESULT qr, int pos, int *size) { + if (!qr || pos < 0) { + *size = 0; + return NULL; + } + const void *bsdata = tclistval2(qr, pos); + *size = (bsdata != NULL) ? bson_size2(bsdata) : 0; + return bsdata; } void ejdbqresultdispose(EJQRESULT qr) { @@ -3101,6 +3106,8 @@ static bool _qrypreprocess(EJCOLL *jcoll, EJQ *ejq, int qflags, EJQF **mqf, if (BSON_IS_NUM_TYPE(bt)) { int64_t v = bson_iterator_long(&it); ejq->max = (uint32_t) ((v < 0) ? 0 : v); + } else if (qflags & JBQRYFINDONE) { + ejq->max = (uint32_t) 1; } if (!(qflags & JBQRYCOUNT)) { bt = bson_find(&it, ejq->hints, "$fields"); //Collect required fields diff --git a/tcejdb/ejdb.h b/tcejdb/ejdb.h index aaa580b..e01bde7 100644 --- a/tcejdb/ejdb.h +++ b/tcejdb/ejdb.h @@ -83,7 +83,8 @@ enum { /** Index modes, index types. */ }; enum { /*< Query search mode flags in ejdbqryexecute() */ - JBQRYCOUNT = 1 /*< Query only count(*) */ + JBQRYCOUNT = 1, /*< Query only count(*) */ + JBQRYFINDONE = 1 << 1 /*< Fetch first record only */ }; EJDB_EXPORT bool ejdbisvalidoidstr(const char *oid); @@ -432,12 +433,12 @@ EJDB_EXPORT EJQRESULT ejdbqryexecute(EJCOLL *jcoll, const EJQ *q, uint32_t *coun EJDB_EXPORT int ejdbqresultnum(EJQRESULT qr); /** - * Gets the pointer of query result BSON data buffer at the specified index `idx`. + * Gets the pointer of query result BSON data buffer at the specified position `pos`. * If `qr` is `NULL` or `idx` is put of index range then the `NULL` pointer will be returned. * @param qr Query result set object. - * @param idx Zero based index of the record. + * @param pos Zero based position of the record. */ -EJDB_EXPORT const void* ejdbqresultbsondata(EJQRESULT qr, int idx); +EJDB_EXPORT const void* ejdbqresultbsondata(EJQRESULT qr, int pos, int *size); /** * Disposes the query result set and frees a records buffers. -- 2.7.4