· 7 years ago · Nov 08, 2018, 05:44 AM
1var mapObj = obj => Object.keys(obj);
2
3var Databases = {
4 exists: name => Databases.hasOwnProperty(name),
5 get: name => Databases.exists(name)
6 ? Databases[name]
7 : (console.error(`Database with name ${name} doens't exists`), null)
8};
9
10var CurrentDatabase = null;
11
12var createDatabase = () => ({
13 tables: {},
14 tableExists(name){
15 return !!this.tables[name]
16 },
17 getTable(name){
18 return this.tables[name]
19 }
20});
21
22var Users = {
23 'root@localhost': {
24 password: '589757',
25 access: [],
26 }
27};
28
29
30var sqlCommand = fn => fn ? fn : ({ });
31
32var ALTER = sqlCommand();
33
34ALTER.USER = name => ({
35 IDENTIFIED: {
36 BY: nenPassword => {
37 if (Users.hasOwnProperty(name)){
38 Users[name].password = nenPassword;
39 }else{
40 console.error(`User ${name} doesn't exists`);
41 }
42 }
43 }
44})
45
46var SHOW = sqlCommand();
47
48SHOW.DATABASES = () => {mapObj(Databases).map((v, k) => v !== `exists` && v !== `get` && console.log(v))}
49SHOW.TABLES = () => {
50 if(CurrentDatabase) {
51 Object.keys(CurrentDatabase.tables).map(n => console.log(n))
52 }else{
53 console.error(`Cannot show tables: no database in use`);
54 }
55}
56
57var CREATE = sqlCommand();
58
59var mergeObjects = (p = {}, c) => ({...p, ...c});
60
61CREATE.DATABASE = name =>
62 Databases.exists(name)
63 ? console.error(`Database ${name} aleardy exists`)
64 : Databases[name] = createDatabase();
65
66buildTransactionSpec = (key, constraints, tableColumnsRest, tableSpecErrors, primaryKeyField, table) => {
67 if (key === `PRIMARY_KEY`){
68 if (tableColumnsRest.includes(constraints)) {
69 primaryKeyField.is = constraints
70 }else{
71 tableSpecErrors.push(x => `Cannot set primary key: field ${constraints} doesn't exisits on ${name}`);
72 }
73 return null
74 }else{
75 var sortConstraints = constraints.sort(({precedence: x}, {precedence: y}) => x > y);
76 const actualConstraints = sortConstraints.map(x => x.fn);
77 return {
78 description: sortConstraints.map(x => x.name).reduce((p, c) => `${p} ${c}`, ``).trim(),
79 transaction(transaction){
80 return actualConstraints.reduce((p, f) => p === null ? p : f(p, table, key), transaction);
81 }
82 }
83 }
84}
85
86var createTableFromSpec = (name, tableSpec) => {
87 var primaryKeyField = {};
88 var tableSpecErrors = [];
89 var table = {
90 name,
91 entries: [],
92 lastInsertId: 0,
93 };
94 var tableColumnsRest = Object.keys(tableSpec);
95 var transactionSpec = Object.entries(tableSpec).map(([key, constraints]) => ({
96 [key]: buildTransactionSpec(key, constraints, tableColumnsRest, tableSpecErrors, primaryKeyField, table)
97 })).filter(x => x[Object.keys(x)[0]]).reduce(mergeObjects);
98 table.primaryKeyField = primaryKeyField.is;
99 table.transactionSpec = transactionSpec;
100 return tableSpecErrors.length ? (tableSpecErrors.map(console.error), null) : table;
101}
102
103CREATE.TABLE = (name, tableSpec) => CurrentDatabase
104 ? CurrentDatabase.tables[name] = createTableFromSpec(name, tableSpec)
105 : console.error(`Cannot create table, no database in use`)
106
107var USE = sqlCommand(database => CurrentDatabase = Databases.get(database));
108
109var sqlTypeSpec = (name, fn, precedence) => ({fn, precedence, name});
110
111var INT = sqlTypeSpec(`INT`, value => {
112 if (isNaN(value)) {
113 console.error(`Type assertion failed: ${value} is not a number`);
114 return null;
115 }else{
116 return parseInt(value);
117 }
118}, 2)
119
120var UNSIGNED = sqlTypeSpec(`UNSIGNED`, value => value < 0 ? value * -1 : value, 3)
121
122var NOT_NULL = sqlTypeSpec(`NOT_NULL`, (value, table, field) => value === undefined
123 ? (console.error(`Type assertion failed: ${field} cannot be null`), null)
124 : value
125, 0);
126
127var AUTO_INCREMENT = sqlTypeSpec(`AUTO_INCREMENT`, (value, table) => table.lastInsertId++ , -1)
128
129var VARCHAR = count => sqlTypeSpec(`VARCHAR(${count})`, (value = '') =>
130 value.length > count
131 ? (console.error(`Type assertion failed ${value} is greather than ${count}.`), null)
132 : value
133, 1)
134
135var isDate = date => !isNaN(Date.parse(date));
136
137var DATE = sqlTypeSpec(`DATE`, value =>
138 isDate(value)
139 ? Date.parse(value)
140 : (console.error(`Type assertion failed ${value} not an valid date.`), null)
141, 1)
142
143
144DESCRIBE = name => CurrentDatabase
145 ? CurrentDatabase.tableExists(name)
146 ? Object.entries(CurrentDatabase.getTable(name).transactionSpec).map(x => `${x[0]}: ${x[1].description} \n`).reduce((p, c) => p + c, `${name}: \n`)
147 : console.error(`Table ${name} doesn't exists`)
148 : console.error(`Cannot describe ${name}: no database in use`)
149
150var safeGetTable = (tableName, action = `execute action`) =>
151 CurrentDatabase
152 ? CurrentDatabase.tableExists(tableName)
153 ? CurrentDatabase.getTable(tableName)
154 : (console.error(`Table ${tableName} doesn't exists`), null)
155 : (console.error(`Cannot ${action}: no database in use`), null)
156
157
158var concatStr = (p = ``, c) => `${p}${c}`
159INSERT = sqlCommand()
160INSERT.INTO = (name) => (...rowSpec) => ({
161 VALUES(...arrs){
162 var table = safeGetTable(name, `insert`);
163 var tableColumns = Object.keys(table.transactionSpec);
164 var notFoundColumns = rowSpec.filter(column => !tableColumns.includes(column));
165 if (notFoundColumns.length) {
166 console.error(notFoundColumns.map(x => `Cannot insert in ${name}: column ${x} doesn't exisit\n`).reduce(concatStr))
167 return null
168 }
169 if (!table){
170 return null
171 }
172 if(!arrs.every(arr => arr.length === rowSpec.length)){
173 console.error(`Row insert failed: Malformatted inserts`)
174 return null;
175 }
176 var insertSpec = arrs.map(dataSpec => dataSpec.map((name, idx) => ({[rowSpec[idx]]: name})).reduce(mergeObjects));
177
178 var newTables = insertSpec.map(inserts =>
179 Object.entries(table.transactionSpec).map(([key, value]) => ({ [key]: value.transaction(inserts[key])})).reduce(mergeObjects)
180 )
181
182 table.entries.push(...newTables)
183 }
184})
185
186
187
188replaceAll = str => ({
189 by: map => Object.entries(map).map(([regex, replacer]) => str => str.replace(new RegExp(regex, 'gi'), replacer)).reduce((p, c) => c(p), str),
190})
191
192compileJSFinder = (keys, str) => new Function('searcher', 'return ' +replaceAll(str).by({
193 'OR': '||',
194 'AND': '&&',
195 'NOT': '!',
196 '=': '===',
197 ...keys.map(k => ({[k]: `searcher.${k}`})).reduce(mergeObjects),
198}));
199
200filterColumns = (entries, columns) => entries.map(row => Object.entries(row).filter(([k, v]) => columns.includes(k)).map(([k, v]) => ({[k]: v})).reduce(mergeObjects))
201
202
203orderBy = (tableList) => ({
204 ORDER_BY: (name) => ({
205 ASC: () => tableList.sort((a, b) => a[name] > b[name]),
206 DESC: () => tableList.sort((a, b) => a[name] < b[name]),
207 DO: () => tableList.sort((a, b) => a[name] > b[name]),
208 }),
209 DO: () => tableList,
210})
211
212SELECT = sqlCommand((...columns) => ({
213 FROM(name){
214 return {
215 DO(){
216 var table = safeGetTable(name, `select`);
217 if (!table) return null
218 if (columns[0] === `*`) return table.entries;
219 return orderBy(filterColumns(table.entries, columns))
220 },
221 WHERE(whereSpec){
222 var table = safeGetTable(name, `select`);
223 if (!table) return null
224 var finder;
225 var findColumns = columns[0] === '*' ? Object.keys(table.transactionSpec) : columns;
226 if (typeof whereSpec === `function`){
227 finder = whereSpec;
228 }else{
229 finder = compileJSFinder(findColumns, whereSpec);
230 }
231 var foundTables = table.entries.filter(finder);
232 return orderBy(filterColumns(foundTables, findColumns));
233 }
234 }
235 }
236}))
237
238
239DELETE = sqlCommand((...columns) => ({
240 FROM(name){
241 return {
242 DO(){
243 var table = safeGetTable(name, `select`);
244 if (!table) return null
245 table.entries = [];
246 return true;
247 },
248 WHERE(whereSpec){
249 var table = safeGetTable(name, `select`);
250 if (!table) return null
251 var finder;
252 var findColumns = Object.keys(table.transactionSpec);
253 if (typeof whereSpec === `function`){
254 finder = whereSpec;
255 }else{
256 finder = compileJSFinder(findColumns, whereSpec);
257 }
258 table.entries = table.entries.filter(i => !finder(i));
259 return true
260 }
261 }
262 }
263}))
264ALTER = sqlCommand();
265ALTER.TABLE = (tname) => ({
266 ADD(name, transactionSpec){
267 var table = safeGetTable(tname, `alter table`);
268 if (!table) return null
269
270 var errors = [];
271 var newPrimaryKey = {};
272 var newSpec = buildTransactionSpec(name, transactionSpec, Object.keys(table.transactionSpec), errors, newPrimaryKey, table);
273 if(errors.length){
274 errors.map(console.error)
275 return null
276 }
277 if(table.primaryKeyField !== undefined && newPrimaryKey.is){
278 console.error(`Cannot reset primary key`);
279 return null
280 }
281 table.transactionSpec[name] = newSpec;
282 table.entries = table.entries.map(e => ({[name]: undefined, ...e}));
283 return true
284 },
285 REMOVE(...names){
286 var table = safeGetTable(tname, `alter table`);
287 if (!table) return null
288 names.forEach(name => delete table.transactionSpec[name])
289 table.entries = table.entries.map(e => {
290 names.forEach(name => delete e[name])
291 return e;
292 });
293 return true
294 },
295})
296
297updateRow = (row, updateSpec) => (updateSpec.map(([key, value]) => row[key] = value), row)
298
299UPDATE = sqlCommand(tableNae => ({
300 SET: (...specs) => ({
301 WHERE(whereSpec){
302 var updateSpec = specs.map(spec => spec.split(`=`).map(part => part.trim()));
303 var table = safeGetTable(tableNae, `select`);
304 if (!table) return null
305 var finder;
306 var findColumns = Object.keys(table.transactionSpec);
307 if (typeof whereSpec === `function`){
308 finder = whereSpec;
309 }else{
310 finder = compileJSFinder(findColumns, whereSpec);
311 }
312 table.entries = table.entries.map(row => finder(row) ? updateRow(row, updateSpec) : row );
313 return true
314 }
315 })
316}))