1 #include "Sql.h"
2
3 namespace Upp {
4
5 #define LLOG(x) // DLOG(x)
6
SqlToBool(const String & s)7 bool SqlToBool(const String& s) {
8 return !(IsNull(s) || *s == '0' && s[1] == '\0');
9 }
10
SqlToBool(const Value & v)11 bool SqlToBool(const Value& v) {
12 if(IsNull(v)) return false;
13 if(IsString(v)) return SqlToBool(String(v));
14 if(IsNumber(v)) return (int) v;
15 return true;
16 }
17
BoolToSql(bool b)18 const String& BoolToSql(bool b) {
19 static String T("1"), F("0");
20 return b ? T : F;
21 }
22
Field(Ref f)23 void FieldOperator::Field(Ref f) {}
24
Field(const char * name,Ref f)25 void FieldOperator::Field(const char *name, Ref f) { Field(f); }
26
Field(const char * name,Ref f,bool * b)27 void FieldOperator::Field(const char *name, Ref f, bool *b) { Field(name, f); }
28
Width(int width)29 void FieldOperator::Width(int width) {}
30
31
operator ()(const char * name,bool & b)32 FieldOperator& FieldOperator::operator()(const char *name, bool& b) {
33 String x = BoolToSql(b);
34 Field(name, x, &b);
35 b = SqlToBool(x);
36 return *this;
37 }
38
39 static char sql_error[] = "Database error";
40
41 #ifndef NOAPPSQL
SqlExc()42 SqlExc::SqlExc() : Exc(sql_error) {
43 SetSessionError(SQL.GetSession());
44 }
45 #endif
46
SqlExc(const SqlSession & session)47 SqlExc::SqlExc(const SqlSession& session) : Exc(sql_error) {
48 SetSessionError(session);
49 }
50
SqlExc(const Sql & sql)51 SqlExc::SqlExc(const Sql& sql) : Exc(sql_error) {
52 SetSessionError(sql.GetSession());
53 }
54
SetSessionError(const SqlSession & session)55 void SqlExc::SetSessionError(const SqlSession& session) {
56 if(session.WasError())
57 *this = session.GetLastError();
58 else
59 *this = String(sql_error);
60 *this << "\nSQL error: " << session.GetErrorStatement();
61 }
62
SqlConnection()63 SqlConnection::SqlConnection() { parse = true; fetchrows = 32; longsize = 16384; }
~SqlConnection()64 SqlConnection::~SqlConnection() {}
65
Cancel()66 void SqlConnection::Cancel() {}
67
GetRowsProcessed() const68 int SqlConnection::GetRowsProcessed() const {
69 NEVER();
70 return 0;
71 }
72
GetUser() const73 String SqlConnection::GetUser() const {
74 NEVER();
75 return Null;
76 }
77
GetInsertedId() const78 Value SqlConnection::GetInsertedId() const
79 {
80 NEVER();
81 return Null;
82 }
83
Compile(const SqlStatement & s)84 String Sql::Compile(const SqlStatement& s)
85 {
86 byte dialect = GetDialect();
87 ASSERT(dialect);
88 return s.Get(dialect);
89 }
90
Clear()91 void Sql::Clear() {
92 if(cn) {
93 cn->Cancel();
94 cn->parse = true;
95 }
96 }
97
SetParam(int i,const Value & val)98 void Sql::SetParam(int i, const Value& val) {
99 cn->SetParam(i, val);
100 if(GetSession().GetTrace())
101 param.Set(i, val);
102 }
103
SetStatement(const String & s)104 void Sql::SetStatement(const String& s) {
105 cn->statement = s;
106 cn->parse = true;
107 }
108
Execute()109 bool Sql::Execute() {
110 SqlSession &session = GetSession();
111
112 session.SetStatement(cn->statement);
113 session.SetStatus(SqlSession::BEFORE_EXECUTING);
114 cn->starttime = msecs();
115 Stream *s = session.GetTrace();
116 if(s) {
117 #ifndef NOAPPSQL
118 if(this == &AppCursor())
119 *s << "SQL* ";
120 else
121 if(this == &AppCursorR())
122 *s << "SQLR* ";
123 #endif
124 String st = cn->statement;
125 if(session.IsTraceCompression())
126 st = CompressLog(st);
127 int i = 0;
128 for(const char *q = st; *q; q++)
129 if(*q == '?' && i < param.GetCount()) {
130 Value v = param[i++];
131 if(IsString(v))
132 *s << '\'' << v << '\'';
133 else
134 *s << v;
135 }
136 else
137 s->Put(*q);
138 *s << '\n';
139 }
140 if(!session.IsOpen())
141 {
142 session.SetStatus(SqlSession::CONNECTION_ERROR);
143 return false;
144 }
145 session.SetStatus(SqlSession::START_EXECUTING);
146 bool b = cn->Execute();
147 session.SetTime(msecs() - cn->starttime);
148 session.SetStatus(SqlSession::END_EXECUTING);
149 if(!b)
150 session.SetStatus(SqlSession::EXECUTING_ERROR);
151 for(int i = 0; i < cn->info.GetCount(); i++)
152 cn->info[i].name = ToUpper(cn->info[i].name);
153
154 session.SetStatus(SqlSession::AFTER_EXECUTING);
155 if(!b && session.throwonerror)
156 throw SqlExc(GetSession());
157 return b;
158 }
159
ExecuteX()160 void Sql::ExecuteX() {
161 if(!Execute())
162 throw SqlExc(GetSession());
163 }
164
Execute(const String & s)165 bool Sql::Execute(const String& s) {
166 SetStatement(s);
167 return Execute();
168 }
169
ExecuteX(const String & s)170 void Sql::ExecuteX(const String& s) {
171 SetStatement(s);
172 ExecuteX();
173 }
174
175 //$-
176
177 #define E__SetParam(I) SetParam(I - 1, p##I)
178
179 #define E__RunF(I) \
180 bool Sql::Run(__List##I(E__Value)) { \
181 __List##I(E__SetParam); \
182 return Run(); \
183 }
184 __Expand(E__RunF)
185
186 #define E__RunFX(I) \
187 void Sql::RunX(__List##I(E__Value)) { \
188 __List##I(E__SetParam); \
189 RunX(); \
190 }
__Expand(E__RunFX)191 __Expand(E__RunFX)
192
193 #define E__ExecuteF(I) \
194 bool Sql::Execute(const String& s, __List##I(E__Value)) { \
195 SetStatement(s); \
196 __List##I(E__SetParam); \
197 return Execute(); \
198 }
199 __Expand(E__ExecuteF)
200
201 #define E__ExecuteFX(I) \
202 void Sql::ExecuteX(const String& s, __List##I(E__Value)) { \
203 SetStatement(s); \
204 __List##I(E__SetParam); \
205 ExecuteX(); \
206 }
207 __Expand(E__ExecuteFX)
208
209 //$+
210
211 bool Sql::Fetch() {
212 SqlSession& session = GetSession();
213 session.SetStatus(SqlSession::START_FETCHING);
214
215 dword t0 = msecs();
216 bool b = cn->Fetch();
217 dword t = msecs();
218
219 dword total = cn->starttime == INT_MAX ? 0 : t - cn->starttime;
220 dword fetch = t - t0;
221
222 session.SetStatus(SqlSession::END_FETCHING);
223 if(!b) {
224 session.SetTime(total);
225 session.SetStatus(SqlSession::END_FETCHING_MANY);
226 }
227 Stream *s = session.GetTrace();
228 if(s) {
229 if((int)total > session.traceslow)
230 *s << "SLOW SQL: " << total << " ms: " << cn->statement << UPP::EOL;
231 else
232 if((int)fetch > session.traceslow)
233 *s << "SLOW SQL: " << fetch << " ms further fetch: " << cn->statement << UPP::EOL;
234 }
235 cn->starttime = INT_MAX;
236 return b;
237 }
238
239 //$-
240 #define E__GetColumn(I) cn->GetColumn(I - 1, p##I)
241
242 #define E__FetchF(I) \
243 bool Sql::Fetch(__List##I(E__Ref)) { \
244 if(!Fetch()) return false; \
245 __List##I(E__GetColumn); \
246 return true; \
247 }
__Expand(E__FetchF)248 __Expand(E__FetchF)
249 //$+
250
251 Vector<Value> Sql::GetRow() const {
252 Vector<Value> row;
253 int n = GetColumns();
254 row.SetCount(n);
255 for(int i = 0; i < n; i++)
256 row[i] = (*this)[i];
257 return row;
258 }
259
Fetch(Vector<Value> & row)260 bool Sql::Fetch(Vector<Value>& row) {
261 if(!Fetch()) return false;
262 row = GetRow();
263 return true;
264 }
265
GetRowMap() const266 ValueMap Sql::GetRowMap() const
267 {
268 ValueMap m;
269 int n = GetColumns();
270 for(int i = 0; i < n; i++)
271 m.Add(GetColumnInfo(i).name, (*this)[i]);
272 return m;
273 }
274
operator %(const SqlStatement & q)275 Value Sql::operator%(const SqlStatement& q)
276 {
277 return Select0(Compile(q));
278 }
279
operator ^(const SqlStatement & q)280 ValueMap Sql::operator^(const SqlStatement& q)
281 {
282 Execute(q);
283 ValueMap m;
284 Fetch(m);
285 return m;
286 }
287
operator /(const SqlStatement & q)288 ValueArray Sql::operator/(const SqlStatement& q)
289 {
290 ValueArray va;
291 Execute(q);
292 ValueMap m;
293 while(Fetch(m))
294 va.Add(m);
295 return va;
296 }
297
Fetch(ValueMap & row)298 bool Sql::Fetch(ValueMap& row) {
299 if(!Fetch()) return false;
300 row = GetRowMap();
301 return true;
302 }
303
304 struct sReadFields : public FieldOperator {
305 Sql *sql;
306
FieldUpp::sReadFields307 void Field(const char *name, Ref f) {
308 sql->GetColumn(SqlId(name), f);
309 }
310 };
311
Get(Fields fo)312 void Sql::Get(Fields fo)
313 {
314 sReadFields ff;
315 ff.sql = this;
316 fo(ff);
317 }
318
Fetch(Fields fo)319 bool Sql::Fetch(Fields fo) {
320 if(!Fetch()) return false;
321 Get(fo);
322 return true;
323 }
324
GetColumnCount() const325 int Sql::GetColumnCount() const
326 {
327 return cn->info.GetCount();
328 }
329
GetColumns() const330 int Sql::GetColumns() const {
331 return GetColumnCount();
332 }
333
GetColumn(int i,Ref r) const334 void Sql::GetColumn(int i, Ref r) const {
335 cn->GetColumn(i, r);
336 }
337
GetColumn(SqlId colid,Ref r) const338 void Sql::GetColumn(SqlId colid, Ref r) const
339 {
340 String s = ~colid;
341 for(int j = 0; j < 2; j++) {
342 for(int i = 0; i < cn->info.GetCount(); i++)
343 if(cn->info[i].name == s) {
344 GetColumn(i, r);
345 return;
346 }
347 s = ToUpper(s);
348 }
349 r.SetNull();
350 }
351
operator [](int i) const352 Value Sql::operator[](int i) const {
353 Value v;
354 cn->GetColumn(i, v);
355 return v;
356 }
357
operator [](SqlId id) const358 Value Sql::operator[](SqlId id) const {
359 String s = ~id;
360 for(int j = 0; j < 2; j++) {
361 for(int i = 0; i < cn->info.GetCount(); i++)
362 if(cn->info[i].name == s)
363 return operator[](i);
364 s = ToUpper(s);
365 }
366 NEVER_(String().Cat() << "SQL [" << ~id << "] not found");
367 return Value();
368 }
369
Select0(const String & s)370 Value Sql::Select0(const String& s) {
371 SetStatement(s);
372 if(!Run())
373 return ErrorValue(GetLastError());
374 if(!Fetch())
375 return Null;
376 Value v;
377 cn->GetColumn(0, v);
378 return v;
379 }
380
Select(const String & s)381 Value Sql::Select(const String& s) {
382 return Select0("select " + s);
383 }
384
385 //$-
386 #define E__SelectF(I) \
387 Value Sql::Select(const String& s, __List##I(E__Value)) { \
388 __List##I(E__SetParam); \
389 return Select(s); \
390 }
391 __Expand(E__SelectF)
392
393 #define E__Inserter(I) clist += ", ", clist += c##I, qlist += ", ?", SetParam(I, v##I)
394
395 #define E__InsertF(I) \
396 bool Sql::Insert(const char *table, const char *c0, const Value& v0, __List##I(E__ColVal)) { \
397 String clist = c0; \
398 String qlist = "?"; \
399 SetParam(0, v0); \
400 __List##I(E__Inserter); \
401 return Execute(String("insert into ") + table + '(' + clist + ") values(" + qlist + ')'); \
402 }
__Expand(E__InsertF)403 __Expand(E__InsertF)
404
405 #define E__InserterId(I) clist += ", ", clist += c##I.ToString(), qlist += ", ?", SetParam(I, v##I)
406
407 #define E__InsertIdF(I) \
408 bool Sql::Insert(SqlId table, SqlId c0, const Value& v0, __List##I(E__IdVal)) { \
409 String clist = c0.ToString(); \
410 String qlist = "?"; \
411 SetParam(0, v0); \
412 __List##I(E__InserterId); \
413 return Execute( \
414 String("insert into ") + table.ToString() + '(' + clist + ") values(" + qlist + ')'); \
415 }
416 __Expand(E__InsertIdF)
417
418 static inline void sComma(int I, String& s) {
419 if(I > 1) s.Cat(", ");
420 }
421
422 #define E__Updater(I) sComma(I, list), list.Cat(c##I), list.Cat(" = ?"), SetParam(I - 1, v##I)
423
424 #define E__UpdateF(I) \
425 bool Sql::Update(const char *table, const char *key, const Value& keyval, __List##I(E__ColVal)) { \
426 String list; \
427 __List##I(E__Updater); \
428 SetParam(I, keyval); \
429 return Execute(String ("update ") + table + " set " + list + " where " + key + " = ?"); \
430 }
431 __Expand(E__UpdateF)
432
433 #define E__UpdaterId(I) sComma(I, list), list.Cat(c##I.ToString()), list.Cat(" = ?"), SetParam(I - 1, v##I)
434
435 #define E__UpdateIdF(I) \
436 bool Sql::Update(SqlId table, SqlId key, const Value& keyval, __List##I(E__IdVal)) { \
437 String list; \
438 __List##I(E__UpdaterId); \
439 SetParam(I, keyval); \
440 return Execute(String ("update ") + table.ToString() + \
441 " set " + list + " where " + key.ToString() + " = ?"); \
442 }
__Expand(E__UpdateIdF)443 __Expand(E__UpdateIdF)
444 //$+
445
446 bool Sql::Delete(const char *table, const char *key, const Value& keyval) {
447 return Execute("delete from " + String(table) + " where " + key + " = ?", keyval);
448 }
449
Delete(SqlId table,SqlId key,const Value & keyval)450 bool Sql::Delete(SqlId table, SqlId key, const Value& keyval) {
451 return Delete(~table.ToString(), ~key.ToString(), keyval);
452 }
453
GetDialect() const454 int Sql::GetDialect() const {
455 return GetSession().GetDialect();
456 }
457
458 struct NfInsert : public FieldOperator {
459 int i;
460 Sql *sql;
461 String clist;
462 String qlist;
463
FieldUpp::NfInsert464 virtual void Field(const char *name, Ref f) {
465 if(i) {
466 clist += ", ";
467 qlist += ", ";
468 }
469 clist += name;
470 qlist += "? ";
471 sql->SetParam(i++, f);
472 }
473 };
474
Insert(Fields nf,const char * table)475 bool Sql::Insert(Fields nf, const char *table) {
476 NfInsert w;
477 w.i = 0;
478 w.sql = this;
479 nf(w);
480 return Execute(String("insert into ") + (table ? String(table) : w.table) +
481 '(' + w.clist + ") values(" + w.qlist + ')');
482 }
483
Insert(Fields nf)484 bool Sql::Insert(Fields nf) {
485 return Insert(nf, NULL);
486 }
487
Insert(Fields nf,SqlId table)488 bool Sql::Insert(Fields nf, SqlId table) {
489 return Insert(nf, (const char *)~table);
490 }
491
492 struct NfInsertNoKey : public FieldOperator {
493 int i;
494 Sql *sql;
495 String clist;
496 String qlist;
497
FieldUpp::NfInsertNoKey498 virtual void Field(const char *name, Ref f) {
499 if(clist.GetCount()) {
500 clist += ", ";
501 qlist += ", ";
502 }
503 if(i) {
504 clist += name;
505 qlist += "? ";
506 sql->SetParam(i - 1, f);
507 }
508 i++;
509 }
510 };
511
InsertNoKey(Fields nf,const char * table)512 bool Sql::InsertNoKey(Fields nf, const char *table) {
513 NfInsertNoKey w;
514 w.i = 0;
515 w.sql = this;
516 nf(w);
517 return Execute(String("insert into ") + (table ? String(table) : w.table) +
518 '(' + w.clist + ") values(" + w.qlist + ')');
519 }
520
InsertNoKey(Fields nf)521 bool Sql::InsertNoKey(Fields nf) {
522 return InsertNoKey(nf, NULL);
523 }
524
InsertNoKey(Fields nf,SqlId table)525 bool Sql::InsertNoKey(Fields nf, SqlId table) {
526 return InsertNoKey(nf, (const char *)~table);
527 }
528
529 struct NfInsertNoNulls : public FieldOperator {
530 int i;
531 Sql *sql;
532 String clist;
533 String qlist;
534
FieldUpp::NfInsertNoNulls535 virtual void Field(const char *name, Ref f) {
536 if(!f.IsNull()) {
537 if(clist.GetCount()) {
538 clist += ", ";
539 qlist += ", ";
540 }
541 clist << name;
542 qlist << "? ";
543 sql->SetParam(i++, f);
544 }
545 }
546 };
547
InsertNoNulls(Fields nf,const char * table)548 bool Sql::InsertNoNulls(Fields nf, const char *table)
549 {
550 NfInsertNoNulls w;
551 w.i = 0;
552 w.sql = this;
553 nf(w);
554 return Execute(String("insert into ") + (table ? String(table) : w.table) +
555 '(' + w.clist + ") values(" + w.qlist + ')');
556 }
557
InsertNoNulls(Fields nf)558 bool Sql::InsertNoNulls(Fields nf)
559 {
560 return InsertNoNulls(nf, NULL);
561 }
562
InsertNoNulls(Fields nf,SqlId table)563 bool Sql::InsertNoNulls(Fields nf, SqlId table)
564 {
565 return InsertNoNulls(nf, (const char *)~table);
566 }
567
568 #define E__Updater(I) sComma(I, list), list.Cat(c##I), list.Cat(" = ?"), SetParam(I - 1, v##I)
569
570 struct NfUpdate : public FieldOperator {
571 int i;
572 Sql *sql;
573 String list;
574 String key;
575 Value keyval;
576
FieldUpp::NfUpdate577 virtual void Field(const char *name, Ref f) {
578 if(i == 0) {
579 key = name;
580 keyval = f;
581 }
582 else {
583 if(i > 1)
584 list += ", ";
585 list << name << " = ?";
586 sql->SetParam(i - 1, f);
587 }
588 i++;
589 }
590 };
591
Update(Fields nf,const char * table)592 bool Sql::Update(Fields nf, const char *table) {
593 NfUpdate w;
594 w.i = 0;
595 w.sql = this;
596 nf(w);
597 SetParam(w.i - 1, w.keyval);
598 return Execute(String ("update ") + (table ? String(table) : w.table) +
599 " set " + w.list + " where " + w.key + " = ?");
600 }
601
Update(Fields nf)602 bool Sql::Update(Fields nf) {
603 return Update(nf, NULL);
604 }
605
Update(Fields nf,SqlId table)606 bool Sql::Update(Fields nf, SqlId table) {
607 return Update(nf, (const char *)~table);
608 }
609
SetSession(SqlSource & s)610 void Sql::SetSession(SqlSource& s) {
611 Detach();
612 cn = s.CreateConnection();
613 }
614
SetError(String err,String stmt,int code,const char * scode,ERRORCLASS clss)615 void Sql::SetError(String err, String stmt, int code, const char *scode, ERRORCLASS clss)
616 {
617 GetSession().SetError(err, stmt, code, scode, clss);
618 }
619
ClearError()620 void Sql::ClearError() { GetSession().ClearError(); }
621
GetLastError() const622 String Sql::GetLastError() const { return GetSession().GetLastError(); }
GetErrorStatement() const623 String Sql::GetErrorStatement() const { return GetSession().GetErrorStatement(); }
GetErrorCode() const624 int Sql::GetErrorCode() const { return GetSession().GetErrorCode(); }
GetErrorCodeString() const625 String Sql::GetErrorCodeString() const { return GetSession().GetErrorCodeString(); }
GetErrorClass() const626 Sql::ERRORCLASS Sql::GetErrorClass() const { return GetSession().GetErrorClass(); }
WasError() const627 bool Sql::WasError() const { return GetSession().WasError(); }
628
Begin()629 void Sql::Begin() { ClearError(); GetSession().Begin(); }
Commit()630 void Sql::Commit() { GetSession().Commit(); }
Rollback()631 void Sql::Rollback() { GetSession().Rollback(); }
GetTransactionLevel()632 int Sql::GetTransactionLevel() { return GetSession().GetTransactionLevel(); }
633
Savepoint()634 String Sql::Savepoint() { return GetSession().Savepoint(); }
RollbackTo(const String & savepoint)635 void Sql::RollbackTo(const String& savepoint) { GetSession().RollbackTo(savepoint); }
636
IsOpen()637 bool Sql::IsOpen() { return cn && GetSession().IsOpen(); }
638
Attach(Sql & sql,SqlConnection * con)639 void SqlConnection::Attach(Sql& sql, SqlConnection *con)
640 {
641 sql.Attach(con); // Duck tape to fix Oci8
642 }
643
644 #ifndef NOAPPSQL
Sql()645 Sql::Sql() {
646 cn = NULL;
647 if(SQL.cn)
648 cn = SQL.GetSession().CreateConnection();
649 }
650 #endif
651
Sql(SqlSource & s)652 Sql::Sql(SqlSource& s) {
653 cn = s.CreateConnection();
654 }
655
656 #ifndef NOAPPSQL
Sql(const char * stmt)657 Sql::Sql(const char *stmt) {
658 cn = SQL.GetSession().CreateConnection();
659 SetStatement(stmt);
660 }
661 #endif
662
Sql(const char * stmt,SqlSource & s)663 Sql::Sql(const char *stmt, SqlSource& s) {
664 cn = s.CreateConnection();
665 SetStatement(stmt);
666 }
667
668 #ifndef NOAPPSQL
Sql(const SqlStatement & stmt)669 Sql::Sql(const SqlStatement& stmt) {
670 cn = SQL.GetSession().CreateConnection();
671 SetStatement(stmt);
672 }
673 #endif
674
Sql(const SqlStatement & stmt,SqlSource & s)675 Sql::Sql(const SqlStatement& stmt, SqlSource& s) {
676 cn = s.CreateConnection();
677 SetStatement(stmt);
678 }
679
Sql(SqlConnection * connection)680 Sql::Sql(SqlConnection *connection)
681 : cn(connection)
682 {}
683
Detach()684 void Sql::Detach()
685 {
686 if(cn) delete cn;
687 cn = NULL;
688 param.Clear();
689 }
690
Attach(SqlConnection * connection)691 void Sql::Attach(SqlConnection *connection)
692 {
693 Detach();
694 cn = connection;
695 }
696
~Sql()697 Sql::~Sql() {
698 Detach();
699 }
700
701 #ifndef NOAPPSQL
702
SqlR()703 SqlR::SqlR()
704 : Sql(SQLR.GetSession()) {}
705
SqlR(const char * stmt)706 SqlR::SqlR(const char *stmt)
707 : Sql(stmt, SQLR.GetSession()) {}
708
SqlR(const SqlStatement & s)709 SqlR::SqlR(const SqlStatement& s)
710 : Sql(s, SQLR.GetSession()) {}
711
712 #endif
713
714 #ifndef NOAPPSQL
operator *=(ValueMap & map,SqlSelect select)715 void operator*=(ValueMap& map, SqlSelect select)
716 {
717 map.Clear();
718 Sql sql;
719 sql * select;
720 while(sql.Fetch())
721 map.Add(sql[0], sql[1]);
722 }
723 #endif
724
725 }
726