123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- /*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- package cs.db;
- import sys.db.*;
- import cs.system.data.*;
- class AdoNet {
- public static function create(cnx:IDbConnection, dbName:String):Connection {
- return new AdoConnection(cnx, dbName);
- }
- }
- private class AdoConnection implements Connection {
- private static var ids = 0;
- private var id:Int;
- private var cnx:IDbConnection;
- // escape handling
- private var escapeRegex:EReg;
- private var escapes:Array<IDbDataParameter>;
- private var name:String;
- private var command:IDbCommand;
- private var transaction:IDbTransaction;
- public function new(cnx, name:String) {
- this.id = cs.system.threading.Interlocked.Increment(ids);
- this.cnx = cnx;
- this.name = name;
- this.escapes = [];
- this.command = cnx.CreateCommand();
- this.escapeRegex = ~/@HX_ESCAPE(\d+)_(\d+)/;
- }
- public function close():Void {
- cnx.Close();
- }
- public function escape(s:String):String {
- var param = command.CreateParameter();
- var name = "@HX_ESCAPE" + id + "_" + escapes.push(param) + "";
- param.ParameterName = name;
- param.Value = s;
- return name;
- }
- public function quote(s:String):String {
- var param = command.CreateParameter();
- var name = "@HX_ESCAPE" + id + "_" + escapes.push(param) + "";
- param.ParameterName = name;
- param.Value = s;
- return name;
- }
- public function addValue(s:StringBuf, v:Dynamic) {
- if (Std.isOfType(v, Date)) {
- v = Std.string(v);
- } else if (Std.isOfType(v, haxe.io.Bytes)) {
- var bt:haxe.io.Bytes = v;
- v = bt.getData();
- }
- var param = command.CreateParameter();
- var name = "@HX_ESCAPE" + id + "_" + escapes.push(param) + "";
- param.ParameterName = name;
- param.Value = v;
- s.add(name);
- }
- public function lastInsertId():Int {
- var ret = cnx.CreateCommand();
- ret.CommandText = switch (name) {
- case 'SQLite':
- 'SELECT last_insert_rowid()';
- case _:
- 'SELECT @@IDENTITY';
- }
- ret.CommandType = CommandType.Text;
- var r = cast ret.ExecuteScalar();
- ret.Dispose();
- return r;
- }
- public function dbName():String {
- return name;
- }
- public function startTransaction():Void {
- if (this.transaction != null)
- throw 'Transaction already active';
- this.transaction = cnx.BeginTransaction();
- }
- public function commit():Void {
- if (this.transaction == null)
- throw 'No transaction was initiated';
- this.transaction.Commit();
- }
- public function rollback():Void {
- if (this.transaction == null)
- throw 'No transaction was initiated';
- this.transaction.Rollback();
- }
- private static function getFirstStatement(s:String) {
- var buf = new StringBuf();
- var hasData = false;
- var chr = 0, i = 0;
- inline function getch()
- return chr = StringTools.fastCodeAt(s, i++);
- while (!StringTools.isEof(getch())) {
- inline function peek() {
- var c = StringTools.fastCodeAt(s, i);
- if (StringTools.isEof(c))
- break;
- return c;
- }
- switch (chr) {
- case ' '.code | '\t'.code | '\n'.code:
- if (hasData)
- return buf.toString();
- case '-'.code if (peek() == '-'.code):
- if (hasData)
- return buf.toString();
- while (!StringTools.isEof(getch())) {
- if (chr == '\n'.code)
- break;
- }
- case '#'.code:
- if (hasData)
- return buf.toString();
- while (!StringTools.isEof(getch())) {
- if (chr == '\n'.code)
- break;
- }
- case '/'.code if (peek() == '*'.code):
- i++;
- if (hasData)
- return buf.toString();
- while (!StringTools.isEof(getch())) {
- if (chr == '*'.code && peek() == '/'.code) {
- i++;
- break;
- }
- }
- case _:
- hasData = true;
- buf.addChar(chr);
- }
- }
- return buf.toString();
- }
- public function request(s:String):ResultSet {
- var newst = new StringBuf();
- // cycle through the request string, adding any @HX_ESCAPE reference to the command
- var ret:ResultSet = null;
- var r = escapeRegex;
- var myid = id + "", escapes = escapes, elen = escapes.length;
- var cmd = this.command;
- try {
- while (r.match(s)) {
- var id = r.matched(1);
- #if debug
- if (id != myid)
- throw "Request quotes are only valid for one single request; They can't be cached.";
- #end
- newst.add(r.matchedLeft());
- var eid = Std.parseInt(r.matched(2));
- #if debug
- if (eid == null || eid > elen)
- throw "Invalid request quote ID " + eid;
- #end
- cmd.Parameters.Add(escapes[eid - 1]);
- newst.add(escapes[eid - 1].ParameterName);
- s = r.matchedRight();
- }
- newst.add(s);
- s = newst.toString();
- cmd.CommandText = s;
- var stmt = getFirstStatement(s).toLowerCase();
- if (stmt == 'select') {
- ret = new AdoResultSet(cmd.ExecuteReader());
- } else {
- cmd.ExecuteNonQuery();
- ret = EmptyResultSet.empty;
- }
- if (escapes.length != 0)
- this.escapes = [];
- this.id = cs.system.threading.Interlocked.Increment(ids);
- cmd.Dispose();
- this.command = cnx.CreateCommand();
- return ret;
- } catch (e:Dynamic) {
- if (escapes.length != 0)
- this.escapes = [];
- this.id = cs.system.threading.Interlocked.Increment(ids);
- try {
- cmd.Dispose();
- } catch (e:Dynamic) {}
- this.command = cnx.CreateCommand();
- cs.Lib.rethrow(e);
- }
- return null;
- }
- }
- private class AdoResultSet implements ResultSet {
- public var length(get, null):Int;
- public var nfields(get, null):Int;
- private var reader:IDataReader;
- private var didNext:Bool;
- private var names:Array<String>;
- private var types:Array<Class<Dynamic>>;
- public function new(reader) {
- this.reader = reader;
- this.names = [for (i in 0...reader.FieldCount) reader.GetName(i)];
- this.types = [for (i in 0...names.length) cs.Lib.fromNativeType(reader.GetFieldType(i))];
- }
- private function get_length() {
- return reader.Depth;
- }
- private function get_nfields() {
- return names.length;
- }
- public function hasNext():Bool {
- didNext = true;
- return reader.Read();
- }
- public function next():Dynamic {
- if (!didNext && !hasNext())
- return null;
- didNext = false;
- var ret = {}, names = names, types = types;
- for (i in 0...names.length) {
- var name = names[i], t = types[i], val:Dynamic = null;
- if (reader.IsDBNull(i)) {
- val = null;
- } else if (t == cs.system.Single) {
- val = reader.GetDouble(i);
- } else if (t == cs.system.DateTime || t == cs.system.TimeSpan) {
- var d = reader.GetDateTime(i);
- if (d != null)
- val = @:privateAccess Date.fromNative(d);
- } else if (t == cs.system.DBNull) {
- val = null;
- } else if (t == cs.system.Byte) {
- var v2:cs.StdTypes.UInt8 = reader.GetValue(i);
- val = cast(v2, Int);
- } else if (Std.string(t) == 'System.Byte[]') {
- val = haxe.io.Bytes.ofData(reader.GetValue(i));
- } else {
- val = reader.GetValue(i);
- }
- if (Std.isOfType(val, cs.system.DBNull))
- val = null;
- Reflect.setField(ret, name, val);
- }
- return ret;
- }
- public function results():List<Dynamic> {
- var l = new List();
- while (hasNext())
- l.add(next());
- return l;
- }
- public function getResult(n:Int):String {
- return reader.GetString(n);
- }
- public function getIntResult(n:Int):Int {
- return reader.GetInt32(n);
- }
- public function getFloatResult(n:Int):Float {
- return reader.GetDouble(n);
- }
- public function getFieldsNames():Null<Array<String>> {
- return names;
- }
- }
- private class EmptyResultSet implements ResultSet {
- public static var empty = new EmptyResultSet();
- public function new() {}
- public var length(get, null):Int;
- public var nfields(get, null):Int;
- private function get_length() {
- return 0;
- }
- private function get_nfields() {
- return 0;
- }
- public function hasNext():Bool
- return false;
- public function next():Dynamic
- return null;
- public function results():List<Dynamic>
- return new List();
- public function getResult(n:Int):String
- return null;
- public function getIntResult(n:Int):Int
- return 0;
- public function getFloatResult(n:Int):Float
- return 0;
- public function getFieldsNames():Null<Array<String>>
- return null;
- }
|