forked from IronLanguages/ironpython3
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStatement.cs
More file actions
320 lines (263 loc) · 10.5 KB
/
Statement.cs
File metadata and controls
320 lines (263 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
//
// Copyright (c) Jeff Hardy 2010-2012.
//
using System;
using System.Collections;
using System.Diagnostics;
using Community.CsharpSqlite;
using IronPython.Runtime;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
using sqlite3_stmt = Community.CsharpSqlite.Sqlite3.Vdbe;
namespace IronPython.SQLite
{
internal enum StatementType
{
Unknown,
Select,
Insert,
Update,
Delete,
Replace,
Other
}
[DebuggerDisplay("{sql}")]
internal class Statement
{
private readonly Guid uniqueid;
private Sqlite3.sqlite3 db;
internal sqlite3_stmt st;
private object current = null, nextRow = null;
private bool started = false;
private string sql;
private bool bound = false;
internal bool in_use = false;
public string Tail { get; private set; }
public Statement(PythonSQLite.Connection connection, string operation)
{
this.uniqueid = Guid.NewGuid();
this.db = connection.db;
this.sql = operation;
this.st = null;
string tail = null;
if(Sqlite3.sqlite3_prepare(this.db, this.sql, -1, ref this.st, ref tail) != Sqlite3.SQLITE_OK /*TODO: || too much sql */)
{
Sqlite3.sqlite3_finalize(st);
this.st = null;
throw PythonSQLite.GetSqliteError(this.db, null);
}
this.Tail = tail;
}
private Statement(Sqlite3.sqlite3 db, sqlite3_stmt stmt, string operation, string tail)
{
this.uniqueid = Guid.NewGuid();
this.db = db;
this.sql = operation;
this.st = stmt;
this.Tail = tail;
}
~Statement()
{
if(this.st != null)
{
Sqlite3.sqlite3_finalize(this.st);
}
this.st = null;
}
private StatementType _type = StatementType.Unknown;
public StatementType StatementType
{
get
{
if(this._type != StatementType.Unknown)
return _type;
string s = this.sql.TrimStart();
if(s.StartsWith("select", StringComparison.InvariantCultureIgnoreCase))
this._type = StatementType.Select;
else if(s.StartsWith("insert", StringComparison.InvariantCultureIgnoreCase))
this._type = StatementType.Insert;
else if(s.StartsWith("update", StringComparison.InvariantCultureIgnoreCase))
this._type = StatementType.Update;
else if(s.StartsWith("delete", StringComparison.InvariantCultureIgnoreCase))
this._type = StatementType.Delete;
else if(s.StartsWith("replace", StringComparison.InvariantCultureIgnoreCase))
this._type = StatementType.Replace;
else
this._type = StatementType.Other;
return this._type;
}
}
public void BindParameters(CodeContext context, object parameters)
{
if(bound)
this.ClearParameters();
int num_params_needed = Sqlite3.sqlite3_bind_parameter_count(this.st);
if(parameters == null)
{
if(num_params_needed > 0)
throw PythonSQLite.MakeProgrammingError("parameters are required but not specified.");
else
return;
}
if(parameters is IDictionary)
BindParameters(context, (IDictionary)parameters, num_params_needed);
else if(parameters is IList)
BindParameters(context, (IList)parameters, num_params_needed);
else
throw PythonSQLite.MakeProgrammingError("unknown parameter type");
bound = true;
}
private void BindParameters(CodeContext context, IDictionary args, int num_params_needed)
{
for(int i = 1; i <= num_params_needed; ++i)
{
string binding_name = Sqlite3.sqlite3_bind_parameter_name(this.st, i);
if(string.IsNullOrEmpty(binding_name))
throw PythonSQLite.MakeProgrammingError("Binding {0} has no name, but you supplied a dictionary (which has only names).".Format(i));
// remove the leading colon
binding_name = binding_name.Substring(1);
if(args.Contains(binding_name))
BindParameter(context, i, maybeAdapt(context, args[binding_name]));
else
throw PythonSQLite.MakeProgrammingError("You did not supply a value for binding {0}.".Format(i));
}
}
private void BindParameters(CodeContext context, IList args, int num_params_needed)
{
if(num_params_needed != args.Count)
throw PythonSQLite.MakeProgrammingError("Incorrect number of bindings supplied.");
for(int i = 0; i < args.Count; ++i)
{
BindParameter(context, i + 1, maybeAdapt(context, args[i]));
}
}
private void BindParameter(CodeContext context, int index, object arg)
{
int rc;
if(arg == null)
rc = Sqlite3.sqlite3_bind_null(st, index);
else if(arg is int)
rc = Sqlite3.sqlite3_bind_int(st, index, (int)arg);
else if(arg is bool)
rc = Sqlite3.sqlite3_bind_int(st, index, (bool)arg ? 1 : 0);
else if(arg is long)
rc = Sqlite3.sqlite3_bind_int64(st, index, (long)arg);
else if (arg is System.Numerics.BigInteger)
rc = Sqlite3.sqlite3_bind_int64(st, index, (long)((System.Numerics.BigInteger)arg));
else if(arg is float)
rc = Sqlite3.sqlite3_bind_double(st, index, (float)arg);
else if(arg is double)
rc = Sqlite3.sqlite3_bind_double(st, index, (double)arg);
else if(arg is string)
rc = Sqlite3.sqlite3_bind_text(st, index, (string)arg, -1, Sqlite3.SQLITE_TRANSIENT);
else if(arg is byte[])
rc = Sqlite3.sqlite3_bind_blob(this.st, index, (byte[])arg, -1, Sqlite3.SQLITE_TRANSIENT);
else
throw PythonSQLite.MakeInterfaceError("Unable to bind parameter {0} - unsupported type {1}".Format(index, arg.GetType()));
if(rc != Sqlite3.SQLITE_OK)
throw PythonSQLite.MakeInterfaceError("Unable to bind parameter {0}: {1}".Format(index, Sqlite3.sqlite3_errmsg(db)));
}
private object maybeAdapt(CodeContext context, object value)
{
return needsAdaptation(context, value) ? adaptValue(context, value) : value;
}
private bool needsAdaptation(CodeContext context, object value)
{
// TODO The check for primitive types could probably be cached like pysqlite does
if(value == null ||
value is int ||
value is bool ||
value is long ||
value is System.Numerics.BigInteger ||
value is float ||
value is double ||
value is string ||
value is byte[])
{
object proto = DynamicHelpers.GetPythonTypeFromType(typeof(PythonSQLite.PrepareProtocol));
object type = DynamicHelpers.GetPythonType(value);
object key = new PythonTuple(new[] { type, proto });
return PythonSQLite.adapters.ContainsKey(key);
}
else
{
return true;
}
}
private object adaptValue(CodeContext context, object value)
{
object proto = DynamicHelpers.GetPythonTypeFromType(typeof(PythonSQLite.PrepareProtocol));
object type = DynamicHelpers.GetPythonType(value);
object key = new PythonTuple(new[] { type, proto });
object adapter;
if(PythonSQLite.adapters.TryGetValue(key, out adapter))
{
object adapted = PythonCalls.Call(context, adapter, value);
return adapted;
}
// TODO: Use proto? Any value whatsoever?
object conform;
if(context.LanguageContext.Operations.TryGetMember(value, "__conform__", out conform))
{
object adapted = PythonCalls.Call(context, conform, proto);
if(adapted != null)
{
return adapted;
}
}
return value;
}
public int RawStep()
{
return Util.Step(st);
}
public int SqliteFinalize()
{
int rc = Sqlite3.SQLITE_OK;
if(this.st != null)
{
rc = Sqlite3.sqlite3_finalize(this.st);
this.st = null;
}
this.in_use = false;
return rc;
}
public int Reset()
{
int rc = Sqlite3.SQLITE_OK;
if(this.in_use && this.st != null)
{
rc = Sqlite3.sqlite3_reset(this.st);
if(rc == Sqlite3.SQLITE_OK)
this.in_use = false;
}
return rc;
}
private void ClearParameters()
{
if(Sqlite3.sqlite3_clear_bindings(this.st) != Sqlite3.SQLITE_OK)
throw PythonSQLite.GetSqliteError(this.db, null);
}
internal void MarkDirty()
{
this.in_use = true;
}
internal int Recompile(CodeContext context, object parameters)
{
sqlite3_stmt new_st = null;
string tail = null;
int rc = Sqlite3.sqlite3_prepare(this.db, this.sql, -1, ref new_st, ref tail);
if(rc == Sqlite3.SQLITE_OK)
{
Statement new_stmt = new Statement(this.st.db, new_st, this.sql, tail);
new_stmt.BindParameters(context, parameters);
Sqlite3.sqlite3_finalize(this.st);
this.st = new_st;
}
return rc;
}
}
}