@mlagie/sql-connector 3.0.1 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mlagie/sql-connector",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "The sql-connector module allows you to manage connections to a MySQL database, define table schemas, and interact with data in a simple and efficient way.",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -88,8 +88,8 @@ class ModelInstance {
88
88
  const dialect = getDialect();
89
89
  const values = Object.values(model);
90
90
 
91
- const setClause = setKeys.map(key => {
92
- return `${dialect.escape(key)} = ?`;
91
+ const setClause = setKeys.map((key, i) => {
92
+ return `${dialect.escape(key)} = ${dialect.getPlaceholder(i)}`;
93
93
  }).join(', ');
94
94
 
95
95
  let targetCriteria;
@@ -126,7 +126,7 @@ class ModelInstance {
126
126
  targetCriteria = fallbackRec;
127
127
  }
128
128
 
129
- const { sql: whereClause, values: whereValues } = buildQueryParts({ where: targetCriteria });
129
+ const { sql: whereClause, values: whereValues } = buildQueryParts({ where: targetCriteria }, setKeys.length);
130
130
  values.push(...whereValues);
131
131
 
132
132
  const sql_request = `UPDATE ${dialect.escape(this._name)} SET ${setClause} ${whereClause}`;
@@ -61,7 +61,7 @@ function buildSelect(select = []) {
61
61
  .join(',\n');
62
62
  }
63
63
 
64
- function buildWhere(where, values) {
64
+ function buildWhere(where, values, offset = 0) {
65
65
  const conditions = [];
66
66
 
67
67
  for (const [key, value] of Object.entries(where)) {
@@ -70,7 +70,7 @@ function buildWhere(where, values) {
70
70
  throw new Error("OR conditions cannot be empty");
71
71
  }
72
72
  const clauses = value
73
- .map((item) => buildWhere(item, values))
73
+ .map((item) => buildWhere(item, values, offset))
74
74
  .filter(Boolean);
75
75
 
76
76
  if (clauses.length) {
@@ -85,7 +85,7 @@ function buildWhere(where, values) {
85
85
  throw new Error("AND conditions cannot be empty");
86
86
  }
87
87
  const clauses = value
88
- .map((item) => buildWhere(item, values))
88
+ .map((item) => buildWhere(item, values, offset))
89
89
  .filter(Boolean);
90
90
 
91
91
  if (clauses.length) {
@@ -106,15 +106,17 @@ function buildWhere(where, values) {
106
106
  for (const [operator, operatorValue] of Object.entries(value)) {
107
107
  if (operator === "IN" || operator === "NOT IN") {
108
108
  const placeholders = operatorValue
109
- .map(() => "?")
109
+ .map((_, i) => getDialect().getPlaceholder(offset + values.length + i))
110
110
  .join(",");
111
111
 
112
112
  conditions.push(
113
113
  `${getDialect().escape(key)} ${operator} (${placeholders})`
114
114
  );
115
115
  values.push(...operatorValue);
116
+ } else if (operatorValue === null && (operator === "=" || operator === "!=")) {
117
+ conditions.push(`${getDialect().escape(key)} IS ${operator === "!=" ? "NOT " : ""}NULL`);
116
118
  } else {
117
- conditions.push(`${getDialect().escape(key)} ${operator} ?`);
119
+ conditions.push(`${getDialect().escape(key)} ${operator} ${getDialect().getPlaceholder(offset + values.length)}`);
118
120
  values.push(operatorValue);
119
121
  }
120
122
  }
@@ -122,7 +124,12 @@ function buildWhere(where, values) {
122
124
  }
123
125
  }
124
126
 
125
- conditions.push(`${getDialect().escape(key)} = ?`);
127
+ if (value === null) {
128
+ conditions.push(`${getDialect().escape(key)} IS NULL`);
129
+ continue;
130
+ }
131
+
132
+ conditions.push(`${getDialect().escape(key)} = ${getDialect().getPlaceholder(offset + values.length)}`);
126
133
  values.push(value);
127
134
  }
128
135
 
@@ -132,9 +139,10 @@ function buildWhere(where, values) {
132
139
  /**
133
140
  * Construit les parties de la requête et extrait les valeurs sécurisées
134
141
  * @param {Object} options Options de filtrage, tri et pagination
142
+ * @param {number} [valueOffset=0] Décalage d'index pour les placeholders (utile quand des valeurs ont déjà été liées avant le WHERE, ex: SET d'un UPDATE)
135
143
  * @returns {Object} Un objet contenant la chaîne SQL générée et le tableau des valeurs { sql, values }
136
144
  */
137
- function buildQueryParts(options) {
145
+ function buildQueryParts(options, valueOffset = 0) {
138
146
  const parts = [];
139
147
  const values = [];
140
148
 
@@ -145,7 +153,7 @@ function buildQueryParts(options) {
145
153
  if (typeof options.where !== 'object' || Array.isArray(options.where)) {
146
154
  throw new Error("Raw string WHERE clauses are not allowed. Use a structured filter instead.");
147
155
  }
148
- parts.push(`WHERE ${buildWhere(options.where, values)}`);
156
+ parts.push(`WHERE ${buildWhere(options.where, values, valueOffset)}`);
149
157
  }
150
158
 
151
159
  if (options.groupBy) {
@@ -169,8 +177,8 @@ function buildQueryParts(options) {
169
177
  if (!Number.isInteger(options.limit) || options.limit < 0) {
170
178
  throw new Error("Invalid LIMIT value");
171
179
  }
172
-
173
- parts.push(`LIMIT ?`);
180
+
181
+ parts.push(`LIMIT ${getDialect().getPlaceholder(valueOffset + values.length)}`);
174
182
  values.push(options.limit);
175
183
  }
176
184