File: db.c

package info (click to toggle)
zsv 1.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 49,160 kB
  • sloc: ansic: 175,811; cpp: 56,301; sh: 3,623; makefile: 3,048; javascript: 577; cs: 90; awk: 70; python: 41; sql: 15
file content (148 lines) | stat: -rw-r--r-- 5,903 bytes parent folder | download
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
#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <jsonwriter.h>
#include <zsv/utils/string.h>

// starts_w_str_underscore(): helper function
// returns 1 if s starts with prefix (case-insensitive), followed by underscore
static char starts_w_str_underscore(const unsigned char *s, size_t s_len, const unsigned char *prefix) {
  char result = 0;
  unsigned char *s_lc = zsv_strtolowercase(s, &s_len);
  size_t pfx_len = strlen((const char *)prefix);
  unsigned char *prefix_lc = zsv_strtolowercase(prefix, &pfx_len);
  if (pfx_len + 1 < s_len && !memcmp(s_lc, prefix_lc, pfx_len) && s_lc[pfx_len] == '_')
    result = 1;
  free(s_lc);
  free(prefix_lc);
  return result;
}

int zsv_dbtable2json(sqlite3 *db, const char *tname, jsonwriter_handle jsw, size_t limit) {
  int err = 0;
  const char *index_sql = "select name, sql from sqlite_master where type = 'index' and tbl_name = :tbl_name";
  const char *unique_sql = "select 1 from PRAGMA_index_list(?) where name = ? and [unique] <> 0";
  sqlite3_str *data_sql = sqlite3_str_new(db);
  if (data_sql) {
    sqlite3_str_appendf(data_sql, "select * from \"%w\"", tname);
    sqlite3_stmt *data_stmt = NULL;
    sqlite3_stmt *index_stmt = NULL;
    sqlite3_stmt *unique_stmt = NULL;
    int colcount = 0;

    err = 1;
    if (sqlite3_prepare_v2(db, sqlite3_str_value(data_sql), -1, &data_stmt, NULL) != SQLITE_OK)
      fprintf(stderr, "Unable to prepare %s: %s\n", sqlite3_str_value(data_sql), sqlite3_errmsg(db));
    else if (!(colcount = sqlite3_column_count(data_stmt)))
      fprintf(stderr, "No columns found in table %s\n", tname);
    else if (sqlite3_prepare_v2(db, index_sql, -1, &index_stmt, NULL) != SQLITE_OK)
      fprintf(stderr, "Unable to prepare %s: %s\n", index_sql, sqlite3_errmsg(db));
    else if (sqlite3_prepare_v2(db, unique_sql, -1, &unique_stmt, NULL) != SQLITE_OK)
      fprintf(stderr, "Unable to prepare %s: %s\n", unique_sql, sqlite3_errmsg(db));
    else {
      err = 0;
      jsonwriter_start_array(jsw); // output is an array with 2 items: meta and data

      // ----- meta: columns and index info
      jsonwriter_start_object(jsw);

      jsonwriter_object_cstr(jsw, "name", tname);

      // indexes
      jsonwriter_object_object(jsw, "indexes"); // indexes
      sqlite3_bind_text(index_stmt, 1, tname, (int)strlen(tname), SQLITE_STATIC);
      while (sqlite3_step(index_stmt) == SQLITE_ROW) {
        const unsigned char *text = sqlite3_column_text(index_stmt, 0);
        const unsigned char *ix_sql = sqlite3_column_text(index_stmt, 1);
        size_t len = text ? sqlite3_column_bytes(index_stmt, 0) : 0;
        size_t ix_sql_len = ix_sql ? sqlite3_column_bytes(index_stmt, 1) : 0;

        if (text && ix_sql && len && ix_sql_len) {
          // on: for now we just look for the first and last parens
          const unsigned char *first_paren = memchr(ix_sql, '(', ix_sql_len);
          const unsigned char *last_paren = ix_sql + ix_sql_len;
          while (first_paren && last_paren > first_paren + 1 && *last_paren != ')')
            last_paren--;
          if (first_paren && last_paren > first_paren) {
            // name

            // strip the leading "tablename_" from the index name
            const char *ix_name = (const char *)text;
            size_t ix_name_len = len;
            if (ix_name_len > strlen(tname) + 1 &&
                starts_w_str_underscore((const unsigned char *)ix_name, ix_name_len, (const unsigned char *)tname)) {
              ix_name += strlen(tname) + 1;
              ix_name_len -= strlen(tname) + 1;
            }
            jsonwriter_object_keyn(jsw, (const char *)ix_name, ix_name_len);

            // ix obj
            jsonwriter_start_object(jsw);

            // on
            jsonwriter_object_strn(jsw, "on", first_paren + 1, last_paren - first_paren - 1);

            // unique
            sqlite3_bind_text(unique_stmt, 1, tname, (int)strlen(tname), SQLITE_STATIC);
            sqlite3_bind_text(unique_stmt, 2, (const char *)text, len, SQLITE_STATIC);
            if (sqlite3_step(unique_stmt) == SQLITE_ROW)
              jsonwriter_object_bool(jsw, "unique", 1);
            sqlite3_reset(unique_stmt);

            // end ix obj
            jsonwriter_end_object(jsw);
          }
        }
      }
      jsonwriter_end_object(jsw); // end indexes

      // columns
      jsonwriter_object_array(jsw, "columns");
      for (int i = 0; i < colcount; i++) {
        const char *colname = sqlite3_column_name(data_stmt, i);
        jsonwriter_start_object(jsw);
        jsonwriter_object_cstr(jsw, "name", colname);
        const char *dtype = sqlite3_column_decltype(data_stmt, i);
        if (dtype)
          jsonwriter_object_cstr(jsw, "datatype", dtype);

        // TO DO: collate nocase etc
        jsonwriter_end_object(jsw);
      }
      jsonwriter_end_array(jsw); // end columns

      jsonwriter_end_object(jsw); // end meta obj

      // ------ data: array of rows
      jsonwriter_start_array(jsw);
      // for each row
      size_t count = 0;
      while (sqlite3_step(data_stmt) == SQLITE_ROW) {
        jsonwriter_start_array(jsw); // start row
        for (int i = 0; i < colcount; i++) {
          const unsigned char *text = sqlite3_column_text(data_stmt, i);
          if (text) {
            int len = sqlite3_column_bytes(data_stmt, i);
            jsonwriter_strn(jsw, text, len);
          } else
            jsonwriter_null(jsw);
        }
        jsonwriter_end_array(jsw); // end row
        if (limit && ++count >= limit)
          break;
      }
      jsonwriter_end_array(jsw);

      jsonwriter_end_array(jsw); // end of output
    }
    if (data_stmt)
      sqlite3_finalize(data_stmt);
    if (index_stmt)
      sqlite3_finalize(index_stmt);
    if (unique_stmt)
      sqlite3_finalize(unique_stmt);

    sqlite3_free(sqlite3_str_finish(data_sql));
  } // if data_sql
  return err;
}