1 /// Dealing with query arguments
2 module dpq2.args;
3 
4 @safe:
5 
6 public import dpq2.conv.from_d_types;
7 public import dpq2.conv.from_bson;
8 
9 import dpq2.value;
10 import dpq2.oids: Oid;
11 import std.conv: to;
12 import std.string: toStringz;
13 
14 /// Query parameters
15 struct QueryParams
16 {
17     string sqlCommand; /// SQL command
18     ValueFormat resultFormat = ValueFormat.BINARY; /// Result value format
19     private Value[] _args; // SQL command arguments
20 
21     /// SQL command arguments
22     @property void args(Value[] vargs)
23     {
24         _args = vargs;
25     }
26 
27     /// ditto
28     @property ref inout (Value[]) args() inout pure
29     {
30         return _args;
31     }
32 
33     /// Fills out arguments from array
34     ///
35     /// Useful for simple text-only query params
36     /// Postgres infers a data type for the parameter in the same way it would do for an untyped literal string.
37     @property void argsFromArray(in string[] arr)
38     {
39         _args.length = arr.length;
40 
41         foreach(i, ref a; _args)
42             a = toValue(arr[i], ValueFormat.TEXT);
43     }
44 
45     /// Fills out arguments from variadic arguments
46     void argsVariadic(Targs ...)(Targs t_args)
47     {
48         _args.length = t_args.length;
49 
50         static foreach(i, T; Targs)
51         {
52             _args[i] = toValue!T(t_args[i]);
53         }
54     }
55 
56     /// Access to prepared statement name
57     ///
58     /// Use it to prepare queries
59     // FIXME: it is need to check in debug mode what sqlCommand is used in "SQL command" or "prepared statement" mode
60     @property string preparedStatementName() const { return sqlCommand; }
61     /// ditto
62     @property void preparedStatementName(string s){ sqlCommand = s; }
63 }
64 
65 unittest
66 {
67     QueryParams qp;
68     qp.argsVariadic(123, "asd", true);
69 
70     assert(qp.args[0] == 123.toValue);
71     assert(qp.args[1] == "asd".toValue);
72     assert(qp.args[2] == true.toValue);
73 }
74 
75 /// Used as parameters by PQexecParams-like functions
76 package struct InternalQueryParams
77 {
78     private
79     {
80         const(string)* sqlCommand;
81         Oid[] oids;
82         int[] formats;
83         int[] lengths;
84         const(ubyte)*[] values;
85     }
86 
87     ValueFormat resultFormat;
88 
89     this(in QueryParams* qp) pure
90     {
91         sqlCommand = &qp.sqlCommand;
92         resultFormat = qp.resultFormat;
93 
94         oids = new Oid[qp.args.length];
95         formats = new int[qp.args.length];
96         lengths = new int[qp.args.length];
97         values = new const(ubyte)* [qp.args.length];
98 
99         for(int i = 0; i < qp.args.length; ++i)
100         {
101             oids[i] = qp.args[i].oidType;
102             formats[i] = qp.args[i].format;
103 
104             if(!qp.args[i].isNull)
105             {
106                 lengths[i] = qp.args[i].data.length.to!int;
107 
108                 immutable ubyte[] zeroLengthArg = [123]; // fake value, isn't used as argument
109 
110                 if(qp.args[i].data.length == 0)
111                     values[i] = &zeroLengthArg[0];
112                 else
113                     values[i] = &qp.args[i].data[0];
114             }
115         }
116     }
117 
118     /// Values used by PQexecParams-like functions
119     const(char)* command() pure const
120     {
121         return cast(const(char)*) (*sqlCommand).toStringz;
122     }
123 
124     /// ditto
125     const(char)* stmtName() pure const
126     {
127         return command();
128     }
129 
130     /// ditto
131     int nParams() pure const
132     {
133         return values.length.to!int;
134     }
135 
136     /// ditto
137     const(Oid)* paramTypes() pure
138     {
139         if(oids.length == 0)
140             return null;
141         else
142             return &oids[0];
143     }
144 
145     /// ditto
146     const(ubyte*)* paramValues() pure
147     {
148         if(values.length == 0)
149             return null;
150         else
151             return &values[0];
152     }
153 
154     /// ditto
155     const(int)* paramLengths() pure
156     {
157         if(lengths.length == 0)
158             return null;
159         else
160             return &lengths[0];
161     }
162 
163     /// ditto
164     const(int)* paramFormats() pure
165     {
166         if(formats.length == 0)
167             return null;
168         else
169             return &formats[0];
170     }
171 }