sqlite_btreeinfo

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

sqlite_btreeinfo

wmertens
I can't figure out how to get access to the sqlite_btreeinfo vtable that
was added in 3.22 :-( The only documentation is the C file and there
doesn't seem to be a compile flag for it.

I went and downloaded the file from
https://sqlite.org/src/artifact/4f0ebf278f46e68e, then compiled it on on
macOS with

$ gcc -g -fPIC -dynamiclib btreeinfo.c -o btreeinfo.dylib
btreeinfo.c:414:5: warning: excess elements in struct initializer
    0                            /* xShadowName */
    ^
1 warning generated.

and tried loading it but:

sqlite> .load btreeinfo.dylib
sqlite> select * from sqlite_btreeinfo;
Error: no such table: sqlite_btreeinfo

:-(

Wout.
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: sqlite_btreeinfo

Keith Medcalf

I have the extension compiled in.  When a database does not exist I get:

SQLite version 3.27.0 2018-12-10 01:48:29
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> select * from sqlite_btreeinfo;
Error: not an error


However, when a database is loaded is works fine:

sqlite> .open tz.db
sqlite> select * from sqlite_btreeinfo;
table|sqlite_master|sqlite_master|1||1|23|1|1|
table|sqlite_stat1|sqlite_stat1|2|CREATE TABLE sqlite_stat1(tbl,idx,stat)|1|16|1|1|
table|sqlite_stat4|sqlite_stat4|3|CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample)|1|1|1|1|
table|tz_geopoly_rowid|tz_geopoly_rowid|4|CREATE TABLE "tz_geopoly_rowid"(rowid INTEGER PRIMARY KEY,nodeno,a0,a1)|1|2028|507|2|
table|tz_geopoly_node|tz_geopoly_node|5|CREATE TABLE "tz_geopoly_node"(nodeno INTEGER PRIMARY KEY,data)|1|44|11|2|
table|tz_geopoly_parent|tz_geopoly_parent|6|CREATE TABLE "tz_geopoly_parent"(nodeno INTEGER PRIMARY KEY,parentnode)|1|33|1|1|
table|TZ_Version|TZ_Version|7|CREATE TABLE TZ_Version
(
    Version     text collate nocase,
    PIPVersion  text collate nocase
)|1|2|1|1|
table|TZ_Countries|TZ_Countries|8|CREATE TABLE TZ_Countries
(
    Code            text not null collate nocase primary key,
    Country         text not null collate nocase unique
) WITHOUT ROWID|0|34|2|2|
index|sqlite_autoindex_TZ_Countries_2|TZ_Countries|9||0|32|2|2|
table|TZ_Zones|TZ_Zones|10|CREATE TABLE TZ_Zones
(
    ID              INTEGER PRIMARY KEY,
    Country_Code    text collate nocase references TZ_Countries(Code),
    Area            text collate nocase,
    Location        text collate nocase,
    City            text collate nocase,
    Zone            text not null collate nocase unique
)|1|658|7|2|
index|sqlite_autoindex_TZ_Zones_1|TZ_Zones|11||0|720|4|2|
table|TZ_ZoneData|TZ_ZoneData|12|CREATE TABLE TZ_ZoneData
(
    Zone_ID         integer not null references TZ_Zones(ID),
    Abbreviation    text collate nocase not null,
    StartTime       integer not null,
    Offset          integer not null,
    isDST           integer not null
)|1|35088|204|2|
index|TZ_Country|TZ_Countries|13|CREATE INDEX TZ_Country on TZ_Countries (Country, Code)|0|32|2|2|
index|TZ_ZonesCountry|TZ_Zones|14|CREATE INDEX TZ_ZonesCountry  on TZ_Zones (Country_Code)|0|210|2|2|
index|TZ_ZonesArea|TZ_Zones|15|CREATE INDEX TZ_ZonesArea     on TZ_Zones (Area)|0|508|2|2|
index|TZ_ZonesLocation|TZ_Zones|16|CREATE INDEX TZ_ZonesLocation on TZ_Zones (Location)|0|774|3|2|
index|TZ_ZonesCity|TZ_Zones|17|CREATE INDEX TZ_ZonesCity     on TZ_Zones (City)|0|438|2|2|
index|TZ_ZoneDataID|TZ_ZoneData|18|CREATE INDEX TZ_ZoneDataID on TZ_ZoneData (Zone_ID)|0|36401|89|2|
index|TZ_ZoneStart|TZ_ZoneData|19|CREATE INDEX TZ_ZoneStart  on TZ_ZoneData (Zone_ID, StartTime, Offset, Abbreviation)|0|14616|84|3|
index|TZ_ZoneOffset|TZ_ZoneData|20|CREATE INDEX TZ_ZoneOffset on TZ_ZoneData (Zone_ID, StartTime + Offset, Offset)|0|35903|161|2|
sqlite>


---
The fact that there's a Highway to Hell but only a Stairway to Heaven says a lot about anticipated traffic volume.

>-----Original Message-----
>From: sqlite-users [mailto:sqlite-users-
>[hidden email]] On Behalf Of Wout Mertens
>Sent: Wednesday, 12 December, 2018 13:41
>To: SQLite mailing list
>Subject: [sqlite] sqlite_btreeinfo
>
>I can't figure out how to get access to the sqlite_btreeinfo vtable
>that
>was added in 3.22 :-( The only documentation is the C file and there
>doesn't seem to be a compile flag for it.
>
>I went and downloaded the file from
>https://sqlite.org/src/artifact/4f0ebf278f46e68e, then compiled it on
>on
>macOS with
>
>$ gcc -g -fPIC -dynamiclib btreeinfo.c -o btreeinfo.dylib
>btreeinfo.c:414:5: warning: excess elements in struct initializer
>    0                            /* xShadowName */
>    ^
>1 warning generated.
>
>and tried loading it but:
>
>sqlite> .load btreeinfo.dylib
>sqlite> select * from sqlite_btreeinfo;
>Error: no such table: sqlite_btreeinfo
>
>:-(
>
>Wout.
>_______________________________________________
>sqlite-users mailing list
>[hidden email]
>http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users



_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: sqlite_btreeinfo

wmertens
And how do you compile sqlite so it has the extension? The error I posted
was actually with a loaded db, so that's not it.

I must be compiling or loading the extension wrong somehow. Also, I saw
that it requires sqlite_db[vtab? not sure] and that table is also missing

Wout.


On Thu, Dec 13, 2018 at 12:43 AM Keith Medcalf <[hidden email]> wrote:

>
> I have the extension compiled in.  When a database does not exist I get:
>
> SQLite version 3.27.0 2018-12-10 01:48:29
> Enter ".help" for usage hints.
> Connected to a transient in-memory database.
> Use ".open FILENAME" to reopen on a persistent database.
> sqlite> select * from sqlite_btreeinfo;
> Error: not an error
>
>
> However, when a database is loaded is works fine:
>
> sqlite> .open tz.db
> sqlite> select * from sqlite_btreeinfo;
> table|sqlite_master|sqlite_master|1||1|23|1|1|
> table|sqlite_stat1|sqlite_stat1|2|CREATE TABLE
> sqlite_stat1(tbl,idx,stat)|1|16|1|1|
> table|sqlite_stat4|sqlite_stat4|3|CREATE TABLE
> sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample)|1|1|1|1|
> table|tz_geopoly_rowid|tz_geopoly_rowid|4|CREATE TABLE
> "tz_geopoly_rowid"(rowid INTEGER PRIMARY KEY,nodeno,a0,a1)|1|2028|507|2|
> table|tz_geopoly_node|tz_geopoly_node|5|CREATE TABLE
> "tz_geopoly_node"(nodeno INTEGER PRIMARY KEY,data)|1|44|11|2|
> table|tz_geopoly_parent|tz_geopoly_parent|6|CREATE TABLE
> "tz_geopoly_parent"(nodeno INTEGER PRIMARY KEY,parentnode)|1|33|1|1|
> table|TZ_Version|TZ_Version|7|CREATE TABLE TZ_Version
> (
>     Version     text collate nocase,
>     PIPVersion  text collate nocase
> )|1|2|1|1|
> table|TZ_Countries|TZ_Countries|8|CREATE TABLE TZ_Countries
> (
>     Code            text not null collate nocase primary key,
>     Country         text not null collate nocase unique
> ) WITHOUT ROWID|0|34|2|2|
> index|sqlite_autoindex_TZ_Countries_2|TZ_Countries|9||0|32|2|2|
> table|TZ_Zones|TZ_Zones|10|CREATE TABLE TZ_Zones
> (
>     ID              INTEGER PRIMARY KEY,
>     Country_Code    text collate nocase references TZ_Countries(Code),
>     Area            text collate nocase,
>     Location        text collate nocase,
>     City            text collate nocase,
>     Zone            text not null collate nocase unique
> )|1|658|7|2|
> index|sqlite_autoindex_TZ_Zones_1|TZ_Zones|11||0|720|4|2|
> table|TZ_ZoneData|TZ_ZoneData|12|CREATE TABLE TZ_ZoneData
> (
>     Zone_ID         integer not null references TZ_Zones(ID),
>     Abbreviation    text collate nocase not null,
>     StartTime       integer not null,
>     Offset          integer not null,
>     isDST           integer not null
> )|1|35088|204|2|
> index|TZ_Country|TZ_Countries|13|CREATE INDEX TZ_Country on TZ_Countries
> (Country, Code)|0|32|2|2|
> index|TZ_ZonesCountry|TZ_Zones|14|CREATE INDEX TZ_ZonesCountry  on
> TZ_Zones (Country_Code)|0|210|2|2|
> index|TZ_ZonesArea|TZ_Zones|15|CREATE INDEX TZ_ZonesArea     on TZ_Zones
> (Area)|0|508|2|2|
> index|TZ_ZonesLocation|TZ_Zones|16|CREATE INDEX TZ_ZonesLocation on
> TZ_Zones (Location)|0|774|3|2|
> index|TZ_ZonesCity|TZ_Zones|17|CREATE INDEX TZ_ZonesCity     on TZ_Zones
> (City)|0|438|2|2|
> index|TZ_ZoneDataID|TZ_ZoneData|18|CREATE INDEX TZ_ZoneDataID on
> TZ_ZoneData (Zone_ID)|0|36401|89|2|
> index|TZ_ZoneStart|TZ_ZoneData|19|CREATE INDEX TZ_ZoneStart  on
> TZ_ZoneData (Zone_ID, StartTime, Offset, Abbreviation)|0|14616|84|3|
> index|TZ_ZoneOffset|TZ_ZoneData|20|CREATE INDEX TZ_ZoneOffset on
> TZ_ZoneData (Zone_ID, StartTime + Offset, Offset)|0|35903|161|2|
> sqlite>
>
>
> ---
> The fact that there's a Highway to Hell but only a Stairway to Heaven says
> a lot about anticipated traffic volume.
>
> >-----Original Message-----
> >From: sqlite-users [mailto:sqlite-users-
> >[hidden email]] On Behalf Of Wout Mertens
> >Sent: Wednesday, 12 December, 2018 13:41
> >To: SQLite mailing list
> >Subject: [sqlite] sqlite_btreeinfo
> >
> >I can't figure out how to get access to the sqlite_btreeinfo vtable
> >that
> >was added in 3.22 :-( The only documentation is the C file and there
> >doesn't seem to be a compile flag for it.
> >
> >I went and downloaded the file from
> >https://sqlite.org/src/artifact/4f0ebf278f46e68e, then compiled it on
> >on
> >macOS with
> >
> >$ gcc -g -fPIC -dynamiclib btreeinfo.c -o btreeinfo.dylib
> >btreeinfo.c:414:5: warning: excess elements in struct initializer
> >    0                            /* xShadowName */
> >    ^
> >1 warning generated.
> >
> >and tried loading it but:
> >
> >sqlite> .load btreeinfo.dylib
> >sqlite> select * from sqlite_btreeinfo;
> >Error: no such table: sqlite_btreeinfo
> >
> >:-(
> >
> >Wout.
> >_______________________________________________
> >sqlite-users mailing list
> >[hidden email]
> >http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
>
>
>
> _______________________________________________
> sqlite-users mailing list
> [hidden email]
> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
>
_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
Reply | Threaded
Open this post in threaded view
|

Re: sqlite_btreeinfo

Keith Medcalf

You need to have SQLITE_ENABLE_DBPAGE_VTAB defined when compiling sqlite3.c ... otherwise the btreeinfo extension will not work.

I append btreeinfo.c (and a whole pile of other .c files) to the end of sqlite3.c and also append the following at the very end:


#ifdef USE_NUNICODE
extern void* sqlite3_nunicode_init(void*);
#endif

int core_init(const char* dummy)
{
    int nErr = 0;

    nErr += sqlite3_auto_extension((void*)sqlite3_autobusy_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_ipaddress_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_sqlfcmp_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_sqlfunc_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_sqlfwin_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_sqlhash_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_sqlitemprint_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_sqlmath_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_sqltime_init);

#ifdef USE_NUNICODE
    nErr += sqlite3_auto_extension((void*)sqlite3_nunicode_init);
#else
    nErr += sqlite3_auto_extension((void*)sqlite3_unifuzz_init);
#endif

    nErr += sqlite3_auto_extension((void*)sqlite3_eval_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_fileio_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_ieee_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_nextchar_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_percentile_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_regexp_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_rot_init);
//  nErr += sqlite3_auto_extension((void*)sqlite3_sha_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_totype_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_zorder_init);

#ifndef SQLITE_OMIT_VIRTUALTABLE
    nErr += sqlite3_auto_extension((void*)sqlite3_amatch_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_btreeinfo_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_carray_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_closure_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_csv_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_fuzzer_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_memstat_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_series_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_spellfix_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_unionvtab_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_vtshim_init);
    nErr += sqlite3_auto_extension((void*)sqlite3_wholenumber_init);
#ifdef SQLITE_ENABLE_VFSSTAT
    nErr += sqlite3_vfsstat_init((void*)dummy, (void*)0, (void*)0);
#endif
#ifndef _MSC_VER
    nErr += sqlite3_auto_extension((void*)sqlite3_interpolate_init);
#endif
#endif

#ifndef _MSC_VER
    nErr += sqlite3_auto_extension((void*)sqlite3_compress_init);
#endif

    return nErr ? SQLITE_ERROR : SQLITE_OK;
}

then compile sqlite3.c with -DSQLITE_ENABLE_DBPAGE_VTAB (and a crapload of other defines, which I put in config.h and get them read by defining -D_HAVE_SQLITE_CONFIG_H) -DSQLITE_EXTRA_INIT=core_init  The latter one (EXTRA_INIT) gives the name of a function that is called after the normal initialization is complete.  It adds a bunch of auto_extensions that get added to every connection.

---
The fact that there's a Highway to Hell but only a Stairway to Heaven says a lot about anticipated traffic volume.


>-----Original Message-----
>From: sqlite-users [mailto:sqlite-users-
>[hidden email]] On Behalf Of Wout Mertens
>Sent: Thursday, 13 December, 2018 04:04
>To: SQLite mailing list
>Subject: Re: [sqlite] sqlite_btreeinfo
>
>And how do you compile sqlite so it has the extension? The error I
>posted
>was actually with a loaded db, so that's not it.
>
>I must be compiling or loading the extension wrong somehow. Also, I
>saw
>that it requires sqlite_db[vtab? not sure] and that table is also
>missing
>
>Wout.
>
>
>On Thu, Dec 13, 2018 at 12:43 AM Keith Medcalf <[hidden email]>
>wrote:
>
>>
>> I have the extension compiled in.  When a database does not exist I
>get:
>>
>> SQLite version 3.27.0 2018-12-10 01:48:29
>> Enter ".help" for usage hints.
>> Connected to a transient in-memory database.
>> Use ".open FILENAME" to reopen on a persistent database.
>> sqlite> select * from sqlite_btreeinfo;
>> Error: not an error
>>
>>
>> However, when a database is loaded is works fine:
>>
>> sqlite> .open tz.db
>> sqlite> select * from sqlite_btreeinfo;
>> table|sqlite_master|sqlite_master|1||1|23|1|1|
>> table|sqlite_stat1|sqlite_stat1|2|CREATE TABLE
>> sqlite_stat1(tbl,idx,stat)|1|16|1|1|
>> table|sqlite_stat4|sqlite_stat4|3|CREATE TABLE
>> sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample)|1|1|1|1|
>> table|tz_geopoly_rowid|tz_geopoly_rowid|4|CREATE TABLE
>> "tz_geopoly_rowid"(rowid INTEGER PRIMARY
>KEY,nodeno,a0,a1)|1|2028|507|2|
>> table|tz_geopoly_node|tz_geopoly_node|5|CREATE TABLE
>> "tz_geopoly_node"(nodeno INTEGER PRIMARY KEY,data)|1|44|11|2|
>> table|tz_geopoly_parent|tz_geopoly_parent|6|CREATE TABLE
>> "tz_geopoly_parent"(nodeno INTEGER PRIMARY
>KEY,parentnode)|1|33|1|1|
>> table|TZ_Version|TZ_Version|7|CREATE TABLE TZ_Version
>> (
>>     Version     text collate nocase,
>>     PIPVersion  text collate nocase
>> )|1|2|1|1|
>> table|TZ_Countries|TZ_Countries|8|CREATE TABLE TZ_Countries
>> (
>>     Code            text not null collate nocase primary key,
>>     Country         text not null collate nocase unique
>> ) WITHOUT ROWID|0|34|2|2|
>> index|sqlite_autoindex_TZ_Countries_2|TZ_Countries|9||0|32|2|2|
>> table|TZ_Zones|TZ_Zones|10|CREATE TABLE TZ_Zones
>> (
>>     ID              INTEGER PRIMARY KEY,
>>     Country_Code    text collate nocase references
>TZ_Countries(Code),
>>     Area            text collate nocase,
>>     Location        text collate nocase,
>>     City            text collate nocase,
>>     Zone            text not null collate nocase unique
>> )|1|658|7|2|
>> index|sqlite_autoindex_TZ_Zones_1|TZ_Zones|11||0|720|4|2|
>> table|TZ_ZoneData|TZ_ZoneData|12|CREATE TABLE TZ_ZoneData
>> (
>>     Zone_ID         integer not null references TZ_Zones(ID),
>>     Abbreviation    text collate nocase not null,
>>     StartTime       integer not null,
>>     Offset          integer not null,
>>     isDST           integer not null
>> )|1|35088|204|2|
>> index|TZ_Country|TZ_Countries|13|CREATE INDEX TZ_Country on
>TZ_Countries
>> (Country, Code)|0|32|2|2|
>> index|TZ_ZonesCountry|TZ_Zones|14|CREATE INDEX TZ_ZonesCountry  on
>> TZ_Zones (Country_Code)|0|210|2|2|
>> index|TZ_ZonesArea|TZ_Zones|15|CREATE INDEX TZ_ZonesArea     on
>TZ_Zones
>> (Area)|0|508|2|2|
>> index|TZ_ZonesLocation|TZ_Zones|16|CREATE INDEX TZ_ZonesLocation on
>> TZ_Zones (Location)|0|774|3|2|
>> index|TZ_ZonesCity|TZ_Zones|17|CREATE INDEX TZ_ZonesCity     on
>TZ_Zones
>> (City)|0|438|2|2|
>> index|TZ_ZoneDataID|TZ_ZoneData|18|CREATE INDEX TZ_ZoneDataID on
>> TZ_ZoneData (Zone_ID)|0|36401|89|2|
>> index|TZ_ZoneStart|TZ_ZoneData|19|CREATE INDEX TZ_ZoneStart  on
>> TZ_ZoneData (Zone_ID, StartTime, Offset,
>Abbreviation)|0|14616|84|3|
>> index|TZ_ZoneOffset|TZ_ZoneData|20|CREATE INDEX TZ_ZoneOffset on
>> TZ_ZoneData (Zone_ID, StartTime + Offset, Offset)|0|35903|161|2|
>> sqlite>
>>
>>
>> ---
>> The fact that there's a Highway to Hell but only a Stairway to
>Heaven says
>> a lot about anticipated traffic volume.
>>
>> >-----Original Message-----
>> >From: sqlite-users [mailto:sqlite-users-
>> >[hidden email]] On Behalf Of Wout Mertens
>> >Sent: Wednesday, 12 December, 2018 13:41
>> >To: SQLite mailing list
>> >Subject: [sqlite] sqlite_btreeinfo
>> >
>> >I can't figure out how to get access to the sqlite_btreeinfo
>vtable
>> >that
>> >was added in 3.22 :-( The only documentation is the C file and
>there
>> >doesn't seem to be a compile flag for it.
>> >
>> >I went and downloaded the file from
>> >https://sqlite.org/src/artifact/4f0ebf278f46e68e, then compiled it
>on
>> >on
>> >macOS with
>> >
>> >$ gcc -g -fPIC -dynamiclib btreeinfo.c -o btreeinfo.dylib
>> >btreeinfo.c:414:5: warning: excess elements in struct initializer
>> >    0                            /* xShadowName */
>> >    ^
>> >1 warning generated.
>> >
>> >and tried loading it but:
>> >
>> >sqlite> .load btreeinfo.dylib
>> >sqlite> select * from sqlite_btreeinfo;
>> >Error: no such table: sqlite_btreeinfo
>> >
>> >:-(
>> >
>> >Wout.
>> >_______________________________________________
>> >sqlite-users mailing list
>> >[hidden email]
>> >http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-
>users
>>
>>
>>
>> _______________________________________________
>> sqlite-users mailing list
>> [hidden email]
>> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-
>users
>>
>_______________________________________________
>sqlite-users mailing list
>[hidden email]
>http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users



_______________________________________________
sqlite-users mailing list
[hidden email]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users