s3rds 0.3.1__tar.gz → 0.3.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: s3rds
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: A truly serverless RDS on your own AWS account: Lambda URL endpoint, SQLite on S3, ≈ $0 when idle. Drivers named after the standard clients: s3rds.sqlite, s3rds.pymysql, s3rds.psycopg2; SQLAlchemy via sqlite+s3rds://, Django via ENGINE s3rds.sqlite.
5
5
  Author: Frank Zhang
6
6
  License: MIT
@@ -53,7 +53,7 @@ backwards (`import pymysql`, `import psycopg2`, `mysql+pymysql://`, `django.db.b
53
53
  ### The server: deploy once, ≈ $0 when idle
54
54
 
55
55
  You need an s3rds server in your own AWS account — a Lambda Function URL and an S3 bucket, nothing running
56
- while nobody uses it. Deploy it once with Terraform or CloudFormation from the
56
+ while nobody uses it. Deploy it once with Terraform from the
57
57
  [main repository](https://github.com/cloudformer/s3rds#quick-start). Same as any driver needs its database.
58
58
 
59
59
  **Python drivers for [s3rds](https://github.com/cloudformer/s3rds) — a truly serverless RDS:
@@ -20,7 +20,7 @@ backwards (`import pymysql`, `import psycopg2`, `mysql+pymysql://`, `django.db.b
20
20
  ### The server: deploy once, ≈ $0 when idle
21
21
 
22
22
  You need an s3rds server in your own AWS account — a Lambda Function URL and an S3 bucket, nothing running
23
- while nobody uses it. Deploy it once with Terraform or CloudFormation from the
23
+ while nobody uses it. Deploy it once with Terraform from the
24
24
  [main repository](https://github.com/cloudformer/s3rds#quick-start). Same as any driver needs its database.
25
25
 
26
26
  **Python drivers for [s3rds](https://github.com/cloudformer/s3rds) — a truly serverless RDS:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "s3rds"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "A truly serverless RDS on your own AWS account: Lambda URL endpoint, SQLite on S3, ≈ $0 when idle. Drivers named after the standard clients: s3rds.sqlite, s3rds.pymysql, s3rds.psycopg2; SQLAlchemy via sqlite+s3rds://, Django via ENGINE s3rds.sqlite."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -14,7 +14,7 @@ body {
14
14
  padding: 40px 20px;
15
15
  }
16
16
  .container {
17
- max-width: 1200px;
17
+ max-width: 1440px;
18
18
  margin: 0 auto;
19
19
  background: white;
20
20
  border-radius: 15px;
@@ -92,19 +92,28 @@ h1 { color: #333; margin-bottom: 10px; }
92
92
  font-size: 16px; font-weight: 600; color: #4b5563; margin: 20px 0 10px 0;
93
93
  display: flex; align-items: center; gap: 10px;
94
94
  }
95
- table {
96
- width: 100%; border-collapse: collapse; background: white;
97
- border-radius: 8px; overflow: hidden; margin-bottom: 20px;
95
+ /* result grid: every column visible at once (long text wraps inside its cell, nothing is cut off);
96
+ a very wide or tall result scrolls inside its own frame with the header pinned */
97
+ .grid-wrap {
98
+ overflow: auto; max-height: 60vh; margin-bottom: 20px;
99
+ border: 1px solid #d1d5db; border-radius: 8px; background: white;
98
100
  }
101
+ .grid-wrap::-webkit-scrollbar { width: 10px; height: 10px; }
102
+ .grid-wrap::-webkit-scrollbar-thumb { background: #c4c9d2; border-radius: 5px; border: 2px solid white; }
103
+ .grid-wrap::-webkit-scrollbar-corner { background: white; }
104
+ table { min-width: 100%; border-collapse: separate; border-spacing: 0; background: white; }
99
105
  th {
100
- background: #374151; color: white; padding: 12px; text-align: left;
101
- font-weight: 600; font-size: 14px; white-space: nowrap;
106
+ position: sticky; top: 0; z-index: 1;
107
+ background: #374151; color: white; padding: 9px 12px; text-align: left;
108
+ font-weight: 600; font-size: 13px; white-space: nowrap;
102
109
  }
103
110
  td {
104
- padding: 10px 12px; border-bottom: 1px solid #e5e7eb;
105
- font-size: 14px; color: #374151; max-width: 420px;
106
- overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
111
+ padding: 7px 12px; border-bottom: 1px solid #eef0f3; border-right: 1px solid #f3f4f6;
112
+ font-size: 13px; color: #374151; max-width: 280px; font-variant-numeric: tabular-nums;
113
+ white-space: normal; overflow-wrap: anywhere; vertical-align: top; line-height: 1.45;
107
114
  }
115
+ td.rownum, th.rownum { color: #9ca3af; text-align: right; min-width: 36px; background: #f9fafb; }
116
+ th.rownum { background: #374151; color: #9ca3af; }
108
117
  tr:last-child td { border-bottom: none; }
109
118
  tr:hover td { background: #f9fafb; }
110
119
  td.null { color: #9ca3af; font-style: italic; }
@@ -198,7 +207,7 @@ dialog .form-grid { grid-template-columns: 1fr; }
198
207
  <button class="btn btn-ghost btn-small" id="togglelog">history</button>
199
208
  </div>
200
209
  <div class="log" id="log"></div>
201
- <div id="sqlout" style="margin-top:12px" hidden><table id="sqlgrid"></table></div>
210
+ <div id="sqlout" class="grid-wrap" style="margin-top:12px" hidden><table id="sqlgrid"></table></div>
202
211
  </div>
203
212
 
204
213
  <div id="main"></div>
@@ -306,6 +315,11 @@ function rowsLine(d){ return `${d.rows.length} rows retrieved ${timing(d)}`; }
306
315
  function doneLine(d){ return `${d.rowcount >= 0 ? d.rowcount + ' rows affected' : 'completed'} ${timing(d)}`; }
307
316
  function esc(s){ return String(s).replace(/[&<>"]/g,
308
317
  c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])); }
318
+ function cell(v){
319
+ if (v === null) return '<td class="null">NULL</td>';
320
+ const t = typeof v === 'object' ? JSON.stringify(v) : String(v);
321
+ return `<td title="${esc(t)}">${esc(t)}</td>`;
322
+ }
309
323
  function sqlStr(v){ return v === null ? 'NULL' : "'" + String(v).replace(/'/g, "''") + "'"; }
310
324
  function friendly(m){
311
325
  if (/incomplete input/.test(m))
@@ -433,8 +447,7 @@ function render(){
433
447
  const dataHead = '<tr>' + state.columns.map(c => `<th>${esc(c)}</th>`).join('')
434
448
  + (hasRowid ? '<th>actions</th>' : '') + '</tr>';
435
449
  const dataRows = state.rows.map((r, i) =>
436
- `<tr data-i="${i}">` + r.map(v => v === null
437
- ? '<td class="null">NULL</td>' : `<td>${esc(v)}</td>`).join('')
450
+ `<tr data-i="${i}">` + r.map(cell).join('')
438
451
  + (hasRowid
439
452
  ? `<td style="white-space:nowrap"><button class="btn btn-ghost btn-small" onclick="editRow(${i})">edit</button>
440
453
  <button class="btn-danger" onclick="delRow(${i})">🗑 delete</button></td>` : '')
@@ -450,10 +463,10 @@ function render(){
450
463
  <div class="section-title">Structure
451
464
  <button class="btn btn-secondary btn-small" id="addcol">+ Add column</button>
452
465
  </div>
453
- <table>
466
+ <div class="grid-wrap"><table>
454
467
  <thead><tr><th>Field</th><th>Type</th><th>Null</th><th>Key</th><th>Default</th><th>Extra</th><th>actions</th></tr></thead>
455
468
  <tbody>${structRows}</tbody>
456
- </table>
469
+ </table></div>
457
470
 
458
471
  <div class="add-form">
459
472
  <h3>➕ Insert a row into ${esc(state.table)}</h3>
@@ -467,7 +480,7 @@ function render(){
467
480
  ${state.ms !== undefined ? `<span class="hint">${state.ms} ms · ${state.s3}</span>` : ''}
468
481
  </div>
469
482
  ${state.rows.length
470
- ? `<table id="grid"><thead>${dataHead}</thead><tbody>${dataRows}</tbody></table>`
483
+ ? `<div class="grid-wrap"><table id="grid"><thead>${dataHead}</thead><tbody>${dataRows}</tbody></table></div>`
471
484
  : '<div class="empty">No rows yet — insert the first one with the form above</div>'}
472
485
 
473
486
  <div class="pagination">
@@ -598,9 +611,8 @@ async function runSql(){
598
611
  const out = $('#sqlout'), t = $('#sqlgrid');
599
612
  if (d.columns.length){
600
613
  out.hidden = false;
601
- t.innerHTML = '<thead><tr>' + d.columns.map(c => `<th>${esc(c)}</th>`).join('') + '</tr></thead><tbody>'
602
- + d.rows.map(r => '<tr>' + r.map(v => v === null
603
- ? '<td class="null">NULL</td>' : `<td>${esc(v)}</td>`).join('') + '</tr>').join('') + '</tbody>';
614
+ t.innerHTML = '<thead><tr><th class="rownum">#</th>' + d.columns.map(c => `<th>${esc(c)}</th>`).join('') + '</tr></thead><tbody>'
615
+ + d.rows.map((r, i) => `<tr><td class="rownum">${i + 1}</td>` + r.map(cell).join('') + '</tr>').join('') + '</tbody>';
604
616
  msg(rowsLine(d), true);
605
617
  } else {
606
618
  t.innerHTML = ''; out.hidden = true;
@@ -49,7 +49,7 @@ import socket
49
49
  import urllib.parse
50
50
  import warnings
51
51
 
52
- __version__ = "0.3.1"
52
+ __version__ = "0.3.2"
53
53
 
54
54
  apilevel = "2.0"
55
55
  threadsafety = 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: s3rds
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: A truly serverless RDS on your own AWS account: Lambda URL endpoint, SQLite on S3, ≈ $0 when idle. Drivers named after the standard clients: s3rds.sqlite, s3rds.pymysql, s3rds.psycopg2; SQLAlchemy via sqlite+s3rds://, Django via ENGINE s3rds.sqlite.
5
5
  Author: Frank Zhang
6
6
  License: MIT
@@ -53,7 +53,7 @@ backwards (`import pymysql`, `import psycopg2`, `mysql+pymysql://`, `django.db.b
53
53
  ### The server: deploy once, ≈ $0 when idle
54
54
 
55
55
  You need an s3rds server in your own AWS account — a Lambda Function URL and an S3 bucket, nothing running
56
- while nobody uses it. Deploy it once with Terraform or CloudFormation from the
56
+ while nobody uses it. Deploy it once with Terraform from the
57
57
  [main repository](https://github.com/cloudformer/s3rds#quick-start). Same as any driver needs its database.
58
58
 
59
59
  **Python drivers for [s3rds](https://github.com/cloudformer/s3rds) — a truly serverless RDS:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes