f702ba1999-06-01Marcus Comstedt // Remote SQL server interface
a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
f702ba1999-06-01Marcus Comstedt #define RSQL_PORT 3994 #define RSQL_VERSION 1 #if constant(thread_create) #define LOCK object key=mutex->lock() #define UNLOCK destruct(key)
9eaf1d2008-06-28Martin Nilsson protected private object(Thread.Mutex) mutex = Thread.Mutex();
f702ba1999-06-01Marcus Comstedt #else #define LOCK #define UNLOCK #endif
5d16962003-04-22Martin Nilsson #define ERROR(X ...) predef::error(X)
f702ba1999-06-01Marcus Comstedt 
eeacb22018-01-24Stephen R. van den Berg inherit __builtin.Sql.Connection;
9eaf1d2008-06-28Martin Nilsson protected object(Stdio.File) sock; protected int seqno = 0;
f702ba1999-06-01Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson protected private string host, user, pw; protected private int port;
0a5ec32012-04-12Henrik Grubbström (Grubba) protected private mapping options;
f702ba1999-06-01Marcus Comstedt 
d8bade2013-05-17Henrik Grubbström (Grubba) protected private string db;
9eaf1d2008-06-28Martin Nilsson protected void low_reconnect()
f702ba1999-06-01Marcus Comstedt { object losock = Stdio.File(); if(sock) destruct(sock);
3f35192005-08-18Henrik Grubbström (Grubba)  if (host == "") { #ifdef ENABLE_SPAWN_RSQLD Process.spawn_pike(({ "-x", "rsqld", "--stdin" }), ([ "stdin":losock->pipe(), ])); #else /* !ENABLE_SPAWN_RSQLD */ ERROR("Automatic spawning of rsqld not enabled.\n"); #endif /* ENABLE_SPAWN_RSQLD */ } else { if(!losock->connect(host, port||RSQL_PORT)) ERROR("Can't connect to "+host+(port? ":"+port:"")+": "+
51f1ce2015-09-06Martin Nilsson  strerror(losock->errno())+".\n");
3f35192005-08-18Henrik Grubbström (Grubba)  }
5d16962003-04-22Martin Nilsson  if(8!=losock->write("RSQL%4c", RSQL_VERSION) ||
f702ba1999-06-01Marcus Comstedt  losock->read(4) != "SQL!") { destruct(losock);
5d16962003-04-22Martin Nilsson  ERROR("Initial handshake error on "+host+(port? ":"+port:"")+"\n");
f702ba1999-06-01Marcus Comstedt  } sock = losock;
0a5ec32012-04-12Henrik Grubbström (Grubba)  // FIXME: Propagate options?
f702ba1999-06-01Marcus Comstedt  if(!do_request('L', ({user,pw}), 1)) { sock = 0; if(losock) destruct(losock);
5d16962003-04-22Martin Nilsson  ERROR("Login refused on "+host+(port? ":"+port:"")+"\n");
f702ba1999-06-01Marcus Comstedt  } }
9eaf1d2008-06-28Martin Nilsson protected void low_connect(string the_host, int the_port, string the_user,
0a5ec32012-04-12Henrik Grubbström (Grubba)  string the_pw, mapping the_options)
f702ba1999-06-01Marcus Comstedt { host = the_host; port = the_port; user = the_user; pw = the_pw;
0a5ec32012-04-12Henrik Grubbström (Grubba)  options = the_options;
f702ba1999-06-01Marcus Comstedt  low_reconnect(); }
9eaf1d2008-06-28Martin Nilsson protected mixed do_request(int cmd, mixed|void arg, int|void noreconnect)
f702ba1999-06-01Marcus Comstedt { LOCK;
07bf2b2013-05-17Henrik Grubbström (Grubba)  noreconnect = noreconnect || !options || !options->reconnect;
f702ba1999-06-01Marcus Comstedt  if(!sock) if(noreconnect) { UNLOCK;
5d16962003-04-22Martin Nilsson  ERROR("No connection\n");
d8bade2013-05-17Henrik Grubbström (Grubba)  } else { UNLOCK;
f702ba1999-06-01Marcus Comstedt  low_reconnect();
d8bade2013-05-17Henrik Grubbström (Grubba)  select_db(db); LOCK; noreconnect = 1; }
f702ba1999-06-01Marcus Comstedt  arg = (arg? encode_value(arg) : "");
5d16962003-04-22Martin Nilsson  sock->write("?<%c>%4c%4c%s", cmd, ++seqno, sizeof(arg), arg);
f702ba1999-06-01Marcus Comstedt  string res; int rlen; if((res = sock->read(12)) && sizeof(res)==12 && res[1..7]==sprintf("<%c>%4c", cmd, seqno) && sscanf(res[8..11], "%4c", rlen)==1 && (res[0]=='.' || res[0]=='!')) { mixed rdat = (rlen? sock->read(rlen) : ""); if((!rdat) || sizeof(rdat)!=rlen) { destruct(sock); UNLOCK; if(noreconnect)
5d16962003-04-22Martin Nilsson  ERROR("RSQL Phase error, disconnected\n");
d8bade2013-05-17Henrik Grubbström (Grubba)  else return do_request(cmd, arg);
f702ba1999-06-01Marcus Comstedt  } UNLOCK; rdat = (sizeof(rdat)? decode_value(rdat):0); switch(res[0]) { case '.': return rdat;
5d16962003-04-22Martin Nilsson  case '!': ERROR(rdat);
f702ba1999-06-01Marcus Comstedt  }
5d16962003-04-22Martin Nilsson  ERROR("Internal error\n");
f702ba1999-06-01Marcus Comstedt  } else { destruct(sock); UNLOCK; if(noreconnect)
d8bade2013-05-17Henrik Grubbström (Grubba)  ERROR("RSQL connection closed\n"); else return do_request(cmd, arg);
f702ba1999-06-01Marcus Comstedt  } }
2ca6cd2013-05-22Henrik Grubbström (Grubba) protected mixed do_proxy(string cmd, array(mixed) args) { return do_request('P', ({ cmd, args })); }
d8bade2013-05-17Henrik Grubbström (Grubba) void select_db(string the_db)
f702ba1999-06-01Marcus Comstedt {
d8bade2013-05-17Henrik Grubbström (Grubba)  do_request('D', the_db); db = the_db;
f702ba1999-06-01Marcus Comstedt } int|string error() { return do_request('E'); } void create_db(string db) { do_request('C', db); } void drop_db(string db) { do_request('X', db); } string server_info() { return do_request('I'); } string host_info() { return do_request('i'); } void shutdown() { do_request('s'); }
9c97a62013-05-17Henrik Grubbström (Grubba) int ping() { catch { return do_request('p', UNDEFINED, 1); }; catch { if (do_request('p') >= 0) return 1; }; return -1; }
f702ba1999-06-01Marcus Comstedt void reload() { do_request('r'); } array(string) list_dbs(string|void wild) { return do_request('l', wild); } array(string) list_tables(string|void wild) { return do_request('t', wild); } array(mapping(string:mixed)) list_fields(string ... args) { return do_request('f', args); } string quote(string s) {
eeacb22018-01-24Stephen R. van den Berg  return do_request('q', s);
f702ba1999-06-01Marcus Comstedt }
2d60222013-05-22Henrik Grubbström (Grubba) protected class RemoteResult(protected function(int,mixed:mixed) do_request, protected mixed qid)
f702ba1999-06-01Marcus Comstedt {
eeacb22018-01-24Stephen R. van den Berg  inherit __builtin.Sql.Result;
c071bc2017-11-05Henrik Grubbström (Grubba)  protected void _destruct()
2d60222013-05-22Henrik Grubbström (Grubba)  { do_request('Z', qid); }
6458a72000-04-29Francesco Chemolli 
2d60222013-05-22Henrik Grubbström (Grubba)  int|array(string|int) fetch_row() { return do_request('R', qid); }
f702ba1999-06-01Marcus Comstedt 
2d60222013-05-22Henrik Grubbström (Grubba)  array(mapping(string:mixed)) fetch_fields() { return do_request('F', qid); }
f702ba1999-06-01Marcus Comstedt 
2d60222013-05-22Henrik Grubbström (Grubba)  int num_rows() { return do_request('N', qid); }
f702ba1999-06-01Marcus Comstedt 
2d60222013-05-22Henrik Grubbström (Grubba)  int num_fields() { return do_request('n', qid); }
f702ba1999-06-01Marcus Comstedt 
2d60222013-05-22Henrik Grubbström (Grubba)  int eof() { return do_request('e', qid); }
f702ba1999-06-01Marcus Comstedt 
2d60222013-05-22Henrik Grubbström (Grubba)  void seek(int skip) { do_request('S', ({qid,skip})); } }
f702ba1999-06-01Marcus Comstedt 
eeacb22018-01-24Stephen R. van den Berg variant RemoteResult big_query(object|string q)
2d60222013-05-22Henrik Grubbström (Grubba) { mixed qid = do_request('Q', q); return qid && RemoteResult(do_request, qid); }
f702ba1999-06-01Marcus Comstedt 
eeacb22018-01-24Stephen R. van den Berg variant RemoteResult big_typed_query(object|string q)
2d60222013-05-22Henrik Grubbström (Grubba) { mixed qid = do_request('Q', ({ "big_typed_query", q })); return qid && RemoteResult(do_request, qid); }
eeacb22018-01-24Stephen R. van den Berg variant RemoteResult streaming_query(object|string q)
2d60222013-05-22Henrik Grubbström (Grubba) { mixed qid = do_request('Q', ({ "streaming_query", q })); return qid && RemoteResult(do_request, qid); }
eeacb22018-01-24Stephen R. van den Berg variant RemoteResult streaming_typed_query(object|string q)
2d60222013-05-22Henrik Grubbström (Grubba) { mixed qid = do_request('Q', ({ "streaming_typed_query", q })); return qid && RemoteResult(do_request, qid);
f702ba1999-06-01Marcus Comstedt } array(mapping(string:mixed)) query(mixed ... args) { return do_request('@', args); }
3012472013-05-17Henrik Grubbström (Grubba) int insert_id() { return do_request('#'); }
04ce8b2013-05-22Henrik Grubbström (Grubba) string get_charset() { return do_request('h'); } void set_charset(string charset) { do_request('H', charset); }
2ca6cd2013-05-22Henrik Grubbström (Grubba) protected function|mixed `->(string cmd) { return ::`->(cmd) || lambda(mixed ... args) { return do_proxy(cmd, args); }; }
0a5ec32012-04-12Henrik Grubbström (Grubba) void create(string|void host, string|void db, string|void user, string|void _pw, mapping|void options)
f702ba1999-06-01Marcus Comstedt {
1dc3fb2008-01-09Martin Stjernholm  string pw = _pw; _pw = "CENSORED";
f702ba1999-06-01Marcus Comstedt  // Reconstruct the original URL (minus rsql://) if(!host) {
d7bd0e2003-12-31Martin Nilsson  destruct(this);
f702ba1999-06-01Marcus Comstedt  return; } if(db) host = host+"/"+db; if(pw) user = (user||"")+":"+pw; if(user) host = user+"@"+host; array(string) arr = host/"/"; if(sizeof(arr)<2) {
d7bd0e2003-12-31Martin Nilsson  destruct(this);
f702ba1999-06-01Marcus Comstedt  return; } db = arr[1..]*"/"; host = arr[0]; user = pw = 0; arr = host/"@"; if(sizeof(arr)>1) {
8a531a2006-11-04Martin Nilsson  user = arr[..<1]*"@";
f702ba1999-06-01Marcus Comstedt  host = arr[-1]; arr = user/":"; if(sizeof(arr)>1) { pw = arr[1..]*":"; user = arr[0]; } } int port = 0; sscanf(host, "%s:%d", host, port);
0a5ec32012-04-12Henrik Grubbström (Grubba)  low_connect(host, port, user, pw, options);
f702ba1999-06-01Marcus Comstedt  select_db(db); }